【声明】此第三人称控制器是复现unity明星资产中第三人称控制器,如果有需要可以直接下载该资产并学习。我个人是不喜欢重复造轮子的行为的,但是官方的控制器我使用时有些不尽如人意的BUG。尽管如此,该控制器的效果让我觉得很不错,虽然无法实现无缝动画,不过个人项目应该先想办法让其动起来才行。
【版本】此项目基于Unity【2021.3lts】版本制作
选择移动方式
目前主流的是两种:1.rig+collider.2.CharactorController
此处为了学习,并且考虑的自己做的类型并不需要过于频繁的物理模拟,选择了第二种。
使用CharactorController
【注意】此处是我的代码逐渐合理的思考过程,如果你想看代码直接拖到最后。
动起来
首先为角色挂载我们之前准备的Input部分,这在后边会使用。
然后创建MoveController,并将创建的脚本挂载在角色身上。
为角色挂载CharactorController
我们使用CharactorController的Move方法:
Vector3 targetDir = Vector3.forward;
_characterController.Move(targetDir);
回到游戏测试,角色向前运动。
控制方向
var _currentInput = new Vector3(_inputsMassage.move.x, 0, _inputsMassage.move.y);
Vector3 targetDir = currentInput;
_characterController.Move(targetDir * Time.deltaTime);
能够前后左右移动了,测试没问题,接下来需要思考一些东西了
思考角色行为
在任何时候,只要我们按下前,角色就应该向我们的前方行走。我们只需要提取出移动的大小即可
首先定义行走速度,然后再判断是否有输入:
public float walkSpeed = 1.5f;
private float _currentSpeed;
//首先将移动速度赋予临时变量,考虑到有可能在其他地方使用,我们将其存储起来
_currentSpeed = walkSpeed;
//判断是否进行移动输入
if (_inputsMassage.move == Vector2.zero) _currentSpeed = 0;
我们会将角色分为几个状态,我们首先处理行走。
_characterController.Move(targetDir * _currentSpeed * Time.deltaTime);
作为固定状态,其移动速度是固定的,所以我们认为输入的move是仅指导方向,大小是由状态来决定的。
我们将上方的_currentInput进行归一化操作,得到其单位向量(其实原本与单位向量是类似的,我们只是进行确认)
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;
}
那么上方的targeteDir就该与这个角度一致:
Vector3 targetDir = Quaternion.Euler(0.0f, _targetRotation, 0.0f) * Vector3.forward;
这里使用欧拉角将forward转过需要的角度。
我们测试发现,这些和我们之前的效果没有任何区别,为什么要做这么多没有意义的事情呢?
这是为了之后的Camera控制方向而准备的,当然目前代码里并没有该内容;
好的,至此我们做到的事情是什么呢,让角色的移动方向确定。但我们的角色并没有改变其方向。接下来处理这个问题。
我们通过Mathf.SmoothDampAngle来进行旋转
[Tooltip("角色光滑旋转时间")]
private float RotationSmoothTime = 0.12f;
[Tooltip("在角色光滑旋转过程中的速度")]
private float _rotationVelocity;
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);
}
至此,第一个移动部分该结束了。不要着急,移动还有十分多的问题没有解决,接下来会逐步去处理
代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class ThirdPlayerMoveController : MonoBehaviour
{
CharacterController _characterController;
PlayerInputsMassage _inputsMassage;
[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;
// Start is called before the first frame update
void Start()
{
_characterController = GetComponent<CharacterController>();
_inputsMassage = GetComponent<PlayerInputsMassage>();
}
private void FixedUpdate()
{
Move();
}
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就可以
}
}