Unity 在zSpace上使用鼠标控制相机旋转和鼠标指引式放大缩小,在触屏上手势位置为中心放大缩小

该代码示例展示了如何在zSpace平台上使用鼠标和触屏进行交互,包括鼠标控制相机旋转、鼠标滚轮缩放以及触屏手势(如点击、拖动、捏合)来实现放大缩小。代码中定义了一个`InputController`类来处理这些输入事件,并提供了相应的事件回调。此外,还有一个`CameraControl`类用于处理相机的平移、旋转和缩放,支持基于鼠标的拖动、滚轮和双指捏合操作,以及在不同设备上平移策略的切换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在zSpace上使用鼠标控制相机旋转和鼠标指引式放大缩小,在触屏上手势位置为中心放大缩小

鼠标和触屏的操作

下面展示一些 内联代码片

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityInput = UnityEngine.Input;
using zSpace.Core.Input;
using zSpace.Core;

namespace CamControl
{
    /// <summary>
    /// Class to manage tap/drag/pinch gestures and other controls
    /// </summary>
    public class InputController : MonoBehaviour
    {
        /// <summary>
        /// All the touches we're tracking
        /// </summary>
        private List<TouchInfo> m_Touches = new List<TouchInfo>();

        /// <summary>
        /// Mouse button info
        /// </summary>
        private List<MouseButtonInfo> m_MouseInfo;

        /// <summary>
        /// zStylus
        /// </summary>
        private ZStylus m_ZStylus;

        /// <summary>
        /// Gets the number of active touches
        /// </summary>
        public int ActiveTouchCount => m_Touches.Count;

        /// <summary>
        /// Tracks if any of the mouse buttons were pressed this frame
        /// </summary>
        public bool MouseButtonPressedThisFrame { get; private set; }

        /// <summary>
        /// Tracks if the mouse moved this frame
        /// </summary>
        public bool MouseMovedOnThisFrame { get; private set; }

        /// <summary>
        /// Tracks if a touch began this frame
        /// </summary>
        public bool TouchPressedThisFrame { get; private set; }

        /// <summary>
        /// Current mouse pointer info
        /// </summary>
        public PointerInfo BasicMouseInfo { get; private set; }

        /// <summary>
        /// Event called when a pointer press is detected
        /// </summary>
        public event Action<PointerActionInfo> Pressed;

        /// <summary>
        /// Event called when a pointer is released
        /// </summary>
        public event Action<PointerActionInfo> Released;

        /// <summary>
        /// Event called when a pointer is tapped
        /// </summary>
        public event Action<PointerActionInfo> Tapped;

        /// <summary>
        /// Event called when a drag starts
        /// </summary>
        public event Action<PointerActionInfo> StartedDrag;

        /// <summary>
        /// Event called when a pointer is dragged
        /// </summary>
        public event Action<PointerActionInfo> Dragged;

        /// <summary>
        /// Event called when a pointer starts a hold
        /// </summary>
        public event Action<PointerActionInfo> StartedHold;

        /// <summary>
        /// Event called when the user scrolls the mouse wheel
        /// </summary>
        public event Action<WheelInfo> SpunWheel;

        /// <summary>
        /// Event called when the user performs a pinch gesture
        /// </summary>
        public event Action<PinchInfo> Pinched;

        /// <summary>
        /// Event called whenever the mouse is moved
        /// </summary>
        public event Action<PointerInfo> MouseMoved;

        /// <summary>
        /// 按下zSpace触控笔上的按键
        /// </summary>
        public event Action<int> ZStylusPressed;
		
        private void Awake()
        {
            // Mouse specific initialization
            if (UnityInput.mousePresent)
            {
                m_MouseInfo = new List<MouseButtonInfo>();
                BasicMouseInfo = new MouseCursorInfo { currentPosition = UnityInput.mousePosition };

                for (int i = 0; i < 3; ++i)
                {
                    m_MouseInfo.Add(new MouseButtonInfo
                    {
                        currentPosition = UnityInput.mousePosition,
                        mouseButtonId = i
                    });
                }
            }

            UnityInput.simulateMouseWithTouches = false;
        }

        private void Start()
        {
            if (ZProvider.IsInitialized)
            {
                m_ZStylus = FindObjectOfType<ZStylus>();
                if (m_ZStylus != null)
                {
                    m_ZStylus.OnButtonPressed.AddListener(OnZStylusButtonPressed);
                }
            }
        }

        private void OnZStylusButtonPressed(ZPointer zPointer, int buttonId)
        {
            ZStylusPressed?.Invoke(buttonId);
        }

        /// <summary>
        /// Update all input
        /// </summary>
        private void Update()
        {
            if (BasicMouseInfo != null)
            {
                // Mouse was detected as present
                UpdateMouse();
            }
            // Handle touches
            UpdateTouches();
        }
        /// <summary>
        /// Perform logic to update mouse/pointing device
        /// </summary>
        private void UpdateMouse()
        {
            BasicMouseInfo.previousPosition = BasicMouseInfo.currentPosition;
            BasicMouseInfo.currentPosition = UnityInput.mousePosition;
            BasicMouseInfo.delta = BasicMouseInfo.currentPosition - BasicMouseInfo.previousPosition;
            MouseMovedOnThisFrame = BasicMouseInfo.delta.sqrMagnitude >= Mathf.Epsilon;
            MouseButtonPressedThisFrame = false;

            // Move event
            if (BasicMouseInfo.delta.sqrMagnitude > Mathf.Epsilon)
            {
                MouseMoved?.Invoke(BasicMouseInfo);
            }
            // Button events
            for (int i = 0; i < 3; ++i)
            {
                MouseButtonInfo mouseButton = m_MouseInfo[i];
                mouseButton.delta = BasicMouseInfo.delta;
                mouseButton.previousPosition = BasicMouseInfo.previousPosition;
                mouseButton.currentPosition = BasicMouseInfo.currentPosition;
                if (UnityInput.GetMouseButton(i))
                {
                    if (!mouseButton.isDown)
                    {
                        // First press
                        MouseButtonPressedThisFrame = true;
                        mouseButton.isDown = true;
                        mouseButton.startPosition = UnityInput.mousePosition;
                        mouseButton.startTime = Time.realtimeSinceStartup;
                        mouseButton.startedOverUI = EventSystem.current.IsPointerOverGameObject(-mouseButton.mouseButtonId - 1);

                        // Reset some stuff
                        mouseButton.totalMovement = 0;
                        mouseButton.isDrag = false;
                        mouseButton.wasHold = false;
                        mouseButton.isHold = false;
                        mouseButton.flickVelocity = Vector2.zero;

                        Pressed?.Invoke(mouseButton);
                    }
                    else
                    {
                        float moveDist = mouseButton.delta.magnitude;
                        // Dragging?
                        mouseButton.totalMovement += moveDist;
                        if (mouseButton.totalMovement > 0f)
                        {
                            bool wasDrag = mouseButton.isDrag;

                            mouseButton.isDrag = true;
                            if (mouseButton.isHold)
                            {
                                mouseButton.wasHold = mouseButton.isHold;
                                mouseButton.isHold = false;
                            }

                            // Did it just start now?
                            if (!wasDrag)
                            {
                                StartedDrag?.Invoke(mouseButton);
                            }
                            Dragged?.Invoke(mouseButton);

                            // Flick?
                            if (moveDist > 2f)
                            {
                                mouseButton.flickVelocity = (mouseButton.flickVelocity * 0.2f) + (mouseButton.delta * 0.8f);
                            }
                            else
                            {
                                mouseButton.flickVelocity = Vector2.zero;
                            }
                        }
                        else
                        {
                            // Stationary?
                            if (!mouseButton.isHold && !mouseButton.isDrag && Time.realtimeSinceStartup - mouseButton.startTime >= 0.8f)
                            {
                                mouseButton.isHold = true;
                                StartedHold?.Invoke(mouseButton);
                            }
                        }
                    }
                }
                else // Mouse button not up
                {
                    if (mouseButton.isDown) // Released
                    {
                        mouseButton.isDown = false;
                        // Quick enough (with no drift) to be a tap?
                        if (!mouseButton.isDrag && Time.realtimeSinceStartup - mouseButton.startTime < 0.2f)
                        {
                            Tapped?.Invoke(mouseButton);
                        }
                        Released?.Invoke(mouseButton);
                    }
                }
            }

            // Mouse wheel
            if (Mathf.Abs(UnityInput.GetAxis("Mouse ScrollWheel")) > Mathf.Epsilon)
            {
                SpunWheel?.Invoke(new WheelInfo
                {
                    zoomAmount = UnityInput.GetAxis("Mouse ScrollWheel")
                });
            }
        }

        private void OnDisable()
        {
            for (int i = 0; i < m_MouseInfo.Count; i++)
            {
                m_MouseInfo[i].isDown = false;
            }
            m_Touches.Clear();
        }
        /// <summary>
        /// Update all touches
        /// </summary>
        private void UpdateTouches()
        {
            ClearNoUseTouchInWebGL();

            TouchPressedThisFrame = false;
            for (int i = 0; i < UnityInput.touchCount; ++i)
            {
                Touch touch = UnityInput.GetTouch(i);

                // Find existing touch, or create new one
                TouchInfo existingTouch = m_Touches.FirstOrDefault(t => t.touchId == touch.fingerId);

                if (existingTouch == null)
                {
                    existingTouch = new TouchInfo
                    {
                        touchId = touch.fingerId,
                        startPosition = touch.position,
                        currentPosition = touch.position,
                        previousPosition = touch.position,
                        startTime = Time.realtimeSinceStartup,
                        startedOverUI = EventSystem.current.IsPointerOverGameObject(touch.fingerId)
                    };

                    m_Touches.Add(existingTouch);
                }
                switch (touch.phase)
                {
                    case TouchPhase.Began:
                        TouchPressedThisFrame = true;
                        Pressed?.Invoke(existingTouch);
                        break;

                    case TouchPhase.Moved:
                        bool wasDrag = existingTouch.isDrag;
                        UpdateMovingFinger(touch, existingTouch);

                        // Is this a drag?
                        existingTouch.isDrag = existingTouch.totalMovement >= 5f;

                        if (existingTouch.isDrag)
                        {
                            if (existingTouch.isHold)
                            {
                                existingTouch.wasHold = existingTouch.isHold;
                                existingTouch.isHold = false;
                            }
                            // Did it just start now?
                            if (!wasDrag)
                            {
                                StartedDrag?.Invoke(existingTouch);
                            }
                            Dragged?.Invoke(existingTouch);

                            if (existingTouch.delta.sqrMagnitude > 2f * 2f)
                            {
                                existingTouch.flickVelocity = existingTouch.flickVelocity * 0.2f + existingTouch.delta * 0.8f;
                            }
                            else
                            {
                                existingTouch.flickVelocity = Vector2.zero;
                            }
                        }
                        else
                        {
                            UpdateHoldingFinger(existingTouch);
                        }
                        break;

                    case TouchPhase.Canceled:
                    case TouchPhase.Ended:
                        // Could have moved a bit
                        UpdateMovingFinger(touch, existingTouch);
                        // Quick enough (with no drift) to be a tap?
                        if (!existingTouch.isDrag && Time.realtimeSinceStartup - existingTouch.startTime < 0.2f)
                        {
                            Tapped?.Invoke(existingTouch);
                        }
                        Released?.Invoke(existingTouch);

                        // Remove from track list
                        m_Touches.Remove(existingTouch);
                        break;

                    case TouchPhase.Stationary:
                        UpdateMovingFinger(touch, existingTouch);
                        UpdateHoldingFinger(existingTouch);
                        existingTouch.flickVelocity = Vector2.zero;
                        break;
                }
            }

            if (ActiveTouchCount >= 2 && (m_Touches[0].isDrag || m_Touches[1].isDrag))
            {
                Pinched?.Invoke(new PinchInfo
                {
                    touch1 = m_Touches[0],
                    touch2 = m_Touches[1]
                });
            }
        }
         /// <summary>
        /// 清除webgl上多指离开屏幕,但是未触发离开事件,导致的无效触控信息
        /// </summary>
        private void ClearNoUseTouchInWebGL()
        {
#if UNITY_WEBGL
            if (m_Touches.Count == 0)
            {
                return;
            }

            // 找出没有用到的Touches
            List<TouchInfo> noUseTouches = new List<TouchInfo>(m_Touches.ToArray());
            for (int i = 0; i < UnityInput.touchCount; ++i)
            {
                Touch touch = UnityInput.GetTouch(i);
                // Find existing touch
                TouchInfo existingTouch = noUseTouches.FirstOrDefault(t => t.touchId == touch.fingerId);
                if (existingTouch != null)
                {
                    noUseTouches.Remove(existingTouch);
                }
            }

            // 进行移除(执行移除相关的事件后再移除)
            for (int i = 0; i < noUseTouches.Count; i++)
            {
                TouchInfo noUseTouchInfo = noUseTouches[i];
                TouchInfo existingTouch = m_Touches.FirstOrDefault(t => t.touchId == noUseTouchInfo.touchId);
                if (existingTouch != null)
                {
                    m_Touches.Remove(existingTouch);
                }
            }
#endif
        }

        /// <summary>
        /// Update a TouchInfo that might be holding
        /// </summary>
        /// <param name="existingTouch"></param>
        private void UpdateHoldingFinger(PointerActionInfo existingTouch)
        {
            if (!existingTouch.isHold && !existingTouch.isDrag && Time.realtimeSinceStartup - existingTouch.startTime >= 0.8f)
            {
                existingTouch.isHold = true;
                StartedHold?.Invoke(existingTouch);
            }
        }

        /// <summary>
        /// Update a TouchInfo with movement
        /// </summary>
        /// <param name="touch">The Unity touch object</param>
        /// <param name="existingTouch">The object that's tracking Unity's touch</param>
        private void UpdateMovingFinger(Touch touch, PointerActionInfo existingTouch)
        {
            existingTouch.previousPosition = existingTouch.currentPosition;
            existingTouch.currentPosition = touch.position;
            existingTouch.delta = existingTouch.currentPosition - existingTouch.previousPosition;
            existingTouch.totalMovement += existingTouch.delta.magnitude;
        }
    }

    #region 输入相关类
    /// <summary>
    /// Class to track information about a passive pointer input
    /// </summary>
    public abstract class PointerInfo
    {
        /// <summary>
        /// Current pointer position
        /// </summary>
        public Vector2 currentPosition;

        /// <summary>
        /// Previous frame's pointer position
        /// </summary>
        public Vector2 previousPosition;

        /// <summary>
        /// Movement delta for this frame
        /// </summary>
        public Vector2 delta;

        /// <summary>
        /// Tracks if this pointer began over UI
        /// </summary>
        public bool startedOverUI;
    }


    /// <summary>
    /// Class to track information about an active pointer input
    /// </summary>
    public class PointerActionInfo : PointerInfo
    {
        /// <summary>
        /// Position where the input started
        /// </summary>
        public Vector2 startPosition;

        /// <summary>
        /// Flick velocity is a moving average of deltas
        /// </summary>
        public Vector2 flickVelocity;

        /// <summary>
        /// Total movement for this pointer, since being held down
        /// </summary>
        public float totalMovement;

        /// <summary>
        /// Time hold started
        /// </summary>
        public float startTime;

        /// <summary>
        /// Has this input been dragged?
        /// </summary>
        public bool isDrag;

        /// <summary>
        /// Is this input holding?
        /// </summary>
        public bool isHold;

        /// <summary>
        /// Was this input previously holding, then dragged?
        /// </summary>
        public bool wasHold;
    }


    /// <summary>
    /// Information about a pinch gesture
    /// </summary>
    public struct PinchInfo
    {
        /// <summary>
        /// The first touch involved in the pinch
        /// </summary>
        public TouchInfo touch1;

        /// <summary>
        /// The second touch involved in the pinch
        /// </summary>
        public TouchInfo touch2;
    }


    /// <summary>
    /// Touch info
    /// </summary>
    public class TouchInfo : PointerActionInfo
    {
        /// <summary>
        /// Our touch ID
        /// </summary>
        public int touchId;
    }


    class MouseCursorInfo : PointerInfo
    {
    }


    /// <summary>
    /// Info for mouse
    /// </summary>
    public class MouseButtonInfo : PointerActionInfo
    {
        /// <summary>
        /// Is this mouse button down
        /// </summary>
        public bool isDown;

        /// <summary>
        /// Our mouse button id
        /// </summary>
        public int mouseButtonId;
    }


    /// <summary>
    /// Information about a zoom action (usually mouse-wheel or button based)
    /// </summary>
    public struct WheelInfo
    {
        /// <summary>
        /// Amount of zoom
        /// </summary>
        public float zoomAmount;
    }
    #endregion
}

动态控制相机的位置

下面展示一些 内联代码片

using Ardez.Hotfix;
using ArdezEvent;
using UnityEngine;
using UnityEngine.EventSystems;
using zSpace.Core;

namespace CamControl
{
    public enum ZoomPanPolicy
    {
        /// <summary>
        /// 沿着视野正对的平面平移
        /// </summary>
        LockToScreenAlignedPlane = 0,

        /// <summary>
        /// 沿着目标所在的XZ平面平移(水平面移动)
        /// </summary>
        LockTargetPlane = 1,
    }

    [RequireComponent(typeof(InputController))]
    [DefaultExecutionOrder(ZCameraRig.ScriptPriority - 1)]
    public class CameraControl : MonoBehaviour
    {
        [SerializeField, Tooltip("ZFrame,用于控制缩放")]
        private ZFrame m_ZFrame = null;
        [SerializeField, Tooltip("主相机ZCamera")]
        private Camera m_MainCamera = null;
        [SerializeField, Tooltip("缩放范围,默认1视野最小适合观察小模型,20视野最大适合观察大模型")]
        private Vector2 m_ZoomRange = new Vector2(1, 20);
        [SerializeField, Tooltip("垂直方向最小值和最大值")]
        private Vector2 m_AngleLimit = new Vector2(-20f, 80f);
        [SerializeField, Tooltip("缩放速率")]
        private float m_ZoomRate = 60f;
        [SerializeField, Tooltip("平移策略(大地形建议使用LockTargetPlane方式,只在目标点XZ平面上移动)")]
        private ZoomPanPolicy m_ZoomPanPolicy = ZoomPanPolicy.LockToScreenAlignedPlane;
        [SerializeField, Tooltip("放大时的移动速率")]
        private float m_ZoomPanRate = 65f;
        [SerializeField, Tooltip("水平方向旋转速率")]
        private float m_HorizontalRotateRate = 60;
        [SerializeField, Tooltip("垂直方向旋转速率")]
        private float m_VerticalRotateRate = 30;
        [SerializeField]
        private float RotateLerpSpeed = 4f;
        [SerializeField]
        private float ZoomLerpSpeed = 4f;
        [SerializeField]
        private float MoveLerpSpeed = 4f;
        [SerializeField, Tooltip("是否使用角度插值")]
        private bool m_UseAngleLerp = false;                                    // 是否使用角度插值
        [SerializeField, Tooltip("非zSpace是否使用缩放平移")]
        private bool m_UseZoomMove = false;                                     // 是否使用缩放平移
        [SerializeField]
        private bool m_IgnoreStartOverUI = false;                               // 是否忽略从UI上面开始滑动

        private InputController m_InputController;

        private Vector3 m_StartPosition;                                // 初始位置
        private Vector3 m_StartAngles;                                  // 初始角度
        private float m_StartZoomScale;                                 // 初始缩放
        private float m_CurrentZoomScale;                               // 当前的ZFrame缩放
        private float m_DesiredZoomScale;                               // 期望的ZFrame缩放
        private float m_StartAxisZValue;                                // 初始Z轴距离
        private float m_CurrentAxisZValue;
        private float m_DesiredAxisZValue;
        private Vector3 m_CurrentAngles;                                // 当前角度
        private Vector3 m_DesiredAngles;                                // 期望角度
        private Vector3 m_CurrentPosition;                              // CameraControl当前位置(缩放平移用)
        private Vector3 m_DesiredPosition;                              // CameraControl期望位置(缩放平移用)
        public float ZoomMaxRange => m_ZoomRange.y - m_ZoomRange.x;
        
        private void Awake()
        {
            if (m_MainCamera == null)
            {
                m_MainCamera = Camera.main;
            }

            // 初始化参数
            m_StartPosition = m_CurrentPosition = m_DesiredPosition = transform.position;
            m_StartZoomScale = m_CurrentZoomScale = m_DesiredZoomScale = m_ZFrame.ViewerScale;
            m_StartAxisZValue = m_CurrentAxisZValue = m_DesiredAxisZValue = m_ZFrame.transform.parent.localPosition.z;
            m_CurrentAngles = m_DesiredAngles = transform.eulerAngles;

            Revert();

            // 订阅拖拽、滚轮、双指捏合事件、笔按下事件
            m_InputController = GetComponent<InputController>();
            m_InputController.Dragged += Dragged;
            m_InputController.SpunWheel += SpunWheel;
            m_InputController.Pinched += Pinched;
            m_InputController.ZStylusPressed += ZStylusPressed;

            // 订阅复位事件
            //EventUtil.AddListener(EventID.EVENT_REFRESHBTN, OnRevert);
        }

        private void OnRevert(ArdezEvent.EventArgs eventArgs)
        {
            Revert();
        }

        private void ZStylusPressed(int buttonId)
        {
            if (buttonId == 2)
            {
                //m_UseAngleLerp = true;
                // 左键
                m_DesiredAngles.y -= 30f;
            }
            else if (buttonId == 1)
            {
               // m_UseAngleLerp = true;
                // 右键
                m_DesiredAngles.y += 30f;
            }

            if (m_UseAngleLerp)
            {
                //m_DesiredAngles.y = WrapAngle360(m_DesiredAngles.y);
                //m_CurrentAngles.y = WrapAngle360(m_CurrentAngles.y);

                //m_UseAngleLerp = false;
            }
        }

        /// <summary>
        /// 设置新的目标位置
        /// </summary>
        /// <param name="viewConfig">视野配置</param>
        public void SetNewPos(ViewConfig viewConfig)
        {
            SetNewPos(viewConfig.transform, viewConfig.TargetDis, viewConfig.TargetZValue, viewConfig.MinViewScale, viewConfig.MaxViewScale, viewConfig.MinAngle, viewConfig.MaxAngle, viewConfig.UseSmoothMove);

            SetUseZoomMove(viewConfig.UseZoomMove);
        }

        /// <summary>
        /// 设置是否使用缩放平移
        /// </summary>
        public void SetUseZoomMove(bool useZoomMove)
        {
            m_UseZoomMove = useZoomMove;
        }

        /// <summary>
        /// 设置新的目标位置
        /// </summary>
        /// <param name="targetTrans">目标Transform</param>
        /// <param name="targetZFrameViewScale">相机缩放</param>
        /// <param name="targetZSpaceAxisZValue">zSpace局部Z坐标,非zSpace设备上固定为0</param>
        /// <param name="minViewScale">相机最小缩放值,默认为1,物体显得很大</param>
        /// <param name="maxViewScale">相机最大缩放值,默认为20, 物体显得很小</param>
        /// <param name="minAngle">相机垂直方向最小角度,默认-20</param>
        /// <param name="maxAngle">相机垂直方向最大角度,默认80</param>
        public void SetNewPos(Transform targetTrans, float? targetZFrameViewScale = null, float? targetZSpaceAxisZValue = null, float? minViewScale = null, float? maxViewScale = null, float? minAngle = null, float? maxAngle = null, bool useSmoothMove = true)
        {
            SetNewPos(targetTrans.position, targetTrans.eulerAngles, targetZFrameViewScale, targetZSpaceAxisZValue, minViewScale, maxViewScale, minAngle, maxAngle, useSmoothMove);
        }
        /// <summary>
        /// 设置新的目标位置
        /// </summary>
        /// <param name="position">目标位置</param>
        /// <param name="eulerAngles">目标位置的欧拉角</param>
        /// <param name="targetZFrameViewScale">相机缩放</param>
        /// <param name="targetZSpaceAxisZValue">zSpace局部Z坐标,非zSpace设备上固定为0</param>
        /// <param name="minViewScale">相机最小缩放值,默认为1,物体显得很大</param>
        /// <param name="maxViewScale">相机最大缩放值,默认为20, 物体显得很小</param>
        /// <param name="minAngle">相机垂直方向最小角度,默认-20</param>
        /// <param name="maxAngle">相机垂直方向最大角度,默认80</param>
        public void SetNewPos(Vector3 position, Vector3 eulerAngles, float? targetZFrameViewScale = null, float? targetZSpaceAxisZValue = null, float? minViewScale = null, float? maxViewScale = null, float? minAngle = null, float? maxAngle = null, bool useSmoothMove = true)
        {
            m_StartPosition = position;
            m_StartAngles = eulerAngles;
            if (targetZFrameViewScale != null) m_StartZoomScale = targetZFrameViewScale.Value;
            if (targetZSpaceAxisZValue != null) m_StartAxisZValue = targetZSpaceAxisZValue.Value;
            if (minViewScale != null) m_ZoomRange.x = minViewScale.Value;
            if (maxViewScale != null) m_ZoomRange.y = maxViewScale.Value;
            if (minAngle != null) m_AngleLimit.x = minAngle.Value;
            if (maxAngle != null) m_AngleLimit.y = maxAngle.Value;

            if (useSmoothMove == false)
            {
                m_CurrentPosition = m_DesiredPosition = m_StartPosition;
                m_CurrentAngles = m_DesiredAngles = m_StartAngles;
                m_CurrentZoomScale = m_DesiredZoomScale = m_StartZoomScale;
                m_CurrentAxisZValue = m_DesiredAxisZValue = m_StartAxisZValue;
            }

            // 非zSpace设备上固定为0
            //if (ZProvider.IsInitialized == false)
            //{
            //    m_StartAxisZValue = 0f;
            //}

            #region 校验
            // 缩放范围校验
            if (m_ZoomRange.x > m_ZoomRange.y)
            {
                Debug.LogErrorFormat("MinViewScale: {0} > MaxViewScale: {1}, 请检查!", m_ZoomRange.x, m_ZoomRange.y);
            }
            if (m_StartZoomScale < m_ZoomRange.x)
            {
                Debug.LogErrorFormat("StartZoomScale: {0} < MinViewScale: {1}, 请检查!", m_StartZoomScale, m_ZoomRange.x);
            }
            if (m_StartZoomScale > m_ZoomRange.y)
            {
                Debug.LogErrorFormat("StartZoomScale: {0} > MaxViewScale: {1}, 请检查!", m_StartZoomScale, m_ZoomRange.y);
            }

            // 俯仰角范围校验
            if (m_AngleLimit.x > m_AngleLimit.y)
            {
                Debug.LogErrorFormat("MinAngle: {0} > MaxAngle: {1}, 请检查!", m_AngleLimit.x, m_AngleLimit.y);
            }
            if (WrapAngle180(m_StartAngles.x) < m_AngleLimit.x)
            {
                Debug.LogErrorFormat("StartAngle: {0} < MinAngle: {1}, 请检查!", WrapAngle180(m_StartAngles.x), m_AngleLimit.x);
            }
            if (WrapAngle180(m_StartAngles.x) > m_AngleLimit.y)
            {
                Debug.LogErrorFormat("StartAngle: {0} > MaxAngle: {1}, 请检查!", WrapAngle180(m_StartAngles.x), m_AngleLimit.y);
            }

            // Z轴坐标校验
            if (m_StartAxisZValue > 0)
            {
                Debug.LogErrorFormat("TargetZValue: {0}, 建议使用0或者负值, 请检查!", m_StartAxisZValue);
            }
            #endregion

            Revert();
        }

        /// <summary>
        /// 复位
        /// </summary>
        public void Revert()
        {
            m_DesiredPosition = m_StartPosition;
            m_DesiredAngles = m_StartAngles;
            m_DesiredZoomScale = m_StartZoomScale;
            m_DesiredAxisZValue = m_StartAxisZValue;
            m_UseAngleLerp = true;
        }

        /// <summary>
        /// 滑动屏幕旋转
        /// </summary>
        private void Dragged(PointerActionInfo pointerActionInfo)
        {
            if (Input.touchCount > 1)
            {
                return;
            }

            if (m_IgnoreStartOverUI == false && pointerActionInfo.startedOverUI)
            {
                return;
            }

            // 垂直方向,Camera的X轴角度
            m_DesiredAngles.x -= pointerActionInfo.delta.y * m_VerticalRotateRate * Time.deltaTime;
            m_DesiredAngles.x = WrapAngle180(m_DesiredAngles.x);
            m_DesiredAngles.x = Mathf.Clamp(m_DesiredAngles.x, m_AngleLimit.x, m_AngleLimit.y);
            // 水平方向,Camera的Y轴角度
            m_DesiredAngles.y += pointerActionInfo.delta.x * m_HorizontalRotateRate * Time.deltaTime;

            if (m_UseAngleLerp)
            {
                m_CurrentAngles.y = WrapAngle360(m_CurrentAngles.y);
                m_DesiredAngles.y = WrapAngle360(m_DesiredAngles.y);
                m_DesiredAngles.y = FindNearestAngle(m_CurrentAngles.y, m_DesiredAngles.y);
                m_UseAngleLerp = false;
            }
        }
        /// <summary>
        /// 滚动鼠标
        /// </summary>
        private void SpunWheel(WheelInfo wheelInfo)
        {
            if (m_IgnoreStartOverUI == false && IsPointerOverUI())
            {
                return;
            }

            m_DesiredZoomScale -= wheelInfo.zoomAmount * m_ZoomRate * Time.deltaTime * Mathf.Abs(m_DesiredZoomScale);

            if (m_UseZoomMove)
            {
                ZoomMove(wheelInfo.zoomAmount, Input.mousePosition);
            }
        }

        /// <summary>
        /// 双指捏合缩放
        /// </summary>
        private void Pinched(PinchInfo pinchInfo)
        {
            if (m_IgnoreStartOverUI == false && (pinchInfo.touch1.startedOverUI || pinchInfo.touch2.startedOverUI))
            {
                return;
            }
            Vector2 center = 0.5f * (pinchInfo.touch1.currentPosition + pinchInfo.touch2.currentPosition);
            float currentDistance = (pinchInfo.touch1.currentPosition - pinchInfo.touch2.currentPosition).magnitude;
            float prevDistance = (pinchInfo.touch1.previousPosition - pinchInfo.touch2.previousPosition).magnitude;
            float zoomChange = prevDistance / currentDistance;
            float zoomAmount = 1 - zoomChange;

            m_DesiredZoomScale -= zoomAmount * m_ZoomRate * Time.deltaTime * Mathf.Abs(m_DesiredZoomScale);

            if (m_UseZoomMove)
            {
                ZoomMove(zoomAmount, center);
            }
        }

        /// <summary>
        /// 放大时平移到鼠标指定位置,缩小时平移到初始位置
        /// </summary>
        private void ZoomMove(float zoomAmount, Vector2 center)
        {
            m_DesiredZoomScale = Mathf.Clamp(m_DesiredZoomScale, m_ZoomRange.x, m_ZoomRange.y);

            if (zoomAmount > 0 && m_DesiredZoomScale > m_ZoomRange.x)
            {
                // 放大,平移至center位置
                Ray ray = m_MainCamera.ScreenPointToRay(center);
                Plane plane = new Plane(transform.forward, transform.position);
                switch (m_ZoomPanPolicy)
                {
                    case ZoomPanPolicy.LockToScreenAlignedPlane:
                        plane = new Plane(transform.forward, transform.position);
                        break;
                    case ZoomPanPolicy.LockTargetPlane:
                        plane = new Plane(Vector3.up, transform.position);
                        break;
                }

                if (plane.Raycast(ray, out float enter))
                {
                    Vector3 mousePoint = ray.GetPoint(enter);
                    Vector3 direction = mousePoint - transform.position;
                    float length = Mathf.Min(direction.magnitude, 50f);
                    m_DesiredPosition += m_ZoomPanRate * zoomAmount * Time.deltaTime * length * direction.normalized;
                }
            }
            else if (zoomAmount < 0 && m_DesiredZoomScale < m_ZoomRange.y)
            {
                // 缩小,复位到初始位置
                m_DesiredPosition = Vector3.Lerp(m_DesiredPosition, m_StartPosition, Time.deltaTime * GetZoomRate() * 10f);
            }
        }
        private void Update()
        {
            // 复位和切换位置时使用角度插值
            if (m_UseAngleLerp)
            {
                m_CurrentAngles.y = Mathf.LerpAngle(m_CurrentAngles.y, m_DesiredAngles.y, Time.deltaTime * RotateLerpSpeed);
            }
            else
            {
                m_CurrentAngles.y = Mathf.Lerp(m_CurrentAngles.y, m_DesiredAngles.y, Time.deltaTime * RotateLerpSpeed);
            }
            m_CurrentAngles.x = Mathf.LerpAngle(m_CurrentAngles.x, m_DesiredAngles.x, Time.deltaTime * RotateLerpSpeed);
            transform.eulerAngles = m_CurrentAngles;

            // 缩放
            m_DesiredZoomScale = Mathf.Clamp(m_DesiredZoomScale, m_ZoomRange.x, m_ZoomRange.y);
            m_CurrentZoomScale = Mathf.Lerp(m_CurrentZoomScale, m_DesiredZoomScale, Time.deltaTime * ZoomLerpSpeed);
            m_ZFrame.ViewerScale = m_CurrentZoomScale;

            // z坐标
            m_CurrentAxisZValue = Mathf.Lerp(m_CurrentAxisZValue, m_DesiredAxisZValue, Time.deltaTime * ZoomLerpSpeed);
            SetLocalPositionZ(m_ZFrame.transform.parent, m_CurrentAxisZValue);

            // 位置
            m_CurrentPosition = Vector3.Lerp(m_CurrentPosition, m_DesiredPosition, Time.deltaTime * MoveLerpSpeed);
            transform.position = m_CurrentPosition;
        }

        #region 工具方法
        /// <summary>
        /// 是否点在UI上
        /// </summary>
        private bool IsPointerOverUI()
        {
            return EventSystem.current.IsPointerOverGameObject();
        }

        /// <summary>
        /// 角度转换(-180到180)
        /// </summary>
        private float WrapAngle180(float angle)
        {
            angle %= 360;
            if (angle > 180)
            {
                angle -= 360;
            }
            return angle;
        }

        /// <summary>
        /// 角度转换(0到360)
        /// </summary>
        private float WrapAngle360(float angle)
        {
            angle %= 360;
            if (angle < 0)
            {
                angle += 360;
            }
            return angle;
        }

        /// <summary>
        /// 找最快到达的角度,例如10°到180°,直接10插值到180就是最快
        /// 例如10°到200°,那么10直接插值到200,就不如10插值到-160°快
        /// </summary>
        /// <param name="current">输入范围0-360°</param>
        /// <param name="target">输入范围0-360°</param>
        /// <returns></returns>
        private float FindNearestAngle(float current, float target)
        {
            float min = target - current;
            if (min > 180)
            {
                target -= 360;
            }
            if (min < -180)
            {
                target += 360;
            }
            return target;
        }

        /// <summary>
        /// 根据zoom获取比例
        /// </summary>
        private float GetZoomRate()
        {
            return 1 - (m_DesiredZoomScale - m_ZoomRange.x) / ZoomMaxRange;
        }

        /// <summary>
        /// 设置局部Z坐标
        /// </summary>
        private void SetLocalPositionZ(Transform target, float newValue)
        {
            Vector3 v = target.localPosition;
            v.z = newValue;
            target.localPosition = v;
        }
        #endregion
    }
}

图片: 在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值