CharacterController.velocity始终为(0,0,0)问题求助
问题分析与修复方案
核心问题
- 输入未被读取:代码中
inputVector从未获取玩家的键盘输入(如Input.GetAxis),导致水平移动方向始终为0,cc.velocity自然没有水平分量。 - Move调用方式错误:两次分开调用
cc.Move,导致水平移动的位移未被纳入cc.velocity计算,只有重力部分的位移被记录,所以水平方向velocity始终为(0,0,0)。 - 状态判断未执行:
DetermineMoveState()方法没有在帧更新逻辑中被调用,currentMoveState从未更新,无法判断冲刺状态。 - 地面状态未更新:
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
相关产品推荐
相关产品推荐

