【Unity】サードパーソンコントローラーを実装してみる(サード、カメラコントロール)

【Unity】サードパーソンコントローラーを実装してみる(サード、カメラコントロール)


[声明] このサードパーソン コントローラーは unity star アセットのサードパーソン コントローラーを複製したもので、必要に応じてアセットを直接ダウンロードして学習することができます。個人的には車輪の再発明の振る舞いが好きではありませんが、公式のコントローラーは私が使用すると満足できないバグがいくつかあります。それでも、コントローラーは私にはかなり良さそうに見えます。シームレスなアニメーションは不可能ですが、個人的なプロジェクトでは、最初にそれを動かす方法を見つけ出す必要があります。
[バージョン] このプロジェクトはUnity [2021.3lts]バージョンに基づいています

カメラ制御

カメラを作る

シネママシンのVirtualCameraを使用しています。簡単なセットアップで
このカメラをシーンに追加します。
ここに画像の説明を挿入
このプラグインは、このプロジェクトの紹介にないので、あまり説明しません (私はあまり詳しくありません。ステーション B に非常に優れた UP スピーカーがあり、彼はまた、 RootMotion を使用してシームレスなアニメーションを作成するためのチュートリアルです。自分で見つけることができます)。
ここでは、このプロジェクトを確実に再現できるようにするために、いくつかの簡単なデモのみを行います。
新しく作成した VM を下図のように設定し
ここに画像の説明を挿入
、同時にキャラクターのトラッキング ポイントを作成し、上図のプラグインのフォローにドラッグします。

カメラ制御

VM の方向はすべてトラッキング ポイントによって制御されます。実行後にトラッキング ポイントを回転させることができます。以下では、主にトラッキング ポイントの制御について説明します。

次のステップを達成するには、まずカメラを制御する必要があります

GameObject _mainCamera;

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

まず、トラック ポイントを回転させると、対応するマウス入力が必要になります。マウス入力かどうかを判断したいのですが、マウス入力の場合は、変位を使用して回転の度合いを判断する必要があります。ジョイスティックの場合は、ジョイスティックが中心点からずれている限り、動き続ける信号を生成する必要があります。判定を bool 値にカプセル化します。

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

次に、追跡されたポイントの向きを計算します

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

ここで回転を修正して、上下左右に到達できる限界距離を確保する必要があります。そうしないと、
(1) ユニバーサル ロック
(2) 簡単に言えば、その後に表示される世界が2 つ問題になります。腰を下げることは世界をひっくり返すことです (この効果が必要な場合は制限はありません)
次の機能を追加します

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

次に、各方向を制限します (ここでの正式名称は、航空機の用語であるヨーとピッチを使用します。これは修正しません)。

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

それに応じてトラッキング ポイントを回転させることができます。

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

この関数は LateYpdate で呼び出すことができます
現時点では、移動速度は制御できません。乗数を作成するだけです

エピローグ

この時点で、カメラの制御が実装されました。もちろん、キャラクターの動きとは関係ありませんが、これは非常に単純なことです。でも次のコンテンツに入れる予定です。
これまでのところ、画面はカメラを制御できるはずで、キャラクターの移動方向に合わせてカメラが変化することはありません (私はかなり奇妙ですが、そのような効果を作成しました)。以下にメッセージを残してください。返信します。

コード

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

おすすめ

転載: blog.csdn.net/weixin_52540105/article/details/127705209