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

Unity Character Controller组件跳跃与移动代码协同实现方法

问题根源

当前代码无法正常运行的核心问题有3个:

  • 一帧内重复调用characterController.Move():JumpingCode()和MovementCode()各自独立执行位移,两次位移直接叠加,且JumpingCode()里的水平位移没有做相机对齐、转向平滑处理,会和MovementCode()的正确水平逻辑冲突,导致移动卡顿、跳跃时方向失控。
  • 速度归一化逻辑错误:JumpingCode()中将水平输入值和Y轴速度拼接后直接归一化,会导致水平、垂直速度互相挤压——比如起跳瞬间Y轴速度很大时,水平移动速度会被压缩到几乎为0,下落时同理,移动速度完全不符合预期。
  • 缺少接地重力重置逻辑:CharacterController不会在落地时自动重置Y轴速度,重力会持续累加,导致起跳判定异常、落地后卡顿弹跳。
修复方案

不要让拆分的方法各自执行Move(),逻辑拆分应该遵循「分量独立计算、最后统一合并位移」的原则:水平逻辑只负责计算对齐相机的X/Z轴位移、角色转向;跳跃重力逻辑只负责计算Y轴速度;最后一帧仅调用一次Move()执行合并后的总位移。

修正后的完整代码参考:

// 提前声明的参数变量,和你原有配置保持一致即可
public CharacterController characterController;
public Transform playerCamera;
public float walkSpeed = 5f;
public float jumpHeight = 2f;
public float gravity = -9.81f;
public float gravityScale = 1f;
public float turnSmoothTime = 0.1f;

// 内部缓存变量
private float horizontalInput;
private float verticalInput;
private bool jumpPressed;
private float velocityY;
private float turnSmoothVelocity;

void Update()
{
    GetInput();
    Vector3 horizontalMove = CalculateHorizontalMovement();
    CalculateVerticalVelocity();
    // 合并水平、垂直位移,一帧仅调用一次Move
    Vector3 finalMove = horizontalMove + Vector3.up * velocityY;
    characterController.Move(finalMove * Time.deltaTime);
}

// 获取输入逻辑保持原有实现即可
void GetInput()
{
    horizontalInput = Input.GetAxisRaw("Horizontal");
    verticalInput = Input.GetAxisRaw("Vertical");
    jumpPressed = Input.GetButtonDown("Jump");
}

// 只负责计算水平方向位移、处理角色转向,不执行Move
Vector3 CalculateHorizontalMovement()
{
    Vector3 direction = new Vector3(horizontalInput, 0f, verticalInput).normalized;
    Vector3 moveDir = Vector3.zero;
    if (direction.magnitude > 0.1f)
    {
        float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg + playerCamera.eulerAngles.y;
        float angle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnSmoothVelocity, turnSmoothTime);
        transform.rotation = Quaternion.Euler(0f, angle, 0f);
        moveDir = Quaternion.Euler(0f, targetAngle, 0f) * Vector3.forward * walkSpeed;
    }
    return moveDir;
}

// 只负责计算跳跃、重力带来的Y轴速度,不执行Move
void CalculateVerticalVelocity()
{
    // 落地时重置Y轴速度,给一个微小负速度保证isGrounded检测稳定
    if (characterController.isGrounded && velocityY < 0)
    {
        velocityY = -2f;
    }

    // 起跳判定
    if (jumpPressed && characterController.isGrounded)
    {
        velocityY = Mathf.Sqrt(jumpHeight * -2f * gravity * gravityScale);
    }

    // 重力累加
    velocityY += gravity * gravityScale * Time.deltaTime;
}
关键注意点
  • 不要把不同轴的速度混在一起做归一化,水平速度、垂直速度独立计算后直接相加即可,避免速度互相干扰。
  • 接地时Y轴速度不要设为0,保留-1~-2左右的微小向下速度,能解决CharacterController.isGrounded检测不稳定、踩在地面上偶尔判定为悬空的问题。
  • 所有和时间相关的速度计算都要放在Update里乘以Time.deltaTime,保证不同帧率下移动速度一致。

内容的提问来源于stack exchange,提问作者nondescript

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 20:27:21