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

Unity海盗船无法平滑贴合波浪网格法线旋转问题求助

实现船只平滑贴合波浪的可落地方案

核心问题根源

你当前碰到的旋转跳变,本质是两个原因导致的:

  1. 仅使用中心点单条射线采样,直接匹配当前三角面的法线,大尺寸三角面的法线是离散值,船移动时跨三角面就会出现跳变
  2. 旋转没有做平滑过渡,直接赋值当前帧法线对应的旋转量,没有过渡动画

可直接复用的优化方案

1. 多点采样计算目标姿态

放弃单点法线,在船体的船头、船尾、左舷、右舷4个位置分别发射射线采样水面高度,通过4个点的高度差计算船体的目标俯仰和横滚角度,比直接用碰撞法线兼容性更强,也不会受三角面尺寸影响。

2. 增加旋转平滑阻尼

使用Quaternion.Slerp对当前旋转和目标旋转做球面插值,控制平滑系数即可平衡跟随速度和流畅度。

优化后完整代码

// 船体波浪适配脚本
[SerializeField] private LayerMask m_whatIsSea;
[SerializeField] private float m_offsetFromWaterSurface;
// 新增:四个采样点相对于船体中心点的本地偏移,根据船体实际尺寸调整
[SerializeField] private Vector3 m_bowOffset = new Vector3(0, 0, 2f); // 船头偏移
[SerializeField] private Vector3 m_sternOffset = new Vector3(0, 0, -2f); // 船尾偏移
[SerializeField] private Vector3 m_portOffset = new Vector3(-1f, 0, 0); // 左舷偏移
[SerializeField] private Vector3 m_starboardOffset = new Vector3(1f, 0, 0); // 右舷偏移
[SerializeField] private float m_rotationSmooth = 0.1f; // 旋转平滑系数,值越小越平滑

private void Update()
{
    // 分别采样四个点的水面高度
    float bowHeight = GetWaterHeight(transform.TransformPoint(m_bowOffset));
    float sternHeight = GetWaterHeight(transform.TransformPoint(m_sternOffset));
    float portHeight = GetWaterHeight(transform.TransformPoint(m_portOffset));
    float starboardHeight = GetWaterHeight(transform.TransformPoint(m_starboardOffset));
    
    // 计算中心点高度,用于位置更新
    float centerHeight = (bowHeight + sternHeight + portHeight + starboardHeight) / 4f;
    transform.position = new Vector3(transform.position.x, centerHeight + m_offsetFromWaterSurface, transform.position.z);
    
    // 计算目标旋转:俯仰角(前后高度差)、横滚角(左右高度差)
    float pitchAngle = Mathf.Atan2(sternHeight - bowHeight, m_bowOffset.z - m_sternOffset.z) * Mathf.Rad2Deg;
    float rollAngle = Mathf.Atan2(portHeight - starboardHeight, m_starboardOffset.x - m_portOffset.x) * Mathf.Rad2Deg;
    Quaternion targetRotation = Quaternion.Euler(pitchAngle, transform.eulerAngles.y, rollAngle);
    
    // 平滑过渡旋转
    transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, m_rotationSmooth);
}

// 单独封装采样水面高度的方法
private float GetWaterHeight(Vector3 samplePoint)
{
    RaycastHit hit;
    // 从采样点上方足够高度向下打射线
    if (Physics.Raycast(samplePoint + Vector3.up * Mathf.Abs(m_offsetFromWaterSurface * 4), -Vector3.up, out hit, 100f, m_whatIsSea))
    {
        return hit.point.y;
    }
    // 没检测到水面时返回当前高度,避免报错
    return transform.position.y - m_offsetFromWaterSurface;
}

调试注意事项

  • 四个采样点的偏移值要匹配你船体的实际尺寸,不要超出船体的轮廓范围
  • 若需要更高的贴合精度,可以新增船身中部的采样点,加权平均计算高度即可
  • 如果波浪运动速度很快,可以适当调大m_rotationSmooth的值,避免旋转延迟过大导致穿模

波浪网格示意图


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 16:36:03