精确的捏合缩放

4t0*_*m1c 5 c# camera multi-touch unity-game-engine pinchzoom

我试图弄清楚如何在 Unity3D/C# 中为我的相机创建精确的捏缩放。它必须基于地形上的物理点。下图展示了我想要达到的效果。

在此输入图像描述

相机是 null 的子级,它会缩放(在 0,1 和 1 之间)以“缩放”,以免干扰相机的视角。

所以到目前为止我想到的是每个手指必须使用光线投射来获取 A 和 B 点以及相机父级的当前比例。

例如:A (10,0,2),B (14,0,4),S (0.8,0.8,0.8) >> A (10,0,2),B (14,0,4),S ( 0.3,0.3,0.3)

手指的位置会发生变化,但通过改变比例,hit.point 值应保持不变。

奖励:作为奖励,如果相机能变焦到手指之间的点,而不仅仅是中心,那就太好了。

非常感谢您的任何帮助或参考。

编辑: 到目前为止,我已经在下面提出了这一点,但它并不按照我想要的方式准确。它融合了我上面的一些想法,我认为问题在于它不应该是 /1000,而应该是一个包含当前比例的方程。

if (Input.touchCount == 2) {
        if (!CamZoom) {
            CamZoom = true;
            var rayA = Camera.main.ScreenPointToRay (Input.GetTouch (0).position);
            var rayB = Camera.main.ScreenPointToRay (Input.GetTouch (1).position);
            int layerMask = (1 << 8);
            if (Physics.Raycast (rayA, out hit, 1500, layerMask)) {
                PrevA = new Vector3 (hit.point.x, 0, hit.point.z);
                Debug.Log ("PrevA: " + PrevA);
            }
            if (Physics.Raycast (rayB, out hit, 1500, layerMask)) {
                PrevB = new Vector3 (hit.point.x, 0, hit.point.z);
                Debug.Log ("PrevB: " + PrevB);
            }
            PrevDis = Vector3.Distance (PrevB, PrevA);
            Debug.Log ("PrevDis: " + PrevDis);
            PrevScaleV = new Vector3 (PrevScale, PrevScale, PrevScale);
            PrevScale = this.transform.localScale.x;
            Debug.Log ("PrevScale: " + PrevScale);
        }
        if (CamZoom) {
            var rayA = Camera.main.ScreenPointToRay (Input.GetTouch (0).position);
            var rayB = Camera.main.ScreenPointToRay (Input.GetTouch (1).position);
            int layerMask = (1 << 8);
            if (Physics.Raycast (rayA, out hit, 1500, layerMask)) {
                NewA = new Vector3 (hit.point.x, 0, hit.point.z);
            }
            if (Physics.Raycast (rayB, out hit, 1500, layerMask)) {
                NewB = new Vector3 (hit.point.x, 0, hit.point.z);
            }
            DeltaDis = PrevDis - (Vector3.Distance (NewB, NewA));
            Debug.Log ("Delta: " + DeltaDis);
            NewScale = PrevScale + (DeltaDis / 1000);
            Debug.Log ("NewScale: " + NewScale);
            NewScaleV = new Vector3 (NewScale, NewScale, NewScale);
            this.transform.localScale = Vector3.Lerp(PrevScaleV,NewScaleV,Time.deltaTime);
            PrevScaleV = NewScaleV;
            CamAngle();
        }
    }
Run Code Online (Sandbox Code Playgroud)

Ste*_*lis 6

介绍

\n

我最近必须解决同样的问题,并以与您相同的方法开始,即认为用户正在与场景交互,我们需要弄清楚他们的手指在场景中的位置以及它们的方式\正在移动,然后反转这些动作以将它们反映在我们的相机中。

\n

然而,我们真正想要实现的目标要简单得多。我们只是希望用户感觉他们捏合的屏幕区域的大小变化与捏合变化的比例相同。

\n

目的

\n

首先让我们总结一下我们的目标和约束:

\n
    \n
  • 目标:当用户捏合时,捏合的区域应该出现缩放以匹配捏合。
  • \n
  • 约束:我们不想改变任何对象的比例。
  • \n
  • 约束:我们的相机是透视相机。
  • \n
  • 约束:我们不想改变相机的视野。
  • \n
  • 约束:我们的解决方案应该与分辨率/设备无关。
  • \n
\n

考虑到所有这些,并且考虑到我们知道使用透视相机,物体在靠近时会显得更大,而在远离时会显得较小,似乎缩放用户所看到的内容的唯一解决方案是移动摄像机进/出场景。

\n

解决方案

\n

为了使场景在我们的焦点处看起来更大,我们需要定位相机,以便相机的平截头体在焦点处的横截面相应地更小。

\n

这是一个更好解释的图表:

\n

解决方案

\n

图像的上半部分是我们想要实现的“幻觉”,即使用户在屏幕上扩展的区域扩大两倍。图像的下半部分是我们需要如何移动相机来定位截锥体,以给人留下这种印象。

\n

那么问题就变成了我需要将相机移动多远才能获得所需的横截面

\n

为此,我们可以利用当相机的视场角(以度为单位)为\xce\xb8时,距相机距离d处的平截头体高度h之间的关系。

\n

三角函数

\n

由于我们的视场角\xce\xb8根据我们商定的约束是恒定的,因此我们可以看到hd成线性比例。

\n

线性比例

\n

了解这一点很有用,因为这意味着h的任何乘法/除法都同样反映在d中。这意味着我们可以直接将乘数应用于距离,无需额外计算将高度转换为所需的距离!

\n

执行

\n

所以我们终于得到了代码。

\n

首先,我们将用户期望的尺寸变化作为之前手指之间距离的倍数:

\n
Touch touch0 = Input.GetTouch(0);\nTouch touch1 = Input.GetTouch(1);\nVector2 prevTouchPosition0 = touch0.position - touch0.deltaPosition;\nVector2 prevTouchPosition1 = touch1.position - touch1.deltaPosition;\nfloat touchDistance = (touch1.position - touch0.position).magnitude;\nfloat prevTouchDistance = (prevTouchPosition1 - prevTouchPosition1).magnitude;\nfloat touchChangeMultiplier = touchDistance / prevTouchDistance;\n
Run Code Online (Sandbox Code Playgroud)\n

现在我们知道用户想要缩放他们捏的区域有多大,我们可以将相机与其焦点的距离缩放相反的量。

\n

焦点是相机的前向光线与您要放大的物体的交点。为了举一个简单的例子,我将仅使用原点作为焦点。

\n
Vector3 focalPoint = Vector3.zero;\nVector3 direction = camera.transform.position - focalPoint;\nfloat newDistance = direction.magnitude / touchChangeMultiplier;\ncamera.transform.position = newDistance * direction.normalized;\ncamera.transform.LookAt(focalPoint);\n
Run Code Online (Sandbox Code Playgroud)\n

这里的所有都是它的。

\n

奖金

\n

这个答案已经很长了。因此,简要回答有关使相机聚焦在您捏合的位置的问题:

\n
    \n
  • 当您第一次检测到 2 根手指触摸时,存储屏幕位置和相关的世界位置。
  • \n
  • 变焦时,移动相机将世界位置放回到相同的屏幕位置。
  • \n
\n


Jav*_*cia 1

这是一个小例子:

        if (_Touches.Length == 2)
        {
            Vector2 _CameraViewsize = new Vector2(_Camera.pixelWidth, _Camera.pixelHeight);

            Touch _TouchOne = _Touches[0];
            Touch _TouchTwo = _Touches[1];

            Vector2 _TouchOnePrevPos = _TouchOne.position - _TouchOne.deltaPosition;
            Vector2 _TouchTwoPrevPos = _TouchTwo.position - _TouchTwo.deltaPosition;

            float _PrevTouchDeltaMag = (_TouchOnePrevPos - _TouchTwoPrevPos).magnitude;
            float _TouchDeltaMag = (_TouchOne.position - _TouchTwo.position).magnitude;

            float _DeltaMagDiff = _PrevTouchDeltaMag - _TouchDeltaMag;

            _Camera.transform.position += _Camera.transform.TransformDirection((_TouchOnePrevPos + _TouchTwoPrevPos - _CameraViewsize) * _Camera.orthographicSize / _CameraViewsize.y);

            _Camera.orthographicSize += _DeltaMagDiff * _OrthoZoomSpeed;
            _Camera.orthographicSize = Mathf.Clamp(_Camera.orthographicSize, _MinZoom, _MaxZoom) - 0.001f;

            _Camera.transform.position -= _Camera.transform.TransformDirection((_TouchOne.position + _TouchTwo.position - _CameraViewsize) * _Camera.orthographicSize / _CameraViewsize.y);

        }
Run Code Online (Sandbox Code Playgroud)

在本教程的第二个视频中对此进行了解释