Camera processing of unity -- third person camera

Posted by vaavi8r on Mon, 10 Jan 2022 23:35:28 +0100

The camera should have the functions of translating, following, rotating and zooming the field of view. Previously, the camera was placed at the lower node of the character to follow as a sub object of the character, but it was written dead:
After checking the Internet, I found that it is very convenient to use the camera to process according to the target:
First, there must be a targeted role:

/// <summary>
///Target role
/// </summary>
public Transform target;

You must also limit the elevation of the camera:

/// <summary>
///Minimum elevation
/// </summary>
//public float minAngle = 35;
/// <summary>
///Maximum elevation degree
/// </summary>
public float maxAngle = 85;
/// <summary>
///Initialize angle
/// </summary>
private float initAngle;

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

/// <summary>
///Distance between nearest camera and character
/// </summary>
public float minZ = 5f;
/// <summary>
///Farthest distance between camera and character
/// </summary>
public float maxZ = 20f;
/// <summary>
///Angle of shortest distance
/// </summary>
public float minZAngle = 10;
/// <summary>
///Current distance of camera
/// </summary>
public float CurCameraDis;

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

///Current angle
/// </summary>
public float CurCameraAngle;
public float CurCameraAngleH;

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

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

First, get the distance between the camera and the character:

private void ZoomCamera()
    {
        //The mobile terminal -- should be gesture zoom -- hasn't been written yet
        if (Application.platform == RuntimePlatform.Android ||
        Application.platform == RuntimePlatform.IPhonePlayer)
        {

        }
        else
        {
        	//The computer uses the built-in mouse as the zoom trigger
            if (Input.GetAxis("Mouse ScrollWheel") != 0)
            {
                zoomValue = Input.GetAxis("Mouse ScrollWheel");
                targDis = CurCameraDis - zoomValue * zoomSpeed;
            }

        }
        //Add a difference calculation to smooth scaling - from the current distance to the target distance
        CurCameraDis = Mathf.Lerp(CurCameraDis, targDis, Time.deltaTime * 10);
        //Limit scope
        CurCameraDis = Mathf.Clamp(CurCameraDis, minZ, maxZ);
    }

Then the vertical and horizontal offset values and angles are obtained according to the sliding screen:
Record the coordinates before and after the movement, and then subtract to obtain the corresponding x and y, corresponding 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;
            //vertical
            CurCameraAngle -= mousePosOffset.y * v_speed;
            //level
            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;
        //Use target position. y. The camera height is always based on the character's height, so that the character jumps and the camera will follow
        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);
        //Rebuild coordinate system
        Vector3 m_forwld = (camPosY - targPos2D).normalized;
        Vector3 m_up = target.up;
        m_right = Vector3.Cross(m_forwld, m_up);
        //First vector -- vertical angle: around m_right axis rotation angle
        Quaternion rot = Quaternion.AngleAxis(CurCameraAngle, m_right);
        Vector3 dir = camPosY - target.position;
        //Vector matrix times vector
        dir = rot * dir;
        //The second vector based on the first vector -- rotates around the character's up axis
        Quaternion rotH = Quaternion.AngleAxis(CurCameraAngleH, m_up);
        Vector3 dirH = rotH * dir;
       	//Start point + vector = end point: camera coordinates
        transform.position = target.position + dirH;
        
        //Debug.Log(rot + "---" + rotH);

    }

Finally, let the camera look at the character: there was a bug in the character you wanted to look at directly, and then calculate it yourself.
Then add the distance parameter of the camera: start point + vector * length = end point
transform.rotation = rota; Rota in calculates the lookat rotation

private void RotateCamera()
    {
        if (target == null) return;
        //lookat rotation
        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: the mobile terminal can be commented out first, because I haven't tested it on the mobile terminal, and I haven't found any problems on the computer terminal. That is, when the scene enters, initialize the camera and assign the target. You need to manually call InitCamera() to initialize the camera.

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

public class PlayerCamera : MonoBehaviour
{
    /// <summary>
    ///Target role
    /// </summary>
    public Transform target;
    /// <summary>
    ///Minimum elevation
    /// </summary>
    //public float minAngle = 35;
    /// <summary>
    ///Maximum elevation degree
    /// </summary>
    public float maxAngle = 85;
    /// <summary>
    ///Initialize angle
    /// </summary>
    private float initAngle;
    /// <summary>
    ///Distance between nearest camera and character
    /// </summary>
    public float minZ = 5f;
    /// <summary>
    ///Farthest distance between camera and character
    /// </summary>
    public float maxZ = 20f;
    /// <summary>
    ///Angle of shortest distance
    /// </summary>
    public float minZAngle = 10;
    /// <summary>
    ///Current distance of camera
    /// </summary>
    public float CurCameraDis;
    /// <summary>
    ///Current angle
    /// </summary>
    public float CurCameraAngle;
    public float CurCameraAngleH;
    /// <summary>
    ///Camera offset value
    /// </summary>
    public Vector2 mousePosOffset;
    public float initDic = 10;
    void Start()
    {
        
        InitCamera();

    }

    // Update is called once per frame
    void Update()
    {
        //Mobile terminal
        if (Application.platform == RuntimePlatform.Android ||
            Application.platform == RuntimePlatform.IPhonePlayer)
        {
            if (Input.touches[0].phase == TouchPhase.Began)
            {
                isDown = App.Instance.chickInRect(Input.touches[0].position);
            }
        }
        else
        {
            if (Input.GetMouseButtonDown(0))
            {
                isDown = App.Instance.chickInRect(Input.mousePosition);
            }
        }
        ZoomCamera();
        if (!isDown || Input.touches.Length >= 2) 
        {
            SwipeScreen();
        }
       
    }
    bool isDown = false;
    private void LateUpdate()
    {
       
        FollowPlayer();
        RotateCamera();
       
    }
    /// <summary> 
    ///Initialize camera
    /// </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;

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

        //Mobile terminal
        if (Application.platform == RuntimePlatform.Android ||
            Application.platform == RuntimePlatform.IPhonePlayer)
        {
            if (Input.touches.Length == 1 && Input.touches[0].phase == TouchPhase.Began||
                Input.touches.Length >= 2 && Input.touches[1].phase == TouchPhase.Began)
            {
                oldMousePos = Vector2.zero;
                isMousePress = true;
            }
            else if (Input.touches.Length == 1 && Input.touches[0].phase == TouchPhase.Ended||
                Input.touches.Length >= 2 && Input.touches[1].phase == TouchPhase.Ended)
            {
                mousePosOffset = Vector2.zero;
                isMousePress = false;
            }
            if (!isMousePress)
                return;
            if (Input.touches.Length == 1)
            {
                newMousePos = Input.touches[0].position;
            }
            else
            {
                newMousePos = Input.touches[1].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;
            //vertical
            CurCameraAngle -= mousePosOffset.y * v_speed;
            //level
            CurCameraAngleH = mousePosOffset.x* h_speed;
        }
        oldMousePos = newMousePos;
        CurCameraAngle = Mathf.Clamp(CurCameraAngle, minZAngle, maxAngle);
    }
    private float zoomValue;
    public float zoomSpeed = 20;
    float targDis;
    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);
    }
    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);
        //Rebuild coordinate system
        Vector3 m_forwld = (camPosY - targPos2D).normalized;
        Vector3 m_up = target.up;
        m_right = Vector3.Cross(m_forwld, m_up);
        //First root vector
        Quaternion rot = Quaternion.AngleAxis(CurCameraAngle, m_right);
        Vector3 dir = camPosY - target.position;
        dir = rot * dir;
        //Second root vector based on the first root vector
        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 rotation
        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);
        //Distance measurement
        //if (transform.position == Vector3.zero && CurCameraAngle == 90) 
        //{
        //    rota = Quaternion.LookRotation(-target.up, target.forward);
        //    transform.position = target.position + target.up * CurCameraDis;
        //}
        //else
        //{
        transform.position = target.position + (transform.position - target.position).normalized * CurCameraDis;
        //}

        transform.rotation = rota;
    }
}

Reference: camera perspective operation in making large MMO projects
Learn a little QAQ every day and give me a praise. I will be very happy

Topics: Unity