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
相关产品推荐
相关产品推荐

