gpt4 book ai didi

c# - 在 Unity 中使用捏合手势缩放 ScrollView 内容的正确方法是什么?

转载 作者:行者123 更新时间:2023-12-05 05:07:41 37 4
gpt4 key购买 nike

目标:制作一个支持在设备上捏合缩放和在编辑器中滚动鼠标缩放的通用组件。捏合时,将枢轴设置为两根手指的中点,因此它会围绕您捏合的位置缩放。

我在下面提供了我的脚本。然而,当它“跳跃”或抖动很多时,它在两根手指捏合的设备上的 ScrollView 中效果不佳。不过,它可以在编辑器中使用鼠标滚动。我认为原因可能与 ScrollView 如何在内部更新布局有关。在单个框架中,我的脚本更改了内容的轴心和位置,但是 ScrollView 无法正确处理这些更改。这就是为什么我还使用 1 LateUpdate、2 yield WaitForEndOfFrame、3 在 ScrollView 的 OnValueChanged 事件中注册回调来测试我的脚本,但都失败了。

有谁知道如何解决我的脚本中的问题或任何其他新解决方案如何使 ScrollView 支持双指缩放?谢谢!

我的代码是这样的:

using System.Collections; 
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class PinchZoom : MonoBehaviour {
public float zoomSpeedPinch = 0.001f;
public float zoomSpeedMouseScrollWheel = 0.05f;
public float zoomMin = 0.1f;
public float zoomMax = 1f;
RectTransform rectTransform;
public int type = 1; // for device testing type 1 use LateUpdate; type 2 use Update

private void Awake()
{
rectTransform = GetComponent<RectTransform>();
}

//public void OnValueChanged(Vector2 v) // test failed: called by scroll view event
//{
// //Zoom();
//}

void Update()
{
//Zoom();
if (type == 2)
{
if (Input.touchCount == 2)
StartCoroutine(ZoomInTheEndOfFrame(Input.mouseScrollDelta.y, Input.touchCount, Input.GetTouch(0), Input.GetTouch(1), Input.mousePosition));
else
StartCoroutine(ZoomInTheEndOfFrame(Input.mouseScrollDelta.y, Input.touchCount, default(Touch), default(Touch), Input.mousePosition));
}
}

private void LateUpdate()
{
if (type == 1) Zoom();
}

void Zoom()
{
var mouseScrollWheel = Input.mouseScrollDelta.y;
float scaleChange = 0f;
Vector2 midPoint = Vector2.zero;
if (Input.touchCount == 2)
{
Touch touchZero = Input.GetTouch(0);
Touch touchOne = Input.GetTouch(1);

Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;

float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;

float deltaMagnitudeDiff = touchDeltaMag - prevTouchDeltaMag;

scaleChange = deltaMagnitudeDiff * zoomSpeedPinch;

midPoint = (touchOne.position + touchZero.position) / 2;
}

if (mouseScrollWheel != 0)
{
scaleChange = mouseScrollWheel * zoomSpeedMouseScrollWheel;
midPoint = Input.mousePosition;
}

if (scaleChange != 0)
{
var scaleX = transform.localScale.x;
scaleX += scaleChange;
scaleX = Mathf.Clamp(scaleX, zoomMin, zoomMax);
var size = rectTransform.rect.size;
size.Scale(rectTransform.localScale);
var parentRect = ((RectTransform)rectTransform.parent);
var parentSize = parentRect.rect.size;
parentSize.Scale(parentRect.localScale);
if (size.x > parentSize.x && size.y > parentSize.y)
{
var p1 = Camera.main.ScreenToWorldPoint(midPoint);
var p2 = transform.InverseTransformPoint(p1);
var pivotP = rectTransform.pivot * rectTransform.rect.size;
var p3 = (Vector2)p2 + pivotP;
var newPivot = p3 / rectTransform.rect.size;
newPivot = new Vector2(Mathf.Clamp01(newPivot.x), Mathf.Clamp01(newPivot.y));
rectTransform.SetPivot(newPivot);
}
else
{
rectTransform.SetPivot(new Vector2(0.5f, 0.5f));
}

transform.localScale = new Vector3(scaleX, scaleX, transform.localScale.z);
}
}

//private IEnumerator ZoomInTheEndOfFrame(float mouseScrollWheel, int touchCount, Touch touchZero, Touch touchOne, Vector3 mousePosition) // testing failed
//{
// yield return new WaitForEndOfFrame();
// ZoomWithData(mouseScrollWheel, touchCount, touchZero, touchOne, mousePosition);
//}

}

为了在不让图像“跳跃”的情况下改变枢轴,我使用了一个扩展脚本:

using UnityEngine;

public static class RectTranformExtension
{
/// <summary>
/// Set pivot without changing the position of the element
/// </summary>
public static void SetPivot(this RectTransform rectTransform, Vector2 pivot)
{
Vector3 deltaPosition = rectTransform.pivot - pivot; // get change in pivot
deltaPosition.Scale(rectTransform.rect.size); // apply sizing
deltaPosition.Scale(rectTransform.localScale); // apply scaling
deltaPosition = rectTransform.rotation * deltaPosition; // apply rotation

rectTransform.pivot = pivot; // change the pivot
rectTransform.localPosition -= deltaPosition; // reverse the position change
}
}

最佳答案

经过几天的努力,我终于解决了它。解决方法是在OnDrag()中调用Zoom(),缩放时屏蔽scollView组件接收拖动相关事件。如果你想使用它,只需复制下面我的代码,不要做任何改动。这是我在设备上进行大量测试后得到的。有一些我不想解释的细节太多的潜在小陷阱,只需复制并使用它。我还建议使用当前 content.scale.x * 0.001 的速度

代码:

// please note that scrollRect is the component on the scroll view game object, not where this script is

public void OnDrag(PointerEventData eventData)
{
Zoom();
if (Input.touchCount <= 1) scrollRect.OnDrag(eventData);
}

public void OnEndDrag(PointerEventData eventData)
{
scrollRect.OnEndDrag(eventData);
}

public void OnBeginDrag(PointerEventData eventData)
{
if (Input.touchCount <= 1) scrollRect.OnBeginDrag(eventData);
}

附言在原始脚本中,有一部分如果未通过边界检查,则将枢轴设置为 (0.5,0.5)。这部分应该被注释掉(如果检查不通过则不需要更改pivot)。

还有一点,你可以使用任何你想要的父子关系,但你必须确保接收 onDrag 事件的对象有一个完整的图像来捕捉事件。

关于c# - 在 Unity 中使用捏合手势缩放 ScrollView 内容的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58909983/

37 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com