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

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的RigidbodyInterpolation设置为Interpolate,让Boat的移动在Update帧平滑显示
  • 降低CharacterController的Min Move Distance值,避免微小位移被忽略导致的抖动

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 20:05:08