Unity6是傻逼
参考&插件
使用package:Cinemachine
参考教程:THIRD PERSON MOVEMENT in Unity 在油管上
B站链接:https://www.bilibili.com/video/BV1mp4y1S7jm/
摄像机类型:FreeLock Camera(Cinemachine内置的,装了package才能用,通过GameObject
-Cinemachine
-FreeLock Camera
添加,这样会自动给Main Camera
添加CinemachineBrain
脚本,让它能自动选择激活的Cinemachine Camera)
实操
第三人称摄像机部分
FreeLock Camera三个轨道设置,跟着视频做就完了,拉相机的轨道高度和半径,调整Y轴高度看Camera进行调试
跟着视频做,基本都是拖滑条啥的
踩坑
FreeLock Camera
的Status
在启动后一直是Standby
,Main Camera
自动选择了TopRig
摄像机(三个摄像机轨道之一),正确的应该是选择自己建立的FreeLock Camera
手动点击FreeLock Camera
的Status
solo按钮可以正常运行

修改脚本参数properties
,把优先级priority
改成100就好了

初版WASD移动逻辑
public class ThirdPersonMovement : MonoBehaviour
{
public CharacterController controller;
public float moveSpeed = 5f;
void Start()
{
controller = GetComponent<CharacterController>();
}
void Update()
{
float Horizontal = Input.GetAxisRaw("Horizontal");
float Vertical = Input.GetAxisRaw("Vertical");
Vector3 direction = new Vector3(Horizontal, 0, Vertical).normalized;// 归一化方向向量,确保移动方向的长度恒为 1,从而避免斜向移动速度比直线更快的问题。
if (direction.magnitude >= 0.1f)//如果方向向量的长度大于等于 0.1,表示玩家正在输入移动方向。不能使用direction.magnitude!=0,因为direction是浮点数,这样做在开发中不安全
{
controller.Move(direction * moveSpeed * Time.deltaTime);//使用角色控制器移动
}
}
}
计算方向角
初版不能实现随移动方向改变角色方向,需要引入方向角

if (direction.magnitude >= 0.1f)//如果方向向量的长度大于等于 0.1,表示玩家正在输入移动方向。不能使用direction.magnitude!=0,因为direction是浮点数,这样做在开发中不安全
{
float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;// 计算目标角度,使用反正切函数计算出方向向量的角度
transform.rotation = Quaternion.Euler(0, targetAngle, 0);//将角色的旋转设置为目标角度
controller.Move(direction * moveSpeed * Time.deltaTime);//使用角色控制器移动
}

添加转向平滑
现在主角可以根据移动方向改变头部朝向了,但角度改变是瞬时的,添加平滑
public float turnSmoothTime = 0.1f; // 旋转平滑时间 增加一个变量
void Update()
{
float Horizontal = Input.GetAxisRaw("Horizontal");
float Vertical = Input.GetAxisRaw("Vertical");
Vector3 direction = new Vector3(Horizontal, 0, Vertical).normalized;// 归一化方向向量,确保移动方向的长度恒为 1,从而避免斜向移动速度比直线更快的问题。
if (direction.magnitude >= 0.1f)// 如果方向向量的长度大于等于 0.1,表示玩家正在输入移动方向。不能使用direction.magnitude!=0,因为direction是浮点数,这样做在开发中不安全
{
float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;// 计算目标角度,使用反正切函数计算出方向向量的角度
float angle = Mathf.SmoothDampAngle(
transform.eulerAngles.y, // 当前角度
targetAngle, // 目标角度
ref turnSmoothVelocity, // 当前“速度”参考值(由方法内部更新)
turnSmoothTime // 平滑时间
);
transform.rotation = Quaternion.Euler(0, angle, 0);// 将角色的y轴旋转设置为目标角度
controller.Move(direction * moveSpeed * Time.deltaTime);// 使用角色控制器移动
}
}
ref turnSmoothVelocity
讲解
帧数 | 当前角度 | 目标角度 | turnSmoothVelocity | 说明 |
---|---|---|---|---|
第1帧 | 30° | 90° | 0.0 | 开始旋转 |
第2帧 | 45° | 90° | 15.0 | 正在加速转向 |
第3帧 | 65° | 90° | 10.0 | 慢慢减速 |
第4帧 | 80° | 90° | 5.0 | 快到目标 |
第5帧 | 90° | 90° | 0.0 | 到达目标,速度归零 |
相对摄像机方向改变移动方向
现在实现了平滑转动,但WASD的移动方向是以模型为基准的,摄像机转180°后操作会变成相反的,因此要加入摄像机方向。
public Transform cam;//引入摄像头

记得把Main Camera
拖进Cam
,不然运行会报错
拖成了·FreeLock Camera
会让后面获取的cam旋转角度一直为0
模型旋转部分
float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg + cam.eulerAngles.y;// 计算目标角度,使用反正切函数计算出方向向量的角度,再加上摄像头的y轴旋转角度,得到角色需要朝向的目标角度。
模型移动部分
Vector3 moveDir = Quaternion.Euler(0, targetAngle, 0) * Vector3.forward;// 计算移动方向,使用四元数旋转将目标角度转换为一个新的方向向量
controller.Move(moveDir.normalized * moveSpeed * Time.deltaTime);// 使用角色控制器移动
Quaternion.Euler(0, targetAngle, 0)
是四元数,是旋转量, 与Vector3.forward
相乘,转换为向量。
moveDir.normalized
同样归一化,确保移动方向是个单位向量
添加防穿模
在CinemachineFreeLock
脚本最下方找到Add Extension
添加Cinemachine Collider
添加对Ground
类型Layer的碰撞检测(提前给场景内的模型添加好Layer类型标记)
添加对Player
标签的过滤,否则摄像头会卡在Player
模型内部,同样提前给玩家模型添加Player
标记

装逼让你飞起来
添加跳跃
添加变量
// --- 2. 跳跃部分 ---
public float jumpHeight = 2f; // 跳跃高度
public float gravity = -9.81f; // 重力
private float verticalVelocity; // 垂直速度
Vector3 moveDir; // 移动方向
public float acceleration = 10f; // 平滑减速
在update()
中添加
// --- 2. 跳跃部分 ---
if (controller.isGrounded)
{
if (verticalVelocity < 0) verticalVelocity = -2f; // 稍微压住贴地,避免在地面上抖动
if (Input.GetButtonDown("Jump"))
{
verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * gravity); // 设置初始跳跃速度
}
}
else
{
verticalVelocity += gravity * Time.deltaTime; // 累加重力(加速度)
}
合并水平和竖直速度
试了试分开写两个脚本,会因为它们都在用 CharacterController.Move()
,但不会自动合并彼此的移动向量。导致移动的时候没法跳跃。为了避免以后开发变得麻烦,现在就合进去。
// --- 2. 跳跃部分 ---
if (controller.isGrounded)
{
if (verticalVelocity < 0) verticalVelocity = -2f; // 稍微压住贴地,避免在地面上抖动
if (Input.GetButtonDown("Jump"))
{
verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * gravity); // 设置初始跳跃速度
}
}
else
{
verticalVelocity += gravity * Time.deltaTime; // 累加重力(加速度)
}
// --- 3. 合并水平+垂直移动 ---
Vector3 finalMove = moveDir * moveSpeed;
finalMove.y = verticalVelocity;
controller.Move(finalMove * Time.deltaTime);
踩坑
为了最后合并,把水平移动部分的moveDir
从局部变量提到了类里,但导致moveDir
会保持上一帧的状态,按下按键就一直移动。
需要给if (direction.magnitude >= 0.1f)添加一个else逻辑,没有按下水平方向键时让水平速度快速衰减到0
else
{
// 缓慢停止
moveDir = Vector3.Lerp(moveDir, Vector3.zero, acceleration * Time.deltaTime);
}
moveDir
需要在创建的时候就归一化,而不是合并时归一化,因为衰减过程中moveDir
的长度不应该为1
moveDir = Quaternion.Euler(0, targetAngle, 0) * Vector3.forward;// 计算移动方向,使用四元数旋转将目标角度转换为一个新的方向向量
moveDir = moveDir.normalized; // 归一化移动方向向量,确保其长度恒为 1
controller.isGrounded地面检测失灵
当把主体换为了导入的猫娘模型后,出现了跳跃键失灵的问题,通过Debug.log(controller.isGrounded)
发现地面检测存在问题
把Character Controller
中Min Move Distance
从0.001改为0得到解决
完整代码
完整的ThirdPersonMovement.cs
using UnityEngine;
public class ThirdPersonMovement : MonoBehaviour
{
// --- 1. 水平部分 ---
public CharacterController controller;
public float moveSpeed = 5f;
public float turnSmoothTime = 0.05f; // 旋转平滑时间
float turnSmoothVelocity; // 旋转平滑速度
public Transform cam; // 引入摄像头
// --- 2. 跳跃部分 ---
public float jumpHeight = 2f; // 跳跃高度
public float gravity = -9.81f; // 重力
private float verticalVelocity; // 垂直速度
private Vector3 moveDir; // 移动方向
public float acceleration = 10f; // 平滑减速
void Awake()
{
controller = GetComponent<CharacterController>();
Cursor.lockState = CursorLockMode.Locked;// 默认锁定鼠标
Cursor.visible = false;// 默认光标隐藏
}
void Update()
{
// --- 1. 水平部分 ---
float Horizontal = Input.GetAxisRaw("Horizontal");
float Vertical = Input.GetAxisRaw("Vertical");
Vector3 direction = new Vector3(Horizontal, 0, Vertical).normalized;// 归一化方向向量,确保移动方向的长度恒为 1,从而避免斜向移动速度比直线更快的问题。
if (direction.magnitude >= 0.1f)// 如果方向向量的长度大于等于 0.1,表示玩家正在输入移动方向。不能使用direction.magnitude!=0,因为direction是浮点数,这样做在开发中不安全
{
float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg + cam.eulerAngles.y;// 计算目标角度,使用反正切函数计算出方向向量的角度,再加上摄像头的y轴旋转角度,得到角色需要朝向的目标角度。
float angle = Mathf.SmoothDampAngle(
transform.eulerAngles.y, // 当前角度
targetAngle, // 目标角度
ref turnSmoothVelocity, // 当前“速度”参考值(由方法内部更新)
turnSmoothTime // 平滑时间
);
transform.rotation = Quaternion.Euler(0, angle, 0);// 将角色的y轴旋转设置为目标角度
moveDir = Quaternion.Euler(0, targetAngle, 0) * Vector3.forward;// 计算移动方向,使用四元数旋转将目标角度转换为一个新的方向向量
moveDir = moveDir.normalized; // 归一化移动方向向量,确保其长度恒为 1
}
else
{
// 缓慢停止
moveDir = Vector3.Lerp(moveDir, Vector3.zero, acceleration * Time.deltaTime);
}
// --- 2. 跳跃部分 ---
if (controller.isGrounded)
{
if (verticalVelocity < 0) verticalVelocity = -2f; // 稍微压住贴地,避免在地面上抖动
if (Input.GetButtonDown("Jump"))
{
verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * gravity); // 设置初始跳跃速度
}
}
else
{
verticalVelocity += gravity * Time.deltaTime; // 累加重力(加速度)
}
// --- 3. 合并水平+垂直移动 ---
Vector3 finalMove = moveDir * moveSpeed;
finalMove.y = verticalVelocity;
controller.Move(finalMove * Time.deltaTime);
// ESC 切换鼠标锁定
if (Input.GetKeyDown(KeyCode.Escape))
{
Cursor.lockState = Cursor.lockState == CursorLockMode.Locked ? CursorLockMode.None : CursorLockMode.Locked;
Cursor.visible = !Cursor.visible;
}
}
}
模型导入

画的什么玩意,崩的要死(
答案是进入Material的Inspector面板,修改Outline Thickness参数,拉小点就行了

调整后

Comments NOTHING