使用 Unity 5 UI 进行捏合缩放
Posted
技术标签:
【中文标题】使用 Unity 5 UI 进行捏合缩放【英文标题】:Pinch-To-Zoom with Unity 5 UI 【发布时间】:2017-01-27 19:05:56 【问题描述】:我正在尝试在基于 Unity UI 的应用中重新实现捏拉缩放系统。大约六个月前,我能够通过将 UI 画布作为常规 GameObject 的子对象并操纵该对象的变换来破解一个,但自从更新到 Unity 5.5+ 后,我发现这不起作用。我能得到的最接近的允许捏合手势更改画布的 scaleFactor,这 a) 可以使图像、面板等根据它们的对齐方式不正确地调整大小,并且 b) 缩放后将不允许我平移。
到目前为止,我所拥有的是:
public class PinchToZoomScaler : MonoBehaviour
public Canvas canvas; // The canvas
public float zoomSpeed = 0.5f; // The rate of change of the canvas scale factor
public float _resetDuration = 3.0f;
float _durationTimer = 0.0f;
float _startScale = 0.0f;
void Start()
_startScale = canvas.scaleFactor;
void Update()
// If there are two touches on the device...
if (Input.touchCount == 2)
// Store both touches.
Touch touchZero = Input.GetTouch (0);
Touch touchOne = Input.GetTouch (1);
// Find the position in the previous frame of each touch.
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
// Find the magnitude of the vector (the distance) between the touches in each frame.
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
// Find the difference in the distances between each frame.
float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
// ... change the canvas size based on the change in distance between the touches.
canvas.scaleFactor -= deltaMagnitudeDiff * zoomSpeed;
// Make sure the canvas size never drops below 0.1
canvas.scaleFactor = Mathf.Max (canvas.scaleFactor, _startScale);
canvas.scaleFactor = Mathf.Min (canvas.scaleFactor, _startScale * 3.0f);
_durationTimer = 0.0f;
else
_durationTimer += Time.deltaTime;
if (_durationTimer >= _resetDuration)
canvas.scaleFactor = _startScale;
正如我所说,这在一定程度上有效,但不能很好地统一缩放,也不能让我平移画布。提前感谢您的帮助。
【问题讨论】:
【参考方案1】:将此脚本附加到要放大和缩小的画布对象中
using UnityEngine;
using UnityEngine.EventSystems;
public class ObjectScalling : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
private bool _isDragging;
private float _currentScale;
public float minScale, maxScale;
private float _temp = 0;
private float _scalingRate = 2;
private void Start()
_currentScale = transform.localScale.x;
public void OnPointerDown(PointerEventData eventData)
if (Input.touchCount == 1)
_isDragging = true;
public void OnPointerUp(PointerEventData eventData)
_isDragging = false;
private void Update()
if (_isDragging)
if (Input.touchCount == 2)
transform.localScale = new Vector2(_currentScale, _currentScale);
float distance = Vector3.Distance(Input.GetTouch(0).position, Input.GetTouch(1).position);
if (_temp > distance)
if (_currentScale < minScale)
return;
_currentScale -= (Time.deltaTime) * _scalingRate;
else if (_temp < distance)
if (_currentScale > maxScale)
return;
_currentScale += (Time.deltaTime) * _scalingRate;
_temp = distance;
提醒:此脚本仅适用于画布对象
【讨论】:
【参考方案2】:您可以使用此函数(只需将负 deltaMagnitudeDiff 传递给它) 此外,以 ( 0.05 ) 之类的比率进行多重播放 deltaMagnitudeDiff 也很好
float currentScale = 1f;
void Zoom (float increment)
currentScale += increment;
if (currentScale >= maxScale)
currentScale = maxScale;
else if (currentScale <= minScale)
currentScale = minScale;
rectTransform.localScale = new Vector3 (currentScale, currentScale, 1);
pan.ValidatePosition ();
对于平移, 你可以使用这样的东西:
public class Pan : MonoBehaviour
public float Speed;
Vector3 startDragPosition;
public void BeginDrag ()
startDragPosition = Input.mousePosition;
public void Drag ()
transform.localPosition += (Input.mousePosition - startDragPosition) * Speed;
startDragPosition = Input.mousePosition;
ValidatePosition ();
public void ValidatePosition ()
var temp = transform.localPosition;
var width = ((RectTransform)transform).sizeDelta.x;
var height = ((RectTransform)transform).sizeDelta.y;
var MaxX = 0.5f * width * Mathf.Max (0, transform.localScale.x - 1);
var MaxY = 0.5f * height * Mathf.Max (0, transform.localScale.y - 1);
var offsetX = transform.localScale.x * width * (((RectTransform)transform).pivot.x - 0.5f);
var offsetY = transform.localScale.y * width * (((RectTransform)transform).pivot.y - 0.5f);
if (temp.x < -MaxX + offsetX)
temp.x = -MaxX + offsetX;
else if (temp.x > MaxX + offsetX)
temp.x = MaxX + offsetX;
if (temp.y < -MaxY + offsetY)
temp.y = -MaxY + offsetY;
else if (temp.y > MaxY + offsetY)
temp.y = MaxY + offsetY;
transform.localPosition = temp;
只需从事件触发器组件中调用函数 ( BeginDrag & Drag )。
【讨论】:
【参考方案3】:我使用捏合缩放对象是这样的,当对象位于屏幕中间时,它可以在任何触摸屏上工作:
if (Input.touchCount == 2)
//The distance between the 2 touches is checked and subsequently used to scale the
//object by moving the 2 fingers further, or closer form eachother.
Touch touch0 = Input.GetTouch(0);
Touch touch1 = Input.GetTouch(1);
if (isScaling)//this will only be done if scaling is true
float currentTouchDistance = getTouchDistance();
float deltaTouchDistance = currentTouchDistance - touchDistanceOrigin;
float scalePercentage = (deltaTouchDistance / 1200f) + 1f;
Vector3 scaleTemp = transform.localScale;
scaleTemp.x = scalePercentage * originalScale.x;
scaleTemp.y = scalePercentage * originalScale.y;
scaleTemp.z = scalePercentage * originalScale.z;
//to make the object snap to 100% a check is being done to see if the object scale is close to 100%,
//if it is the scale will be put back to 100% so it snaps to the normal scale.
//this is a quality of life feature, so its easy to get the original size of the object.
if (scaleTemp.x * 100 < 102 && scaleTemp.x * 100 > 98)
scaleTemp.x = 1;
scaleTemp.y = 1;
scaleTemp.z = 1;
//here we apply the calculation done above to actually make the object bigger/smaller.
transform.localScale = scaleTemp;
else
//if 2 fingers are touching the screen but isScaling is not true we are going to see if
//the middle of the screen is looking at the object and if it is set isScalinf to true;
Ray ray;
RaycastHit hitTouch;
ray = cam.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
if (Physics.Raycast(ray, out hitTouch, 100f))
if (hitTouch.transform == transform)
isScaling = true;
//make sure that the distance between the fingers on initial contact is used as the original distance
touchDistanceOrigin = getTouchDistance();
originalScale = transform.localScale;
【讨论】:
以上是关于使用 Unity 5 UI 进行捏合缩放的主要内容,如果未能解决你的问题,请参考以下文章