Unity's camera processing--third-person camera

The camera should have the functions of panning and following, rotating, and zooming the field of view. Previously, the camera was placed under the character's node and followed as a sub-object of the character, but this was a dead end: I checked online and found that the camera is still used according to the target
. It is quite convenient to process:
first, you must have a target character to look at:

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

The camera's up and down elevation angles must also be limited:

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

Then there is the camera's angle of view scaling: it must also have a range: it cannot exceed or be close to 0: ps
: Please ignore minAngle. In the end, minZAngle was used as the minimum angle. I originally wanted to make it a function, but in the end I didn't do it.

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

Then just record the horizontal offset value and the current vertical angle:

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

First initialize the camera coordinates: because I use the camera behind the character as a reference

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

First get the distance value of the camera from the character:

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);
    }

Then we get the vertical and horizontal offset values ​​and angles based on the sliding screen:
record the coordinates before and after the movement, and then subtract them to get the corresponding x and y, which correspond to the horizontal offset value and vertical angle: Finally Limit the range (only the vertical range needs to be limited)

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);
    }

With the data, you can calculate the coordinates of the camera:

 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);

    }

The last step is to let the camera look at the character: I originally wanted to look at the character directly, but there was a bug, so I just calculated it myself.
Then add the distance parameter of the camera: starting point + vector * length = end point
transform.rotation = rota; the rota in it calculates the lookat rotation.

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;
    }

Complete code: You can comment out the mobile version first, because I haven’t tested it on the mobile phone yet, and I haven’t found any problems on the computer side. When the scene is entered, the camera assignment target is initialized, and you need to manually call InitCamera() to initialize the camera.
Mobile phone processing: I use touch points for storage judgment, and up to two touch points control the camera.

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;
    }
    
}

Insert image description here

Reference: Camera perspective operation in making large-scale MMO projects.
Learn a little QAQ every day. I will be very happy if you give me a like.

Guess you like

Origin blog.csdn.net/QO_GQ/article/details/119177767