完美解决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);
    }
}

 

posted @ 2021-08-08 00:14  【Winco】  阅读(700)  评论(0编辑  收藏  举报