Unity技术问询:如何配置Character Controller实现玩家固定步长移动
在Unity中用Character Controller实现固定步长移动
嘿,这个需求我之前做格子类解谜游戏的时候刚好碰到过,用Character Controller实现固定步长移动其实没那么复杂,核心就是把原本的连续帧移动改成按固定单位分段触发的移动逻辑。下面给你一步步拆解具体实现方式:
核心思路
Character Controller本身是为连续移动设计的(比如第三人称漫游),所以我们要做的是:
- 捕获玩家的方向输入,但不是每一帧都响应,而是触发一次输入就移动固定距离
- 移动过程中锁定输入,避免按住方向键时连续移动,确保每一次操作只走“一步”
- 用目标点来控制移动,让角色逐步走到固定步长的终点位置
具体实现步骤
1. 基础设置
先确保你的玩家对象上已经挂载了Character Controller组件,调整好Radius、Height这些基础参数(和普通移动的设置一样就行)。
2. 代码实现(C#)
直接上可复用的代码,关键部分我都加了注释:
using UnityEngine; public class StepBasedMovement : MonoBehaviour { [SerializeField] private CharacterController playerController; [SerializeField] private float stepLength = 1f; // 这里设置你的固定步长,0.1或1都可以 [SerializeField] private float moveSpeed = 2.5f; // 控制每一步移动的快慢,数值越大走得越快 private Vector3 targetMovePosition; private bool isPlayerMoving = false; void Start() { // 初始化目标位置为角色当前位置 targetMovePosition = transform.position; } void Update() { // 只有当角色不在移动状态时,才响应新的输入 if (!isPlayerMoving) { // 获取WASD的输入,转换为世界空间的方向向量 Vector3 inputDirection = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized; // 过滤掉无效输入(比如误触的微小输入) if (inputDirection.magnitude > 0.1f) { // 计算本次移动的目标位置:当前位置 + 输入方向 × 固定步长 targetMovePosition = transform.position + inputDirection * stepLength; // 标记角色进入移动状态,锁定新输入 isPlayerMoving = true; } } else { // 计算当前位置到目标位置的剩余距离 Vector3 remainingMoveDir = targetMovePosition - transform.position; // 如果剩余距离足够小,说明已经走到目标点 if (remainingMoveDir.magnitude < 0.01f) { // 修正位置到精确的目标点,避免累积误差 transform.position = targetMovePosition; // 解除移动锁定,允许响应新输入 isPlayerMoving = false; } else { // 用CharacterController的Move方法逐步移动,记得乘Time.deltaTime保证帧率无关 playerController.Move(remainingMoveDir.normalized * moveSpeed * Time.deltaTime); } } } }
3. 关键细节说明
- 步长调整:直接修改
stepLength的值就行,比如设为0.1就是每次移动0.1单位,设为1就是每次移动1单位 - 移动速度:
moveSpeed控制的是每一步的移动快慢,不是步长大小——数值越大,角色完成一步移动的时间越短 - 输入锁定:
isPlayerMoving变量用来防止玩家按住方向键时连续触发移动,确保每一次按键只走一步 - 斜向移动:代码里已经处理了斜向输入(比如同时按W+D),因为
inputDirection是归一化的,所以斜向移动的步长依然是你设置的固定值,不会变长
4. 碰撞与边界处理注意事项
Character Controller自带碰撞检测,但如果移动路径上有障碍物,角色会被挡住,这时候可能出现“没走到目标点就卡住”的情况,你可以加个小优化:
// 在移动逻辑里替换原有的Move调用,增加碰撞判断 CollisionFlags collisionFlags = playerController.Move(remainingMoveDir.normalized * moveSpeed * Time.deltaTime); // 如果碰到了障碍物(比如墙面、非地面碰撞),就取消这次移动 if (collisionFlags != CollisionFlags.None && collisionFlags != CollisionFlags.CollidedBelow) { targetMovePosition = transform.position; isPlayerMoving = false; }
另外,也可以在触发移动前,用Physics.CheckSphere提前检测目标位置是否有障碍物,避免无效的移动触发。
5. 可选优化
- 加入动画:在
isPlayerMoving为true时播放移动动画,为false时切换回 idle 动画 - 支持冲刺:可以加个Shift键触发的冲刺逻辑,临时增大
stepLength或moveSpeed - 网格对齐:如果是格子游戏,可以把
targetMovePosition的坐标取整,确保角色每次都移动到格子中心
内容的提问来源于stack exchange,提问作者stighy
相关产品推荐
相关产品推荐

