首页 > 解决方案 > 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);
            
        }
    }
}

标签: c#unity3d

解决方案


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


推荐阅读