[Unity] Try to implement a third-person controller (the third, camera control)

[Unity] Try to implement a third-person controller (the third, camera control)


[Statement] This third-person controller is a reproduction of the third-person controller in the unity star asset. If necessary, you can directly download the asset and learn it. I personally don't like the behavior of reinventing the wheel, but the official controller has some unsatisfactory bugs when I use it. Still, the controller looks pretty good to me, and while seamless animation isn't possible, personal projects should figure out how to get it moving first.
[Version] This project is based on Unity [2021.3lts] version

camera control

make camera

We use the VirtualCamera of cinemachine.
Add this camera to your scene
insert image description here
with a simple setup. Because this plug-in is not in the introduction of this project, I will not explain this plug-in too much (I am not that familiar with it, there is a UP speaker at station B who is very good, and he also has a tutorial on using RootMotion to make seamless animations, you can Find it yourself).
I only do some simple demonstrations here to ensure that this project can be reproduced.
Set the newly created VM as shown in the figure below
insert image description here
and create a tracking point for the character at the same time, drag it into the Follow of the plugin in the figure above;

camera control

The direction of our VM is all controlled by the tracking point, you can try to rotate the tracking point after running, so the following mainly deals with the control of the tracking point

To achieve the next step, we must first control the camera

GameObject _mainCamera;

private void Awake()
{
    
    
		// get a reference to our main camera
		if (_mainCamera == null)
		{
    
    
				_mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
		}
}

First of all, if we rotate the track point, we should have corresponding mouse input. We want to determine whether it is a mouse input, if it is a mouse input, then we should use the displacement to determine the degree of rotation, if it is a joystick, then as long as the joystick deviates from the center point, it should generate a signal that keeps moving. We encapsulate the judgment into a bool value:

private bool IsCurrentDeviceMouse
{
    
    
		get {
    
     return _playerInput.currentControlScheme == "KeyboardMouse"; }
}

Then calculate the orientation of the tracked point

[Header("相机设置")]
float _cinemachineTagertX;
float _cinemachineTagertY;

if(_inputsMassage.look.sqrMagnitude>_threshold)//look值大于误差代表有输入
{
    
    
        float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1f : Time.deltaTime;

        _cinemachineTagertX += _inputsMassage.look.x * deltaTimeMultiplier;
        _cinemachineTagertY += _inputsMassage.look.y * deltaTimeMultiplier;
}

We now need to fix the rotation to ensure the limit distance that can be reached up, down, left, and right, otherwise there will be two problems:
(1). Universal lock
(2). Simply put, the world you see after lowering your waist is the world upside down ( If you want this effect, there is no limit)
we add the following function

private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
{
    
    
	if (lfAngle < -360f) lfAngle += 360f;
	if (lfAngle > 360f) lfAngle -= 360f;
	return Mathf.Clamp(lfAngle, lfMin, lfMax);
}

Next, restrict each direction (the official names here use yaw and pitch, which are the terms of the aircraft, we will not fix that)

[Tooltip("相机仰角")]
public float TopClamp = 70.0f;
[Tooltip("相机俯角")]
public float BottomClamp = -30.0f;
[Tooltip("额外的度数覆盖摄像头。有用的微调相机位置时,锁定")]
public float CameraAngleOverride = 0.0f;

_cinemachineTagertX = ClampAngle(_cinemachineTagertX, float.MinValue, float.MaxValue);
_cinemachineTagertY = ClampAngle(_cinemachineTagertY, BottomClamp, TopClamp);

We can then rotate the tracking point accordingly.

_cinemachineFollowTarget.transform.rotation= Quaternion.Euler(-_cinemachineTagertY 
						- CameraAngleOverride,_cinemachineTagertX, 0.0f);

We can call this function in LateYpdate At
this time, the moving speed is uncontrollable, we can just make a multiplier

epilogue

At this moment, we have implemented the control of the camera. Of course, it is still not related to the movement of our characters. This is a very simple thing. But I plan to put it in the next content.
Up to now, your screen should be able to control the camera, and the camera will not change with the moving direction of the character (although I am quite strange, but I have made such an effect), if you have any questions, please leave a message below, I will answer.

the code

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

public class ThirdPlayerMoveController : MonoBehaviour
{
    
    
    CharacterController _characterController;
    PlayerInput _playerInput;
    PlayerInputsMassage _inputsMassage;
    GameObject _mainCamera;

    [Header("相机设置")]
    public GameObject _cinemachineFollowTarget;
    float _cinemachineTagertX;
    float _cinemachineTagertY;
    [Tooltip("相机仰角")]
    public float TopClamp = 70.0f;
    [Tooltip("相机俯角")]
    public float BottomClamp = -30.0f;
    [Tooltip("额外的度数覆盖摄像头。有用的微调相机位置时,锁定")]
    public float CameraAngleOverride = 0.0f;

    [Header("玩家设置")]
    [Tooltip("这将决定普通行走时的速度")]
    public float walkSpeed = 1.5f;

    private float _currentSpeed;
    private float _targetRotation = 0.0f;
    [Tooltip("角色光滑旋转时间")]
    private float RotationSmoothTime = 0.12f;
    [Tooltip("在角色光滑旋转过程中的速度")]
    private float _rotationVelocity;

    private float _threshold = 0.01f;

    private bool IsCurrentDeviceMouse
    {
    
    
        get {
    
     return _playerInput.currentControlScheme == "KeyboardMouse"; }
    }

    private void Awake()
    {
    
    
        // get a reference to our main camera
        if (_mainCamera == null)
        {
    
    
            _mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
        }
    }

    // Start is called before the first frame update
    void Start()
    {
    
    
        _characterController = GetComponent<CharacterController>();
        _inputsMassage = GetComponent<PlayerInputsMassage>();
        _playerInput = GetComponent<PlayerInput>();
    }

    private void FixedUpdate()
    {
    
    
        Move();
    }

    private void LateUpdate()
    {
    
    
        CameraRotation();
    }

    private void CameraRotation()
    {
    
    
        if(_inputsMassage.look.sqrMagnitude>_threshold)//look值大于误差代表有输入
        {
    
    
            float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1f : Time.deltaTime;

            _cinemachineTagertX += _inputsMassage.look.x * deltaTimeMultiplier;
            _cinemachineTagertY += _inputsMassage.look.y * deltaTimeMultiplier;
        }
        _cinemachineTagertX = ClampAngle(_cinemachineTagertX, float.MinValue, float.MaxValue);
        _cinemachineTagertY = ClampAngle(_cinemachineTagertY, BottomClamp, TopClamp);

        _cinemachineFollowTarget.transform.rotation = Quaternion.Euler((-_cinemachineTagertY - CameraAngleOverride) * Settings.mouseYmoveTimes,
                _cinemachineTagertX * Settings.mouseXmoveTimes, 0.0f);
    }

    private void Move()
    {
    
    
        //首先将移动速度赋予临时变量,考虑到有可能在其他地方使用,我们将其存储起来
        _currentSpeed = walkSpeed;
        //判断是否进行移动输入
        if (_inputsMassage.move == Vector2.zero) _currentSpeed = 0;

        var currentInput = new Vector3(_inputsMassage.move.x, 0, _inputsMassage.move.y).normalized;

        if (_inputsMassage.move!=Vector2.zero)
        {
    
    
            _targetRotation = Mathf.Atan2(currentInput.x, currentInput.z) * Mathf.Rad2Deg;
            float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, _targetRotation, ref _rotationVelocity,
                RotationSmoothTime);
            transform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);
        }

        Vector3 targetDir = Quaternion.Euler(0.0f, _targetRotation, 0.0f) * Vector3.forward;

        _characterController.Move(targetDir.normalized * _currentSpeed * Time.deltaTime);
        //TODO:这里的Move可以执行垂直方向的速度,直接加上垂直的Vector就可以
    }

    private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
    {
    
    
        if (lfAngle < -360f) lfAngle += 360f;
        if (lfAngle > 360f) lfAngle -= 360f;
        return Mathf.Clamp(lfAngle, lfMin, lfMax);
    }
}

Guess you like

Origin blog.csdn.net/weixin_52540105/article/details/127705209