c# - Change location of pivot based on Input.GetTouch.position to zoom in
问题描述
So i am trying so to make a script for zooming in and out on a ui element(image), and so far i am doing it by scaling the image based on differences in magnitudes between touches from frame to frame, the only problem is that is zooms from where the pivot is.
The solution would be to move the pivot to the point the is the middle of the line that connects the touches. I tried but it puts my pivot way out of the screen due to the fact that i get values greater than 1 from the formula i used.
Basically, i don't know how to move the pivot so it matches the point the would be the middle of the line that connects the touches.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Zoom : MonoBehaviour
{
public GameObject image1, image2;
public RectTransform tran1, tran2;
public float zoomSpeed = 0.0090f;
public bool startZooming = false;
void Start()
{
tran1 = image1.GetComponent<RectTransform>();
tran2 = image2.GetComponent<RectTransform>();
}
void Update()
{
if(Input.touchCount == 0)
{
//Remember if the player is zooming to be able to change pivot point
startZooming = false;
}
// 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;
//Find pivot point, the middle of the line that connects the touch points
if (deltaMagnitudeDiff < 0 && startZooming == false)
{
float xpivot, ypivot;
xpivot = (touchZero.position.x + touchOne.position.x) / 2;
ypivot = (touchOne.position.y + touchZero.position.y) / 2;
tran1.pivot = new Vector2(xpivot, ypivot);
tran2.pivot = new Vector2(xpivot, ypivot);
startZooming = true; // player is currently zooming, don't change the pivot point
}
float x, y;
x = tran1.localScale.x - deltaMagnitudeDiff * zoomSpeed;
y = tran1.localScale.y - deltaMagnitudeDiff * zoomSpeed;
// Make sure the localScale size never goes below 1 or above 5
x = Mathf.Clamp(x, 1.0f, 5.0f);
y = Mathf.Clamp(y, 1.0f, 5.0f);
// ... change the localScale size based on the change in distance between the touches.
tran1.localScale = new Vector3(x, y, tran1.localScale.z);
tran2.localScale = new Vector3(x, y, tran2.localScale.z);
}
}
}
解决方案
The RectTrasnform.pivot
you are trying to set is
The normalized position in this RectTransform that it rotates around.
this would be a vector between 0,0
and 1,1,
!
You can use ScaleAroundRelative
from this post
public static void ScaleAround(Transform target, Vector3 pivotInWorldSpace, Vector3 newScale)
{
// pivot
var localPivot = target.InverseTransformPoint(pivotInWorldSpace);
// diff from object pivot to desired pivot/origin
Vector3 pivotDelta = target.transform.localPosition - pivot;
Vector3 scaleFactor = new Vector3(
newScale.x / target.transform.localScale.x,
newScale.y / target.transform.localScale.y,
newScale.z / target.transform.localScale.z );
pivotDelta.Scale(scaleFactor);
target.transform.localPosition = pivot + pivotDelta;
//scale
target.transform.localScale = newScale;
}
then I would implement it using something like
private float initialTouchDistance;
private Vector2 pivot1;
private Vector2 pivot2;
private Vector2 initialScale1;
private Vector2 initialScale2;
void Update()
{
// If there are two touches on the device...
if (Input.touchCount == 2)
{
// Store both touches.
var touchZero = Input.GetTouch(0);
var touchOne = Input.GetTouch(1);
var touchZeroPosition = touchZero.position;
var touchOnePosition = touchOne.position;
var currentDistance = Vector2.Distance(touchZeroPosition, touchOnePosition);
// Is this a new zoom process? => One of the two touches was added this frame
if(touchZero.phase == TouchPhase.Began || touchOne.phase == TouchPhase.Began)
{
// initialize values
initialTouchDistance = currentDistance;
initialScale1 = tran1.localScale;
initialScale2 = tran2.localScale;
// get center between the touches
// THIS IS STILL IN PIXEL SPACE
var zoomPivot = (touchZeroPosition + touchOnePosition) / 2f;
// Get the position on the RectTransforms planes in WORLD SPACE
RectTransformUtility.ScreenPointToWorldPointInRectangle(tran1, zoomPivot, Camera.main, out pivot1));
RectTransformUtility.ScreenPointToWorldPointInRectangle(tran2, zoomPivot, Camera.main, out pivot2));
}
// Is this an already running zoom process and at least one of the touches was moved?
else if(touchZero.phase == TouchPhase.Moved || touchOne.phase == TouchPhase.Moved)
{
// Scale factor
var factor = (currentDistance / initialTouchDistance) * zoomSpeed;
factor = Mathf.Clamp(factor, 1, 5);
var scaleVector1 = Vector3.Scale(new Vector3(factor, factor, 1), initialScale1);
var scaleVector2 = Vector3.Scale(new Vector3(factor, factor, 1), initialScale2);
// apply scaled around pivot
ScaleAround(tran1, pivot1, scaleVector1);
ScaleAround(tran2, pivot2, scaleVector2);
}
}
}
Note: Typed on smartphone but I hope the idea gets clear
推荐阅读
- android - 为什么我的 android 应用程序在使用后退按钮导航并从 Internet 加载数据时会跳帧?(导航组件)
- python-3.x - 决策树回归器 Python
- angular - 即使按钮在表单内,表单提交也无法处理角度
- huawei-mobile-services - 如何获取设备支持的 DRM 安全级别?
- c++ - 当包含头文件的文件不在根项目文件夹中时,如何包含头文件?
- javascript - 从nodeJs服务器获得响应后如何调用函数(使用javascript)?
- python - AttributeError:“NoneType”对象没有名为“find_all”的属性
- r - 根据 R 中的某些条件将缺失的日期添加到数据表中
- python - Python:动态分箱熊猫中的列
- java - 如何在启动 android 应用程序时启动特定片段