[声明] このサードパーソン コントローラーは 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);
}
}