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

CharacterController.velocity始终为(0,0,0)问题求助

问题分析与修复方案

核心问题

  1. 输入未被读取:代码中inputVector从未获取玩家的键盘输入(如Input.GetAxis),导致水平移动方向始终为0,cc.velocity自然没有水平分量。
  2. Move调用方式错误:两次分开调用cc.Move,导致水平移动的位移未被纳入cc.velocity计算,只有重力部分的位移被记录,所以水平方向velocity始终为(0,0,0)。
  3. 状态判断未执行:DetermineMoveState()方法没有在帧更新逻辑中被调用,currentMoveState从未更新,无法判断冲刺状态。
  4. 地面状态未更新:onAir变量没有通过地面检测赋值,重力逻辑的正确性无法保证。

修复步骤

  • 读取玩家的水平/垂直轴输入,赋值给inputVector。
  • 将水平移动和重力移动的向量合并后,一次性调用cc.Move,确保cc.velocity能正确记录完整移动速度。
  • 在Update中调用DetermineMoveState(),实时更新移动状态。
  • 用CharacterController.isGrounded更新onAir变量,完善地面检测。
  • 修改静止判断逻辑:忽略y轴分量,用阈值代替严格等于0的判断,避免浮点精度问题。

修复后的完整代码

using UnityEngine;

public enum PlayerMoveState {
    None, Walking, Sprinting, Crouching, Aiming
}

public class PlayerMovement : MonoBehaviour {
    public CharacterController cc;
    public LayerMask ground;
    public Transform eye;

    [HideInInspector] public PlayerMoveState currentMoveState;
    [HideInInspector] public bool onAir;

    [Space]
    [Header("Movement Settings")]
    public float moveSpeed;
    [Range(1f, 2f)] public float sprintMultiplier;
    [Range(0f, 1f)] public float airMultiplier;
    [Range(0f, 1f)] public float crouchMultiplier;
    [Range(0f, 1f)] public float aimMultiplier;
    public float crouchTransition;
    public float jumpHeight;
    public float gravity = -9.81f;
    // 静止判断阈值,规避浮点精度误差
    public float idleThreshold = 0.01f;

    [Space]
    [Header("Keybinds")]
    public KeyCode sprintKey = KeyCode.LeftShift;
    public KeyCode crouchKey = KeyCode.LeftControl;
    public KeyCode jumpKey = KeyCode.Space;

    Vector2 inputVector;
    Vector3 velocity;
    
    void Update() {
        // 每帧更新移动状态
        currentMoveState = DetermineMoveState();
    }

    void FixedUpdate() {
        // 获取玩家输入
        inputVector = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
        inputVector = inputVector.normalized;

        // 根据当前状态计算移动速度
        float currentSpeed = moveSpeed;
        if (currentMoveState == PlayerMoveState.Sprinting) currentSpeed *= sprintMultiplier;
        if (currentMoveState == PlayerMoveState.Crouching) currentSpeed *= crouchMultiplier;
        if (onAir) currentSpeed *= airMultiplier;

        // 计算移动方向
        Vector3 moveDir = transform.forward * inputVector.y + transform.right * inputVector.x;
        moveDir *= currentSpeed * Time.fixedDeltaTime;

        // 重力与跳跃处理
        onAir = !cc.isGrounded;
        if (onAir) {
            velocity.y += gravity * Time.fixedDeltaTime;
        } else {
            velocity.y = -2f;
            if (Input.GetKeyDown(jumpKey)) {
                velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
            }
        }

        // 合并移动向量,单次调用Move
        Vector3 totalMove = moveDir + velocity * Time.fixedDeltaTime;
        cc.Move(totalMove);
    }

    PlayerMoveState DetermineMoveState() {
        if (Input.GetKey(crouchKey)) return PlayerMoveState.Crouching;

        // 忽略y轴,仅检查水平方向是否静止
        Vector3 horizontalVelocity = new Vector3(cc.velocity.x, 0, cc.velocity.z);
        if (horizontalVelocity.sqrMagnitude < idleThreshold) return PlayerMoveState.None;

        // 仅允许地面冲刺(可按需调整)
        if (Input.GetKey(sprintKey) && !onAir) return PlayerMoveState.Sprinting;

        return PlayerMoveState.Walking;
    }
}

额外说明

  • 加入idleThreshold阈值,避免因浮点精度误差导致静止判断失效。
  • 冲刺状态增加地面检测限制,避免空中冲刺的不合理逻辑(可根据需求移除)。
  • 速度调整逻辑整合到FixedUpdate中,确保不同状态下的移动速度正确生效。
  • 用cc.isGrounded直接判断地面状态,简化检测逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 19:20:16