You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:12:09