在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
}
}
图片: