Unity:如何让浮动的Boat平滑推动带CharacterController的Player?
解决Player在浮动Boat上抖动的方案
以下是针对CharacterController与Rigidbody Boat交互抖动问题的具体修复建议:
1. 同步物理更新时机
CharacterController的移动和重力计算要和Boat的Rigidbody物理更新同步,把相关逻辑从Update()移到FixedUpdate()里,避免帧不同步导致的抖动:
private void FixedUpdate() { ApplyGravity(); MoveAround(); // 统一在FixedUpdate执行CharacterController移动,用fixedDeltaTime characterController.Move(moveDirection * Time.fixedDeltaTime); }
注意:移除原来Update方法中对这两个函数的调用,同时确保所有时间相关的计算都使用Time.fixedDeltaTime
2. 替换isGrounded为自定义Boat检测
CharacterController自带的isGrounded在Boat快速浮动时容易误判,改用射线检测精准判断是否站在Boat上:
[SerializeField] private float groundCheckOffset = 0.1f; [SerializeField] private LayerMask boatLayer; // 提前把Boat设为单独层 private bool IsStandingOnBoat() { // 从CharacterController底部向下发射短射线 Vector3 rayStart = transform.position + Vector3.down * (characterController.height/2 - characterController.radius); if (Physics.Raycast(rayStart, Vector3.down, out RaycastHit hit, groundCheckOffset * 2, boatLayer)) { return hit.collider.GetComponent<BoatController>() != null; } return false; }
然后修改重力函数:
private void ApplyGravity() { if (!IsStandingOnBoat()) { moveDirection.y -= gravity * Time.fixedDeltaTime; } else { // 保留轻微向下的力,确保Player贴合Boat,避免浮空 moveDirection.y = -0.05f; } }
3. 修正移动速度的叠加逻辑
你原来的代码先叠加Boat速度再归一化,会丢失Boat的速度分量,导致移动逻辑异常。调整顺序,先计算玩家自身的移动速度,再叠加Boat的速度:
public void MoveAround() { Vector2 moveInput = playerInputActions.Player.Move.ReadValue<Vector2>(); currentInput = new Vector2(moveInput.y, moveInput.x); float speed = isSprinting ? sprintSpeed : walkSpeed; // 先计算玩家本地移动的世界方向 Vector3 playerMove = (transform.forward * currentInput.x + transform.right * currentInput.y).normalized * speed; // 叠加Boat的刚体速度 Vector3 finalMove = playerMove + boatController.boatRigidbody.velocity; // 保留重力的Y分量 finalMove.y = moveDirection.y; moveDirection = finalMove; // 只有在有输入时才限制速度范围,避免Boat速度导致的无意义移动 if (currentInput.magnitude > 0.1f) { moveDirection.x = Mathf.Clamp(moveDirection.x, -speed, speed); moveDirection.z = Mathf.Clamp(moveDirection.z, -speed, speed); } }
4. 平滑Player与Boat的位置贴合
当检测到站在Boat上时,用插值平滑Player的Y轴位置,减少硬碰撞带来的抖动:
[SerializeField] private float smoothAttachSpeed = 8f; private void FixedUpdate() { ApplyGravity(); MoveAround(); if (IsStandingOnBoat()) { Vector3 rayStart = transform.position + Vector3.down * (characterController.height/2 - characterController.radius); if (Physics.Raycast(rayStart, Vector3.down, out RaycastHit hit, groundCheckOffset * 2, boatLayer)) { // 计算Player应该在的Y位置(Boat表面+CharacterController半高) float targetY = hit.point.y + characterController.height/2; // 平滑插值 transform.position = Vector3.Lerp(transform.position, new Vector3(transform.position.x, targetY, transform.position.z), smoothAttachSpeed * Time.fixedDeltaTime); } } characterController.Move(moveDirection * Time.fixedDeltaTime); }
额外检查项:
- 把Boat的Rigidbody
Interpolation设置为Interpolate,让Boat的移动在Update帧平滑显示 - 降低CharacterController的
Min Move Distance值,避免微小位移被忽略导致的抖动
内容的提问来源于stack exchange,提问作者Magnality
相关产品推荐
相关产品推荐

