zoukankan      html  css  js  c++  java
  • 完美解决Unity中拖拽相机视图的跟手问题

    在转载的上一篇博客中,可以看到拖拽相机跟手已经非常完美。但是最近策划同学又提出在相机跟手后,地图地面相对于相机的高度不变。在转载的博客中,因为是根据相机与地图世界坐标的距离计算实现的相机视图跟手,在使用透视相机(近大远小)下不可避免的会出现地图也在上下移动。要实现这个需求有两种方式(附带完整代码):

    1、在移动过程中,每帧都通过射线获取当前屏幕点击点对应的地图的世界坐标,然后在根据地图的世界坐标在保持相机高度不变的情况下,反向计算出相机的位置。每帧通过射线计算,消耗比较大。(不推荐)

    using UnityEngine;
    
    [RequireComponent(typeof(Camera))]
    //按住鼠标右键旋转,同时按wasd移动,按住shift加速移动,按住中键拖拽视图
    public class FreeCamera : MonoBehaviour
    {
    
        //相机旋转速度
        public float rotateSpeed = 5f;
        //相机缩放速度
        public float scaleSpeed = 10f;
    
        //旋转变量
        private float m_deltX = 0f;
        private float m_deltY = 0f;
    
        //移动变量
        float m_camNormalMoveSpeed = 0.2f;
        float m_camFastMoveSpeed = 2f;
        private Vector3 m_mouseMoveBegin = Vector3.zero;
        private Vector3 m_targetPos;
        Camera m_cam;
        float m_distance;
        float m_camHitDistance = 10;
        Quaternion m_camBeginRotation;
    
        void Start()
        {
            m_cam = GetComponent<Camera>();
            m_camBeginRotation = m_cam.transform.rotation;
        }
    
        void Update()
        {
    
            //if (Input.GetMouseButton(1))
            //{
            //    //鼠标右键点下控制相机旋转;
            //    m_deltX += Input.GetAxis("Mouse X") * rotateSpeed;
            //    m_deltY -= Input.GetAxis("Mouse Y") * rotateSpeed;
            //    m_deltX = ClampAngle(m_deltX, -360, 360);
            //    m_deltY = ClampAngle(m_deltY, -70, 70);
            //    m_cam.transform.rotation = m_camBeginRotation * Quaternion.Euler(m_deltY, m_deltX, 0);
    
            //    //鼠标右键按住时控制相机移动
            //    float _inputX = Input.GetAxis("Horizontal");
            //    float _inputY = Input.GetAxis("Vertical");
            //    float _camMoveSpeed = Input.GetKey(KeyCode.LeftShift) ? m_camFastMoveSpeed : m_camNormalMoveSpeed;
            //    m_targetPos = transform.position + transform.forward * _camMoveSpeed * _inputY + transform.right * _camMoveSpeed * _inputX;
            //    transform.position = Vector3.Lerp(transform.position, m_targetPos, 0.5f);
    
            //}
    
            ////鼠标中键点下场景缩放
            //if (Input.GetAxis("Mouse ScrollWheel") != 0)
            //{
            //    m_distance = Input.GetAxis("Mouse ScrollWheel") * scaleSpeed;
            //    m_targetPos = m_cam.transform.position + m_cam.transform.forward * m_distance;
            //    m_cam.transform.position = Vector3.Lerp(m_cam.transform.position, m_targetPos, 0.5f);
            //}
    
            //鼠标拖拽视野
            if (Input.GetMouseButtonDown(1))
            {
                //跟手拖拽的关键
                Ray ray = m_cam.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                if (Physics.Raycast(ray, out hit, Mathf.Infinity))
                {
                    Vector3 vec_cam2hitPoint = hit.point - transform.position;
                    this.m_camHitDistance = Vector3.Dot(vec_cam2hitPoint, transform.forward);
                }
                m_mouseMoveBegin = m_cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, m_camHitDistance));
            }
            else if (Input.GetMouseButton(1))
            {
                //跟手拖拽的关键
                Ray ray = m_cam.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                if (Physics.Raycast(ray, out hit, Mathf.Infinity))
                {
                    Vector3 vec_cam2hitPoint = hit.point - transform.position;
                    this.m_camHitDistance = Vector3.Dot(vec_cam2hitPoint, transform.forward);
                }
                var movePos = m_cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, m_camHitDistance));
                var offset = movePos - m_mouseMoveBegin;
    
                // 计算相机的移动方向 相反
                m_cam.transform.position = m_cam.transform.position - offset;
            }
        }
    
        float ClampAngle(float angle, float minAngle, float maxAgnle)
        {
            if (angle <= -360)
                angle += 360;
            if (angle >= 360)
                angle -= 360;
    
            return Mathf.Clamp(angle, minAngle, maxAgnle);
        }
    }

    2、通过向量的计算,求出当前屏幕点击对应的地图世界坐标,然后根据向量计算的坐标反推出相机的坐标。(推荐使用)

    using UnityEngine;
    
    [RequireComponent(typeof(Camera))]
    //按住鼠标右键旋转,同时按wasd移动,按住shift加速移动,按住中键拖拽视图
    public class FreeCamera : MonoBehaviour
    {
    
        //相机旋转速度
        public float rotateSpeed = 5f;
        //相机缩放速度
        public float scaleSpeed = 10f;
    
        //旋转变量
        private float m_deltX = 0f;
        private float m_deltY = 0f;
    
        //移动变量
        float m_camNormalMoveSpeed = 0.2f;
        float m_camFastMoveSpeed = 2f;
        private Vector3 m_mouseMoveBegin = Vector3.zero;
        private Vector3 m_targetPos;
        Camera m_cam;
        float m_distance;
        float m_camHitDistance = 10;
        Quaternion m_camBeginRotation;
    
        void Start()
        {
            m_cam = GetComponent<Camera>();
            m_camBeginRotation = m_cam.transform.rotation;
        }
    
        void Update()
        {
            //鼠标拖拽视野
            if (Input.GetMouseButtonDown(1))
            {
                //跟手拖拽的关键
                var screenPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 50);
                m_mouseMoveBegin = ScreenToWorldPoint(screenPos, 0.0f);
            }
            else if (Input.GetMouseButton(1))
            {
                var screenPOs = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 50);
                var movePos = ScreenToWorldPoint(screenPOs, 0.0f);
                //相机的移动方向相反
                var offset = m_mouseMoveBegin - movePos;
                offset.y = 0;
    
                m_cam.transform.position += offset;
            }
        }
    
        public Vector3 ScreenToWorldPoint(Vector3 screenPos, float yPlane)
        {
            var position = transform.position;
            var point = m_cam.ScreenToWorldPoint(screenPos);
            var forward = (point - position).normalized;
    
            float scalar = 0.0f;
            if (Mathf.Abs(forward.y) < float.Epsilon)
                scalar = 1.0f;
            else
                scalar = (yPlane - position.y) / forward.y;
    
            var target = new Vector3(position.x + forward.x * scalar, yPlane, position.z + forward.z * scalar);
            return target;
        }
    
        //根据地图的世界坐标反向推算出当前相机的位置 (保持地图和相机高度不变的情况下)
        private Vector3 FoceOn(Vector3 position)
        {
            var forward = transform.forward;
            var fixedY = transform.position.y;
            var rate = (fixedY - position.y) / forward.y;
    
            var target = new Vector3(rate * forward.x + position.x, fixedY, rate * forward.z + position.z);
            return target;
        }
    
        float ClampAngle(float angle, float minAngle, float maxAgnle)
        {
            if (angle <= -360)
                angle += 360;
            if (angle >= 360)
                angle -= 360;
    
            return Mathf.Clamp(angle, minAngle, maxAgnle);
        }
    }
  • 相关阅读:
    ansible-乱
    linux-PXE-12
    linux-ntp-10
    linux-selinxu---性能 -8
    linux-系统启动流程-7
    linux-网络管理-6
    linux-文件系统-5
    linux-包管理器-4
    linux-shell脚本基础-2
    linux-history-ps1-1
  • 原文地址:https://www.cnblogs.com/atong/p/15113821.html
Copyright © 2011-2022 走看看