unity|模拟unity Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转

注意,这是在Game视图下的操作



先上代码

using UnityEngine; using UnityEngine.EventSystems; /// /// 管理着相机的移动,旋转,缩放视图,注视焦点等操作,该管理器是建立在UGUI上的 /// public class MyCameraManager : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler {/// /// 鼠标选中的目标点,或者是其他给的目标点 /// public Transform Target; public float PanSensitivity = 5f; /// /// 是否在视图内 /// private bool _isPointerOverSceneView; private LocalRotationAndScale _mouseOrbit; /// /// 主要的相机 /// public Camera MainCamera { get; private set; }/// /// 是否进入旋转 /// private bool m_rotate; /// /// 是否进入拖拽移动 /// private bool m_pan; /// /// 相机参照点 /// private Transform _point; /// /// 相机到_point点的距离 /// private float _distance; /// /// 是否开启注视 /// private bool _isOpenLookAt; /// /// 鼠标上一帧的位置 /// private Vector3 m_lastMousePosition; private bool _lockInput; /// /// 相机旋转参数 /// private float x = 0.0f; /// /// 相机旋转参数 /// private float y = 0.0f; void IPointerExitHandler.OnPointerExit(PointerEventData eventData) {_isPointerOverSceneView = false; } void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData) { _isPointerOverSceneView = true; }protected virtual void Start() { CreatCamera(); GameObject go = new GameObject("Point"); _point = go.transform; _mouseOrbit = MainCamera.gameObject.GetComponent(); if (_mouseOrbit == null) { _mouseOrbit = MainCamera.gameObject.AddComponent(); }UnlockInput(); _mouseOrbit.enabled = false; }private void CreatCamera() { GameObject go = new GameObject("_mainCamera"); MainCamera = go.AddComponent(); }public void LockInput() { _lockInput = true; }private void Enable() { UnlockInput(); }private void DisEnable() { LockInput(); MainCamera.enabled = false; }private void Update() { HandleInput(); UpdateLerpHandle(); }private void HandleInput() {if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1) || Input.GetMouseButtonUp(2)) {_mouseOrbit.enabled = false; m_rotate = false; // _isOpenLookAt = false; return; }if (_lockInput) { return; }if (Input.GetKeyDown(KeyCode.F)) {Focus(); }bool pan = Input.GetMouseButton(2); bool rotate = Input.GetKey(KeyCode.AltGr) || Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt); if (pan != m_pan) { m_pan = pan; if (m_pan) { m_rotate = false; }} else { if (rotate != m_rotate) { m_rotate = rotate; } }bool isLocked = m_rotate || pan; if (!_isPointerOverSceneView) { return; }if (Input.GetMouseButtonDown(1)) {y = MainCamera.transform.localEulerAngles.x; x = MainCamera.transform.localEulerAngles.y; _isOpenLookAt = false; } if (Input.GetMouseButton(1) && !rotate)//自身旋转 { x += Input.GetAxis("Mouse X") * 250f * 0.02f; y -= Input.GetAxis("Mouse Y") * 120f * 0.02f; y = LocalRotationAndScale.ClampAngle(y, -360f, 360f); var rotation = Quaternion.Euler(y, x, 0); MainCamera.transform.rotation = rotation; _point.position = MainCamera.transform.position + MainCamera.transform.forward * _mouseOrbit.Distance; }#region 处理相机的靠近拉远float mouseWheel = Input.GetAxis("Mouse ScrollWheel"); if (mouseWheel != 0 && !rotate)//按住滚轮键的靠近拉远 { Vector3 distance = MainCamera.transform.forward * mouseWheel * 10f; MainCamera.transform.position += distance; if (mouseWheel < 0) _mouseOrbit.Distance += distance.magnitude; else _mouseOrbit.Distance += -distance.magnitude; }if (Input.GetMouseButton(1) && rotate)//放大缩小模式//按住ALT加鼠标滑动的放大拉远 { float x = Input.GetAxis("Mouse X") * 250f * 0.02f; Vector3 distance = MainCamera.transform.forward * x * 0.05f; MainCamera.transform.position += distance; if (x < 0) _mouseOrbit.Distance += distance.magnitude; else _mouseOrbit.Distance += -distance.magnitude; }#endregionif (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(2)) { m_lastMousePosition = Input.mousePosition; if (m_rotate) { _mouseOrbit.enabled = true; } }if (isLocked) { if (m_pan && !m_rotate) { Pan(); } }} public void UnlockInput() { _lockInput = false; if (_mouseOrbit != null) { _point.position = MainCamera.transform.position + MainCamera.transform.forward * _mouseOrbit.Distance; _mouseOrbit.Target = _point; _mouseOrbit.SyncAngles(); } }private void Pan() { Vector3 delta = m_lastMousePosition - Input.mousePosition; delta = delta / Mathf.Sqrt(MainCamera.pixelHeight * MainCamera.pixelHeight + MainCamera.pixelWidth * MainCamera.pixelWidth); delta *= PanSensitivity; delta = MainCamera.cameraToWorldMatrix.MultiplyVector(delta); MainCamera.transform.position += delta; _point.position += delta; m_lastMousePosition = Input.mousePosition; } /// /// 处理插值事务 /// private void UpdateLerpHandle() {if (_isOpenLookAt) { Vector3 targetToCaemraVector3 = (MainCamera.transform.position - _point.position).normalized * _distance; Vector3 cameraDir = -MainCamera.transform.forward * _distance; Vector3 dir = Vector3.Lerp(targetToCaemraVector3, cameraDir, Time.deltaTime * 15f); MainCamera.transform.position = _point.position + dir; float value = https://www.it610.com/article/Mathf.Abs(Vector3.Dot(dir.normalized, targetToCaemraVector3.normalized)); if (Mathf.Abs(value - 1f) < 0.0000001f) { Debug.Log("关闭插值"); _isOpenLookAt = false; } } } private Bounds CalculateBounds(Transform t) { Renderer renderer = t.GetComponentInChildren(); if (renderer) { Bounds bounds = renderer.bounds; if (bounds.size == Vector3.zero && bounds.center != renderer.transform.position) { bounds = TransformBounds(renderer.transform.localToWorldMatrix, bounds); } CalculateBounds(t, ref bounds); if (bounds.extents == Vector3.zero) { bounds.extents = new Vector3(0.5f, 0.5f, 0.5f); } return bounds; }return new Bounds(t.position, new Vector3(0.5f, 0.5f, 0.5f)); } private void CalculateBounds(Transform t, ref Bounds totalBounds) { foreach (Transform child in t) { Renderer renderer = child.GetComponent(); if (renderer) { Bounds bounds = renderer.bounds; if (bounds.size == Vector3.zero && bounds.center != renderer.transform.position) { bounds = TransformBounds(renderer.transform.localToWorldMatrix, bounds); } totalBounds.Encapsulate(bounds.min); totalBounds.Encapsulate(bounds.max); }CalculateBounds(child, ref totalBounds); } } public static Bounds TransformBounds(Matrix4x4 matrix, Bounds bounds) { var center = matrix.MultiplyPoint(bounds.center); // transform the local extents' axes var extents = bounds.extents; var axisX = matrix.MultiplyVector(new Vector3(extents.x, 0, 0)); var axisY = matrix.MultiplyVector(new Vector3(0, extents.y, 0)); var axisZ = matrix.MultiplyVector(new Vector3(0, 0, extents.z)); // sum their absolute value to get the world extents extents.x = Mathf.Abs(axisX.x) + Mathf.Abs(axisY.x) + Mathf.Abs(axisZ.x); extents.y = Mathf.Abs(axisX.y) + Mathf.Abs(axisY.y) + Mathf.Abs(axisZ.y); extents.z = Mathf.Abs(axisX.z) + Mathf.Abs(axisY.z) + Mathf.Abs(axisZ.z); return new Bounds { center = center, extents = extents }; }protected void Focus() { _isOpenLookAt = true; Bounds bounds = CalculateBounds(Target); float fov = MainCamera.fieldOfView * Mathf.Deg2Rad; float objSize = Mathf.Max(bounds.extents.y, bounds.extents.x, bounds.extents.z) * 2.0f; _distance = Mathf.Abs(objSize / Mathf.Sin(fov / 2.0f)); _mouseOrbit.Distance = _distance; _point.position = bounds.center; }}


using UnityEngine; /// /// 绕目标旋转和向远处向近处看物体 /// public class LocalRotationAndScale : MonoBehaviour { private Camera m_camera; public Transform Target; public float Distance = 5.0f; public float XSpeed = 5.0f; public float YSpeed = 5.0f; public float YMinLimit = -360f; public float YMaxLimit = 360f; public float DistanceMin = .5f; public float DistanceMax = 5000f; private float m_x = 0.0f; private float m_y = 0.0f; private void Awake() { m_camera = GetComponent(); }private void Start() { SyncAngles(); }public void SyncAngles() { Vector3 angles = transform.eulerAngles; m_x = angles.y; m_y = angles.x; }private void LateUpdate() { float deltaX = Input.GetAxis("Mouse X"); float deltaY = Input.GetAxis("Mouse Y"); deltaX = deltaX * XSpeed; deltaY = deltaY * YSpeed; m_x += deltaX; m_y -= deltaY; m_y = ClampAngle(m_y, YMinLimit, YMaxLimit); Zoom(); }private void OnEnable() { RestRotationInfo(); }public void RestRotationInfo() { m_y = m_camera.transform.localEulerAngles.x; m_x = m_camera.transform.localEulerAngles.y; } public void Zoom() { Quaternion rotation = Quaternion.Euler(m_y, m_x, 0); transform.rotation = rotation; Vector3 negDistance = new Vector3(0.0f, 0.0f, -Distance); Vector3 position = rotation * negDistance + Target.position; transform.position = position; } public static float ClampAngle(float angle, float min, float max) { if (angle < -360F) { angle += 360F; } if (angle > 360F) { angle -= 360F; } return Mathf.Clamp(angle, min, max); } }



新建一个场景,按照我给的图片一步一步操作,运行,就可以看到效果了
第一步,首先用ugui建立一个Canvas,再建一个image,把这个image透明度设置为0,并且设置为全屏,就是把图片填满整个canvas。因为这个功能是建立在UGUI上的,只要是在imge上,就可以进行鼠标操作,其实也可以不用那么麻烦,看懂代码后,改几下就可以脱离ugui了。图片所示,就是这里限制在UGUI上,了解代码后,大家可以试试去掉。unity|模拟unity Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转
文章图片


场景布置如图所示场景挂载一个MyCameraManager脚本就可以了
unity|模拟unity Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转
文章图片


unity|模拟unity Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转
文章图片


【unity|模拟unity Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转】场景的两个示例cube
unity|模拟unity Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转
文章图片


大概就是这样子了。其实别看就两个类而已,牵扯到的知识还是蛮多的,也需要一定的功力。其中最大难度的就是Focus()方法了。
///
/// 相机参照点
///
private Transform _point;

相机的运动主要是参照了这个点,好好的理解这个点的运动,那就会很好理解整个流程了。

代码还是有点乱,有心人可以整理下,让其更加简单优雅
其他就是对向量的理解了,还有对3D空间的理解。相信如果你能理解这些知识后,对你的游戏开发的帮助是很大的。

    推荐阅读