模拟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;

        }


        #endregion


        if (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 = 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 Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转_第1张图片

 

场景布置如图所示  场景挂载一个MyCameraManager脚本就可以了

模拟unity Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转_第2张图片

 

模拟unity Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转_第3张图片

 

场景的两个示例cube

模拟unity Scene视图的鼠标功能,如旋转,放大缩小,移动,注视旋转_第4张图片

 

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

    ///


    /// 相机参照点
    ///

    private Transform _point;

 

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

 

代码还是有点乱,有心人可以整理下,让其更加简单优雅

其他就是对向量的理解了,还有对3D空间的理解。相信如果你能理解这些知识后,对你的游戏开发的帮助是很大的。

 

你可能感兴趣的:(unity,3d,C#,三维基础)