unity的摄像机处理--第三人称摄像机

摄像机应该具有平移跟随,旋转,缩放视野的功能,之前使用的是将摄像机放置到角色下节点,作为角色的子物体跟随,但是这样就写死了:
网上查了一下,发现还是利用摄像机根据target进行处理还是挺方便的:
首先必须要有一个看向的目标角色:

/// <summary>
/// 目标角色
/// </summary>
public Transform target;

也必须限制相机的上下仰角角度:

/// <summary>
/// 最小仰角度
/// </summary>
//public float minAngle = 35;
/// <summary>
/// 最大仰角度
/// </summary>
public float maxAngle = 85;
/// <summary>
/// 初始化角度
/// </summary>
private float initAngle;

然后就是摄像机的视角缩放:也必须有一个范围:不能超过和接近0:
ps:请忽视minAngle ,这个最后还是用了minZAngle 作为最小角度,本来想做一个功能的,但最后还是没做

/// <summary>
/// 最近的摄像机与角色距离
/// </summary>
public float minZ = 5f;
/// <summary>
/// 最远的摄像机与角色距离
/// </summary>
public float maxZ = 20f;
/// <summary>
/// 最短距离的角度
/// </summary>
public float minZAngle = 10;
/// <summary>
/// 摄像机的当前距离
/// </summary>
public float CurCameraDis;

然后就是记录一下水平偏移值和当前的垂直角度:

/// 当前角度
/// </summary>
public float CurCameraAngle;
public float CurCameraAngleH;

首先初始化摄像机坐标:因为我是利用摄像机在角色后面为基准的

transform.position = target.position - target.forward;

首先得到摄像机距离角色的距离值:

private void ZoomCamera()
    {
        //手机端---应该是手势缩放--还没写
        if (Application.platform == RuntimePlatform.Android ||
        Application.platform == RuntimePlatform.IPhonePlayer)
        {

        }
        else
        {
        	//电脑端使用鼠标中建作为缩放触发
            if (Input.GetAxis("Mouse ScrollWheel") != 0)
            {
                zoomValue = Input.GetAxis("Mouse ScrollWheel");
                targDis = CurCameraDis - zoomValue * zoomSpeed;
            }

        }
        //添加一个差值计算,这样可以平滑缩放---从当前距离变成目标距离
        CurCameraDis = Mathf.Lerp(CurCameraDis, targDis, Time.deltaTime * 10);
        //限制范围
        CurCameraDis = Mathf.Clamp(CurCameraDis, minZ, maxZ);
    }

然后就是根据滑动屏幕得到垂直和水平的偏移值和角度:
记录移动前坐标和移动后坐标,然后进行相减,得到是相对应的x和y,对应了水平偏移值和垂直角度:最后进行限制范围(只有垂直范围需要限制)

private void SwipeScreen()
    {
       CurCameraAngleH = 0;
       if (Input.GetMouseButtonDown(0))
        {
            oldMousePos = Vector2.zero;
            isMousePress = true;
        }
        else if (Input.GetMouseButtonUp(0))
        {
            mousePosOffset = Vector2.zero;
            isMousePress = false;
        }
        if (!isMousePress)
            return;

        newMousePos = Input.mousePosition;
        
        if (oldMousePos != Vector2.zero)
        {
            mousePosOffset = newMousePos - oldMousePos;
            //垂直
            CurCameraAngle -= mousePosOffset.y * v_speed;
            //水平
            CurCameraAngleH = mousePosOffset.x* h_speed;
        }
        oldMousePos = newMousePos;
        CurCameraAngle = Mathf.Clamp(CurCameraAngle, minZAngle, maxAngle);
    }

有了数据就可以计算相机的坐标了:

 private void FollowPlayer()
    {
        if (target == null) return;
        //使用target.position.y,是将相机的高度一直以角色高度为基准,这样角色跳跃,相机也会跟随
        Vector3 camPosY = new Vector3(transform.position.x, target.position.y, transform.position.z);
        Vector3 targPos2D = new Vector3(target.position.x, target.position.y, target.position.z);
        //重建坐标系
        Vector3 m_forwld = (camPosY - targPos2D).normalized;
        Vector3 m_up = target.up;
        m_right = Vector3.Cross(m_forwld, m_up);
        //第一根向量--垂直角度:绕m_right轴旋转角度
        Quaternion rot = Quaternion.AngleAxis(CurCameraAngle, m_right);
        Vector3 dir = camPosY - target.position;
        //向量矩阵乘以向量
        dir = rot * dir;
        //基于第一根向量的第二根向量--绕角色的上方向轴进行旋转
        Quaternion rotH = Quaternion.AngleAxis(CurCameraAngleH, m_up);
        Vector3 dirH = rotH * dir;
       	//起始点+向量=终点:相机坐标
        transform.position = target.position + dirH;
        
        //Debug.Log(rot + "---" + rotH);

    }

最后就是让相机lookat角色:本来想直接lookat角色的,出了点bug,然后直接自己计算吧。
然后加上相机的距离参数:起始点+向量*长度=终点
transform.rotation = rota;中的rota计算的就是lookat旋转

private void RotateCamera()
    {
        if (target == null) return;
        //lookat旋转
        Vector3 m_forwld = (target.position - transform.position).normalized;
        Vector3 m_up = Vector3.Cross(m_forwld, m_right);
        //Debug.Log(target.position + "---------" + transform.position);
        Quaternion rota = Quaternion.LookRotation(m_forwld, m_up);
        Debug.DrawRay(transform.position, m_forwld * 10, Color.red);
        Debug.DrawRay(transform.position, m_up * 10, Color.green);

        transform.position = target.position + (transform.position - target.position).normalized * CurCameraDis;
        transform.rotation = rota;
    }

完整代码:手机端的可以先注释掉,因为我还没在手机端测试,电脑端没发现问题,就是在场景进入的时候,初始化摄像机赋值target,需要手动调用一下InitCamera()对摄像机进行初始化。
手机端处理:我是使用的触控点进行储存判断,最多两个触控点控制相机。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerCamera : MonoBehaviour
{
    /// <summary>
    /// 目标角色
    /// </summary>
    public Transform target;
    /// <summary>
    /// 最小仰角度
    /// </summary>
    //public float minAngle = 35;
    /// <summary>
    /// 最大仰角度
    /// </summary>
    public float maxAngle = 85;
    /// <summary>
    /// 初始化角度
    /// </summary>
    private float initAngle;
    /// <summary>
    /// 最近的摄像机与角色距离
    /// </summary>
    public float minZ = 5f;
    /// <summary>
    /// 最远的摄像机与角色距离
    /// </summary>
    public float maxZ = 20f;
    /// <summary>
    /// 最短距离的角度
    /// </summary>
    public float minZAngle = 10;
    /// <summary>
    /// 摄像机的当前距离
    /// </summary>
    public float CurCameraDis;
    /// <summary>
    /// 当前角度
    /// </summary>
    public float CurCameraAngle;
    public float CurCameraAngleH;
    /// <summary>
    /// 相机的偏移值
    /// </summary>
    public Vector2 mousePosOffset;
    public float initDic = 10;
    void Start()
    {
        
        InitCamera();

    }

    // Update is called once per frame
    void Update()
    {
        //手机端
        if (Application.platform == RuntimePlatform.Android ||
            Application.platform == RuntimePlatform.IPhonePlayer)
        {
        	//获取除了特殊点之外的触控点,最多两个,如果是1个就是调整相机的上下左右视角范围
        	//如果是两个,则是调整相机缩放z值,冻结相机旋转和移动
            touchMap = CameraMode.Instance.getContrlCamToch();
            if (touchMap.Count == 1)
            {
                isDown = false;
            }
        }
        else
        {
            if (Input.GetMouseButtonDown(0))
            {
            	//这个检测是否在遥感范围内的触控,直接可以改为isDown =false
                isDown = App.Instance.chickInRect(Input.mousePosition);
            }
        }
        ZoomCamera();
        if (!isDown /*|| Input.touches.Length >= 2*/) 
        {
            SwipeScreen();
        }
       
    }
    bool isDown = false;
    private void LateUpdate()
    {
       
        FollowPlayer();
        RotateCamera();
       
    }
    /// <summary> 
    /// 初始化摄像机
    /// </summary>
    public void InitCamera() 
    {  
        if (target == null) return;
        initAngle = 40;
        CurCameraAngle = initAngle;
        mousePosOffset = Vector2.zero;
        transform.position = target.position - target.forward;
        targDis = CurCameraDis;
        isDown = false;
        isEnd = false;
        touchDis = 0;

    }
    bool isMousePress = false;
    Vector2 oldMousePos;
    Vector2 newMousePos;
    public float v_speed = 0.2f;
    public float h_speed = 0.1f;
    private void SwipeScreen()
    {
        CurCameraAngleH = 0;

        //手机端
        if (Application.platform == RuntimePlatform.Android ||
            Application.platform == RuntimePlatform.IPhonePlayer)
        {
            if (touchMap.Count == 1 && touchMap[0].phase == TouchPhase.Began) 
            {
                isEnd = false;
                oldMousePos = Vector2.zero;
                isMousePress = true;
            }
            else if (touchMap.Count == 1 && touchMap[0].phase == TouchPhase.Ended)
            {
                mousePosOffset = Vector2.zero;
                isMousePress = false;
            }
            if (!isMousePress)
                return;
            if (touchMap.Count == 1 && isEnd == false) 
            {
                newMousePos = touchMap[0].position;
            }
        }
        else
        {
            if (Input.GetMouseButtonDown(0))
            {
                oldMousePos = Vector2.zero;
                isMousePress = true;
            }
            else if (Input.GetMouseButtonUp(0))
            {
                mousePosOffset = Vector2.zero;
                isMousePress = false;
            }
            if (!isMousePress)
                return;

            newMousePos = Input.mousePosition;
        }
        if (oldMousePos != Vector2.zero)
        {
            mousePosOffset = newMousePos - oldMousePos;
            //垂直
            CurCameraAngle -= mousePosOffset.y * v_speed;
            //水平
            CurCameraAngleH = mousePosOffset.x* h_speed;
        }
        oldMousePos = newMousePos;
        CurCameraAngle = Mathf.Clamp(CurCameraAngle, minZAngle, maxAngle);
    }
    private float zoomValue;
    public float zoomSpeed = 20;
    float targDis;
    private List<Touch> touchMap;
    private float touchDis;
    private bool isEnd;
    private void ZoomCamera()
    {
        
        if (Application.platform == RuntimePlatform.Android ||
        Application.platform == RuntimePlatform.IPhonePlayer)
        {
            zoomSpeed = 1;
            //touchMap = CameraMode.Instance.getContrlCamToch();
            if (touchMap.Count >= 2)
            {
                Vector2 pos1 = touchMap[0].position;
                Vector2 pos2 = touchMap[1].position;
                if (touchDis == 0)  
                {
                    touchDis = Vector3.Distance(pos1, pos2);
                }
                else
                {
                    targDis = CurCameraDis - (Vector2.Distance(pos1, pos2) - touchDis) * zoomSpeed;
                    touchDis = Vector3.Distance(pos1, pos2);
                }
            }
            for (int k = 0; k < touchMap.Count; k++)
            {
                //如果又触控点离开屏幕
                if (touchMap[k].phase == TouchPhase.Ended)
                {
                    touchDis = 0;
                    isEnd = true;
                }
            }
        }
        else
        {
            zoomSpeed = 20;
            if (Input.GetAxis("Mouse ScrollWheel") != 0)
            {
                zoomValue = Input.GetAxis("Mouse ScrollWheel");
                targDis = CurCameraDis - zoomValue * zoomSpeed;
            }
        }
        CurCameraDis = Mathf.Lerp(CurCameraDis, targDis, Time.deltaTime * 10);
        CurCameraDis = Mathf.Clamp(CurCameraDis, minZ, maxZ);
    }
    Vector3 m_right;
    public float v_sa;
    private void FollowPlayer()
    {
        if (target == null) return;
        Vector3 camPosY = new Vector3(transform.position.x, target.position.y, transform.position.z);
        Vector3 targPos2D = new Vector3(target.position.x, target.position.y, target.position.z);
        //重建坐标系
        Vector3 m_forwld = (camPosY - targPos2D).normalized;
        Vector3 m_up = target.up;
        m_right = Vector3.Cross(m_forwld, m_up);
        //第一根向量
        Quaternion rot = Quaternion.AngleAxis(CurCameraAngle, m_right);
        Vector3 dir = camPosY - target.position;
        dir = rot * dir;
        //基于第一根向量的第二根向量
        Quaternion rotH = Quaternion.AngleAxis(CurCameraAngleH, m_up);
        Vector3 dirH = rotH * dir;
       
        transform.position = target.position + dirH;
        
        //Debug.Log(rot + "---" + rotH);

    }
    private void RotateCamera()
    {
        if (target == null) return;
        //lookat旋转
        Vector3 m_forwld = (target.position - transform.position).normalized;
        Vector3 m_up = Vector3.Cross(m_forwld, m_right);
        //Debug.Log(target.position + "---------" + transform.position);
        Quaternion rota = Quaternion.LookRotation(m_forwld, m_up);
        Debug.DrawRay(transform.position, m_forwld * 10, Color.red);
        Debug.DrawRay(transform.position, m_up * 10, Color.green);

        transform.rotation = rota;
    }
    
}

在这里插入图片描述

参考:制作大型MMO项目中的相机视角操作
每天学一点QAQ,给我一个赞我会很开心哒

猜你喜欢

转载自blog.csdn.net/QO_GQ/article/details/119177767
今日推荐