提交测试代码

This commit is contained in:
Bob.Song
2026-03-03 18:02:59 +08:00
parent cbfe9581b1
commit 209049354c
4 changed files with 292 additions and 252 deletions

View File

@@ -863,6 +863,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 06d107cece7c4cbb9825557923be567f, type: 3} m_Script: {fileID: 11500000, guid: 06d107cece7c4cbb9825557923be567f, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: Assembly-CSharp::BuoyancyWaterProvider m_EditorClassIdentifier: Assembly-CSharp::BuoyancyWaterProvider
Water: {fileID: 0}
waterLevel: 0 waterLevel: 0
--- !u!4 &820800910 --- !u!4 &820800910
Transform: Transform:
@@ -3748,7 +3749,7 @@ RectTransform:
m_Father: {fileID: 2256579932936496278} m_Father: {fileID: 2256579932936496278}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0} m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0.2, y: 1} m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5} m_Pivot: {x: 0.5, y: 0.5}
@@ -4552,7 +4553,7 @@ GameObject:
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
m_NavMeshLayer: 0 m_NavMeshLayer: 0
m_StaticEditorFlags: 0 m_StaticEditorFlags: 0
m_IsActive: 0 m_IsActive: 1
--- !u!1 &2575899344989503316 --- !u!1 &2575899344989503316
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -5308,8 +5309,8 @@ RectTransform:
m_Children: [] m_Children: []
m_Father: {fileID: 4262266657851529727} m_Father: {fileID: 4262266657851529727}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.2, y: 0} m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0.2, y: 1} m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 20, y: 0} m_SizeDelta: {x: 20, y: 0}
m_Pivot: {x: 0.5, y: 0.5} m_Pivot: {x: 0.5, y: 0.5}
@@ -6492,7 +6493,6 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f91c9d873c83492ca6d5e3e3a67c1760, type: 3} m_Script: {fileID: 11500000, guid: f91c9d873c83492ca6d5e3e3a67c1760, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: Assembly-CSharp::CapsuleBuoyancyStable m_EditorClassIdentifier: Assembly-CSharp::CapsuleBuoyancyStable
WaterBehaviour: {fileID: 820800909}
buoyancyScale: 1.6 buoyancyScale: 1.6
samplePoints: 9 samplePoints: 9
submergenceCurve: submergenceCurve:
@@ -6526,6 +6526,9 @@ MonoBehaviour:
uprightAxis: 1 uprightAxis: 1
extraDragInWater: 0.8 extraDragInWater: 0.8
extraAngularDragInWater: 0.8 extraAngularDragInWater: 0.8
Water: {fileID: 0}
_Layer: 1
_ObjectWidth: 3
drawDebug: 0 drawDebug: 0
--- !u!114 &7647511515837834160 --- !u!114 &7647511515837834160
MonoBehaviour: MonoBehaviour:
@@ -7303,7 +7306,6 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: f91c9d873c83492ca6d5e3e3a67c1760, type: 3} m_Script: {fileID: 11500000, guid: f91c9d873c83492ca6d5e3e3a67c1760, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: Assembly-CSharp::CapsuleBuoyancyStable m_EditorClassIdentifier: Assembly-CSharp::CapsuleBuoyancyStable
WaterBehaviour: {fileID: 820800909}
buoyancyScale: 1.6 buoyancyScale: 1.6
samplePoints: 9 samplePoints: 9
submergenceCurve: submergenceCurve:
@@ -7337,6 +7339,9 @@ MonoBehaviour:
uprightAxis: 1 uprightAxis: 1
extraDragInWater: 0.8 extraDragInWater: 0.8
extraAngularDragInWater: 0.8 extraAngularDragInWater: 0.8
Water: {fileID: 2531380344179187550}
_Layer: 1
_ObjectWidth: 0.2
drawDebug: 0 drawDebug: 0
--- !u!153 &8264839114692736908 --- !u!153 &8264839114692736908
ConfigurableJoint: ConfigurableJoint:

View File

@@ -6,219 +6,204 @@ public interface IWaterProvider
Vector3 GetWaterNormal(Vector3 worldPos); Vector3 GetWaterNormal(Vector3 worldPos);
Vector3 GetWaterVelocity(Vector3 worldPos); Vector3 GetWaterVelocity(Vector3 worldPos);
} }
//
/// <summary> // /// <summary>
/// 稳定优先的浮力(只支持 CapsuleCollider / SphereCollider // /// 稳定优先的浮力(只支持 CapsuleCollider / SphereCollider
/// - 竖直方向:目标吃水深度 + PD 控制(稳定,不抖、不弹飞) // /// - 竖直方向:目标吃水深度 + PD 控制(稳定,不抖、不弹飞)
/// - 姿态Righting Torque 扶正(受 Rigidbody.centerOfMass 影响) // /// - 姿态Righting Torque 扶正(受 Rigidbody.centerOfMass 影响)
/// - 入水比例:带平滑(避免水面附近开关抖动) // /// - 入水比例:带平滑(避免水面附近开关抖动)
/// </summary> // /// </summary>
[DisallowMultipleComponent] // [DisallowMultipleComponent]
[RequireComponent(typeof(Rigidbody))] // [RequireComponent(typeof(Rigidbody))]
public class BuoyancyCapsuleSphere : MonoBehaviour // public class BuoyancyCapsuleSphere : MonoBehaviour
{ // {
[Header("Collider (choose one)")] // [Header("Collider (choose one)")] public CapsuleCollider capsule;
public CapsuleCollider capsule; // public SphereCollider sphere;
public SphereCollider sphere; //
// [Header("Water")] public MonoBehaviour waterProviderBehaviour; // 可选
[Header("Water")] // private IWaterProvider waterProvider;
public MonoBehaviour waterProviderBehaviour; // 可选 //
private IWaterProvider waterProvider; // [Tooltip("没有 provider 时的水面高度")] public float waterLevel = 0f;
//
[Tooltip("没有 provider 时的水面高度")] // [Header("Density -> Draft")] [Tooltip("水密度 kg/m^3淡水约1000")]
public float waterLevel = 0f; // public float waterDensity = 1000f;
//
[Header("Density -> Draft")] // [Tooltip("物体等效密度 kg/m^3。越小越浮。浮漂可 80~400 之间调")]
[Tooltip("水密度 kg/m^3淡水约1000")] // public float objectDensity = 250f;
public float waterDensity = 1000f; //
// [Tooltip("额外浮力比例手感调整。1=按密度算")] public float buoyancyScale = 1f;
[Tooltip("物体等效密度 kg/m^3。越小越浮。浮漂可 80~400 之间调")] //
public float objectDensity = 250f; // [Header("Vertical PD (Stability key)")] [Tooltip("竖直弹簧强度(越大越“顶住”目标吃水)")]
// public float verticalKp = 35f;
[Tooltip("额外浮力比例手感调整。1=按密度算")] //
public float buoyancyScale = 1f; // [Tooltip("竖直阻尼(越大越不弹、不抖)")] public float verticalKd = 12f;
//
[Header("Vertical PD (Stability key)")] // [Tooltip("最大向上加速度 m/s^2防止从高处落下入水被顶飞")]
[Tooltip("竖直弹簧强度(越大越“顶住”目标吃水)")] // public float maxUpAccel = 25f;
public float verticalKp = 35f; //
// [Tooltip("最大向下加速度 m/s^2防止强行拉下去造成抖动")]
[Tooltip("竖直阻尼(越大越不弹、不抖)")] // public float maxDownAccel = 10f;
public float verticalKd = 12f; //
// [Header("Submergence smoothing")] [Tooltip("入水比例变化速度1/s。越大越快响应越小越稳")]
[Tooltip("最大向上加速度 m/s^2防止从高处落下入水被顶飞")] // public float submergenceSpeed = 8f;
public float maxUpAccel = 25f; //
// [Tooltip("水面外的缓冲(m),让浮力更平滑接管")] public float surfaceMargin = 0.01f;
[Tooltip("最大向下加速度 m/s^2防止强行拉下去造成抖动")] //
public float maxDownAccel = 10f; // [Header("Righting (Rotation)")] [Tooltip("扶正强度(把 transform.up 拉向水面法线/世界上)")]
// public float rightingKp = 8f;
[Header("Submergence smoothing")] //
[Tooltip("入水比例变化速度1/s。越大越快响应越小越稳")] // [Tooltip("扶正阻尼(抑制旋转抖动)")] public float rightingKd = 3f;
public float submergenceSpeed = 8f; //
// [Header("Water drag (optional but helpful)")] [Tooltip("入水时额外线阻尼(通过 rb.drag 混合)")]
[Tooltip("水面外的缓冲(m),让浮力更平滑接管")] // public float extraLinearDragInWater = 2.5f;
public float surfaceMargin = 0.01f; //
// [Tooltip("入水时额外角阻尼(通过 rb.angularDrag 混合)")]
[Header("Righting (Rotation)")] // public float extraAngularDragInWater = 2.0f;
[Tooltip("扶正强度(把 transform.up 拉向水面法线/世界上)")] //
public float rightingKp = 8f; // [Header("Center of Mass")] [Tooltip("本地重心偏移:例如 (0,-0.02,0) 让底部更重、更容易站漂")]
// public Vector3 centerOfMassOffset = Vector3.zero;
[Tooltip("扶正阻尼(抑制旋转抖动)")] //
public float rightingKd = 3f; // private Rigidbody rb;
// private float baseDrag, baseAngularDrag;
[Header("Water drag (optional but helpful)")] //
[Tooltip("入水时额外线阻尼(通过 rb.drag 混合)")] // // 关键:入水比例必须有“记忆”(滤波),否则水面边界必抖
public float extraLinearDragInWater = 2.5f; // private float subFiltered = 0f;
//
[Tooltip("入水时额外角阻尼(通过 rb.angularDrag 混合)")] // void Reset()
public float extraAngularDragInWater = 2.0f; // {
// rb = GetComponent<Rigidbody>();
[Header("Center of Mass")] // rb.useGravity = true;
[Tooltip("本地重心偏移:例如 (0,-0.02,0) 让底部更重、更容易站漂")] // rb.interpolation = RigidbodyInterpolation.Interpolate;
public Vector3 centerOfMassOffset = Vector3.zero; // rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
//
private Rigidbody rb; // capsule = GetComponent<CapsuleCollider>();
private float baseDrag, baseAngularDrag; // sphere = GetComponent<SphereCollider>();
// }
// 关键:入水比例必须有“记忆”(滤波),否则水面边界必抖 //
private float subFiltered = 0f; // void Awake()
// {
void Reset() // rb = GetComponent<Rigidbody>();
{ // rb.centerOfMass = centerOfMassOffset;
rb = GetComponent<Rigidbody>(); //
rb.useGravity = true; // baseDrag = rb.linearDamping;
rb.interpolation = RigidbodyInterpolation.Interpolate; // baseAngularDrag = rb.angularDamping;
rb.collisionDetectionMode = CollisionDetectionMode.Continuous; //
// waterProvider = waterProviderBehaviour as IWaterProvider;
capsule = GetComponent<CapsuleCollider>(); //
sphere = GetComponent<SphereCollider>(); // // 只允许一个
} // if (capsule != null && sphere != null)
// sphere = null;
void Awake() // }
{ //
rb = GetComponent<Rigidbody>(); // void OnValidate()
rb.centerOfMass = centerOfMassOffset; // {
// objectDensity = Mathf.Max(1e-3f, objectDensity);
baseDrag = rb.linearDamping; // waterDensity = Mathf.Max(1e-3f, waterDensity);
baseAngularDrag = rb.angularDamping; // submergenceSpeed = Mathf.Max(0.1f, submergenceSpeed);
// surfaceMargin = Mathf.Max(0f, surfaceMargin);
waterProvider = waterProviderBehaviour as IWaterProvider; // maxUpAccel = Mathf.Max(0f, maxUpAccel);
// maxDownAccel = Mathf.Max(0f, maxDownAccel);
// 只允许一个 // }
if (capsule != null && sphere != null) //
sphere = null; // void FixedUpdate()
} // {
// if (!capsule && !sphere) return;
void OnValidate() //
{ // waterProvider = waterProviderBehaviour as IWaterProvider;
objectDensity = Mathf.Max(1e-3f, objectDensity); //
waterDensity = Mathf.Max(1e-3f, waterDensity); // GetCenterAndHeight(out var centerWorld, out var shapeHeight);
submergenceSpeed = Mathf.Max(0.1f, submergenceSpeed); //
surfaceMargin = Mathf.Max(0f, surfaceMargin); // // 水面信息
maxUpAccel = Mathf.Max(0f, maxUpAccel); // float waterH = waterProvider?.GetWaterHeight(centerWorld) ?? waterLevel;
maxDownAccel = Mathf.Max(0f, maxDownAccel); // Vector3 waterN = waterProvider?.GetWaterNormal(centerWorld) ?? Vector3.up;
} // if (waterN.sqrMagnitude < 1e-6f) waterN = Vector3.up;
// waterN.Normalize();
void FixedUpdate() //
{ // // 当前中心“浸没深度”(>0 表示中心在水下)
if (!capsule && !sphere) return; // float centerDepth = waterH - centerWorld.y;
//
waterProvider = waterProviderBehaviour as IWaterProvider; // // 近似入水比例centerDepth = -H/2 -> 0; centerDepth = +H/2 -> 1
// float rawSub = Mathf.Clamp01((centerDepth + (shapeHeight * 0.5f) + surfaceMargin) /
// 取“浮体中心点”作为控制点(稳定,不戳点) // (shapeHeight + 2f * surfaceMargin));
Vector3 centerWorld; //
float shapeHeight; // 近似“高度”sphere=直径capsule=高度 // // 入水比例滤波(非常关键
GetCenterAndHeight(out centerWorld, out shapeHeight); // float dt = Time.fixedDeltaTime;
// subFiltered = Mathf.MoveTowards(subFiltered, rawSub, submergenceSpeed * dt);
// 水面信息 //
float waterH = (waterProvider != null) ? waterProvider.GetWaterHeight(centerWorld) : waterLevel; // // 混合拖拽(让水中更稳)
Vector3 waterN = (waterProvider != null) ? waterProvider.GetWaterNormal(centerWorld) : Vector3.up; // rb.linearDamping = Mathf.Lerp(baseDrag, baseDrag + extraLinearDragInWater, subFiltered);
if (waterN.sqrMagnitude < 1e-6f) waterN = Vector3.up; // rb.angularDamping = Mathf.Lerp(baseAngularDrag, baseAngularDrag + extraAngularDragInWater, subFiltered);
waterN.Normalize(); //
// if (subFiltered <= 1e-4f)
// 当前中心“浸没深度”(>0 表示中心在水下) // return; // 基本没入水,不做任何浮力/扶正
float centerDepth = waterH - centerWorld.y; //
// // 目标吃水比例:理想静态平衡 ≈ objectDensity / waterDensity<1 才会浮)
// 近似入水比例centerDepth = -H/2 -> 0; centerDepth = +H/2 -> 1 // float desiredSub = Mathf.Clamp01((objectDensity / waterDensity) * buoyancyScale);
float rawSub = Mathf.Clamp01((centerDepth + (shapeHeight * 0.5f) + surfaceMargin) / (shapeHeight + 2f * surfaceMargin)); //
// // 把 desiredSub 转成目标中心深度
// 入水比例滤波(非常关键 // // desiredSub=0 -> centerDepthTarget = -H/2完全出水
float dt = Time.fixedDeltaTime; // // desiredSub=1 -> centerDepthTarget = +H/2完全入水
subFiltered = Mathf.MoveTowards(subFiltered, rawSub, submergenceSpeed * dt); // float centerDepthTarget = desiredSub * shapeHeight - shapeHeight * 0.5f;
//
// 混合拖拽(让水中更稳) // // 竖直 PD只沿“世界上/重力反方向”控制,最稳
rb.linearDamping = Mathf.Lerp(baseDrag, baseDrag + extraLinearDragInWater, subFiltered); // Vector3 up = (-Physics.gravity).sqrMagnitude > 1e-6f ? (-Physics.gravity).normalized : Vector3.up;
rb.angularDamping = Mathf.Lerp(baseAngularDrag, baseAngularDrag + extraAngularDragInWater, subFiltered); // float vUp = Vector3.Dot(rb.linearVelocity, up);
//
if (subFiltered <= 1e-4f) // float error = centerDepth - centerDepthTarget; // 深了为正 -> 需要向上推
return; // 基本没入水,不做任何浮力/扶正 // float accelUp = (-verticalKp * error) - (verticalKd * vUp);
//
// 目标吃水比例:理想静态平衡 ≈ objectDensity / waterDensity<1 才会浮) // // 限制上下加速度,避免顶飞或强拉抖动
float desiredSub = Mathf.Clamp01((objectDensity / waterDensity) * buoyancyScale); // accelUp = Mathf.Clamp(accelUp, -maxDownAccel, maxUpAccel);
//
// 把 desiredSub 转成目标中心深度 // // 随入水比例渐入(避免水面边界突然接管)
// desiredSub=0 -> centerDepthTarget = -H/2完全出水 // accelUp *= subFiltered;
// desiredSub=1 -> centerDepthTarget = +H/2完全入水 //
float centerDepthTarget = desiredSub * shapeHeight - shapeHeight * 0.5f; // // 施加竖直加速度Acceleration 不受质量影响,更稳定)
// rb.AddForce(up * accelUp, ForceMode.Acceleration);
// 竖直 PD只沿“世界上/重力反方向”控制,最稳 //
Vector3 up = (-Physics.gravity).sqrMagnitude > 1e-6f ? (-Physics.gravity).normalized : Vector3.up; // // 扶正力矩:把物体 up 拉向 waterN平静水就是 Vector3.up
float vUp = Vector3.Dot(rb.linearVelocity, up); // // 注意:这个扶正与重心偏移一起工作,会形成“站漂/躺漂”的稳定姿态
// Vector3 currentUp = transform.up;
float error = centerDepth - centerDepthTarget; // 深了为正 -> 需要向上推 // Vector3 axis = Vector3.Cross(currentUp, waterN);
float accelUp = (-verticalKp * error) - (verticalKd * vUp); //
// // 小角度近似axis 的大小约等于 sin(theta)
// 限制上下加速度,避免顶飞或强拉抖动 // // 扶正加速度型扭矩(同样用 Acceleration减少质量/惯量差带来的抖动
accelUp = Mathf.Clamp(accelUp, -maxDownAccel, maxUpAccel); // Vector3 angVel = rb.angularVelocity;
// Vector3 torqueAccel = axis * rightingKp - angVel * rightingKd;
// 随入水比例渐入(避免水面边界突然接管) //
accelUp *= subFiltered; // torqueAccel *= subFiltered;
//
// 施加竖直加速度Acceleration 不受质量影响,更稳定) // rb.AddTorque(torqueAccel, ForceMode.Acceleration);
rb.AddForce(up * accelUp, ForceMode.Acceleration); // }
//
// 扶正力矩:把物体 up 拉向 waterN平静水就是 Vector3.up // private void GetCenterAndHeight(out Vector3 centerWorld, out float heightWorld)
// 注意:这个扶正与重心偏移一起工作,会形成“站漂/躺漂”的稳定姿态 // {
Vector3 currentUp = transform.up; // if (sphere)
Vector3 axis = Vector3.Cross(currentUp, waterN); // {
// // spherecenter + 半径*缩放(近似取最大缩放)
// 小角度近似axis 的大小约等于 sin(theta) // Transform t = sphere.transform;
// 扶正加速度型扭矩(同样用 Acceleration减少质量/惯量差带来的抖动) // centerWorld = t.TransformPoint(sphere.center);
Vector3 angVel = rb.angularVelocity; //
Vector3 torqueAccel = axis * rightingKp - angVel * rightingKd; // Vector3 s = t.lossyScale;
// float r = sphere.radius * Mathf.Max(Mathf.Abs(s.x), Mathf.Abs(s.y), Mathf.Abs(s.z));
torqueAccel *= subFiltered; // heightWorld = Mathf.Max(1e-6f, r * 2f);
// return;
rb.AddTorque(torqueAccel, ForceMode.Acceleration); // }
} //
// // capsule
private void GetCenterAndHeight(out Vector3 centerWorld, out float heightWorld) // Transform ct = capsule.transform;
{ // centerWorld = ct.TransformPoint(capsule.center);
if (sphere) //
{ // Vector3 ls = ct.lossyScale;
// spherecenter + 半径*缩放(近似取最大缩放) // float sx = Mathf.Abs(ls.x), sy = Mathf.Abs(ls.y), sz = Mathf.Abs(ls.z);
Transform t = sphere.transform; //
centerWorld = t.TransformPoint(sphere.center); // float heightScale = capsule.direction switch
// {
Vector3 s = t.lossyScale; // 0 => sx,
float r = sphere.radius * Mathf.Max(Mathf.Abs(s.x), Mathf.Abs(s.y), Mathf.Abs(s.z)); // 1 => sy,
heightWorld = Mathf.Max(1e-6f, r * 2f); // _ => sz,
return; // };
} //
// heightWorld = Mathf.Max(1e-6f, capsule.height * heightScale);
// capsule // }
Transform ct = capsule.transform; // }
centerWorld = ct.TransformPoint(capsule.center);
Vector3 ls = ct.lossyScale;
float sx = Mathf.Abs(ls.x), sy = Mathf.Abs(ls.y), sz = Mathf.Abs(ls.z);
float heightScale = capsule.direction switch
{
0 => sx,
1 => sy,
_ => sz,
};
heightWorld = Mathf.Max(1e-6f, capsule.height * heightScale);
}
}

View File

@@ -1,7 +1,10 @@
using UnityEngine; using UnityEngine;
using WaveHarmonic.Crest;
public class BuoyancyWaterProvider : MonoBehaviour, IWaterProvider public class BuoyancyWaterProvider : MonoBehaviour, IWaterProvider
{ {
public WaterRenderer Water;
readonly SampleFlowHelper _SampleFlowHelper = new();
public float waterLevel = 0f; public float waterLevel = 0f;
public float GetWaterHeight(Vector3 worldPos) public float GetWaterHeight(Vector3 worldPos)

View File

@@ -1,46 +1,62 @@
using UnityEngine; using System;
using Gaia;
using UnityEngine;
using WaveHarmonic.Crest;
[DisallowMultipleComponent] [DisallowMultipleComponent]
[RequireComponent(typeof(Rigidbody), typeof(CapsuleCollider))] [RequireComponent(typeof(Rigidbody), typeof(CapsuleCollider))]
public class CapsuleBuoyancyStable : MonoBehaviour public class CapsuleBuoyancyStable : MonoBehaviour
{ {
[Header("References")] [Header("Buoyancy")] [Tooltip("完全浸没时总浮力 = mass*g*buoyancyScale。>1 更浮。")]
public MonoBehaviour WaterBehaviour; // 实现 IWaterProvider
private IWaterProvider Water => WaterBehaviour as IWaterProvider;
[Header("Buoyancy")]
[Tooltip("完全浸没时总浮力 = mass*g*buoyancyScale。>1 更浮。")]
public float buoyancyScale = 1.6f; public float buoyancyScale = 1.6f;
[Tooltip("沿胶囊轴向采样点数量(建议 7~11。")] [Tooltip("沿胶囊轴向采样点数量(建议 7~11。")] [Range(3, 15)]
[Range(3, 15)] public int samplePoints = 9; public int samplePoints = 9;
[Tooltip("浸没比例曲线0=刚碰水, 1=充分在水下)。")] [Tooltip("浸没比例曲线0=刚碰水, 1=充分在水下)。")] public AnimationCurve submergenceCurve = AnimationCurve.Linear(0, 0, 1, 1);
public AnimationCurve submergenceCurve = AnimationCurve.Linear(0, 0, 1, 1);
[Header("Damping")] [Header("Damping")] [Tooltip("上浮方向速度阻尼(越大越不弹)。")]
[Tooltip("上浮方向速度阻尼(越大越不弹)。")]
public float verticalDamping = 3.0f; public float verticalDamping = 3.0f;
[Tooltip("整体角速度阻尼(只施加一次,不要太大)。")] [Tooltip("整体角速度阻尼(只施加一次,不要太大)。")] public float angularDamping = 0.6f;
public float angularDamping = 0.6f;
[Header("Optional Upright Stabilizer (Recommended for bobber)")] [Header("Optional Upright Stabilizer (Recommended for bobber)")] [Tooltip("让胶囊轴向更倾向于对齐世界Up。0=关闭。")]
[Tooltip("让胶囊轴向更倾向于对齐世界Up。0=关闭。")]
public float uprightSpring = 0.0f; public float uprightSpring = 0.0f;
[Tooltip("upright 的角速度阻尼。")] [Tooltip("upright 的角速度阻尼。")] public float uprightDamping = 0.5f;
public float uprightDamping = 0.5f;
[Tooltip("胶囊轴向0=X,1=Y,2=Z通常 CapsuleCollider.direction 也一样)。")] [Tooltip("胶囊轴向0=X,1=Y,2=Z通常 CapsuleCollider.direction 也一样)。")]
public int uprightAxis = 1; public int uprightAxis = 1;
[Header("Water Drag")] [Header("Water Drag")] public float extraDragInWater = 0.8f;
public float extraDragInWater = 0.8f;
public float extraAngularDragInWater = 0.8f; public float extraAngularDragInWater = 0.8f;
[Header("Debug")] #region Crest5相关信息
public bool drawDebug = false;
public Transform Water;
private WaterRenderer _waterRenderer;
[Tooltip("要瞄准哪一层水的碰撞层。")] [SerializeField]
CollisionLayer _Layer = CollisionLayer.AfterAnimatedWaves;
[Header("波响应")] [Tooltip("用于物理计算的物体宽度。\n\n此值越大波响应的滤波效果和平滑程度就越高。如果无法对较大波长进行滤波则应增加 LOD 级别。")] [SerializeField]
float _ObjectWidth = 3f;
readonly SampleFlowHelper _SampleFlowHelper = new();
/// <summary>
/// 查询水面信息点位
/// </summary>
Vector3[] _QueryPoints;
Vector3[] _QueryResultDisplacements;
Vector3[] _QueryResultVelocities;
Vector3[] _QueryResultNormal;
#endregion
[Header("Debug")] public bool drawDebug = false;
Rigidbody _rb; Rigidbody _rb;
CapsuleCollider _cap; CapsuleCollider _cap;
@@ -52,15 +68,30 @@ public class CapsuleBuoyancyStable : MonoBehaviour
_cap = GetComponent<CapsuleCollider>(); _cap = GetComponent<CapsuleCollider>();
_baseDrag = _rb.linearDamping; _baseDrag = _rb.linearDamping;
_baseAngularDrag = _rb.angularDamping; _baseAngularDrag = _rb.angularDamping;
}
if (WaterBehaviour != null && Water == null) private void Start()
Debug.LogError($"{name}: WaterBehaviour 没有实现 IWaterProvider。", this); {
int length = Mathf.Max(3, samplePoints);
_QueryPoints = new Vector3[length];
_QueryResultDisplacements = new Vector3[length];
_QueryResultVelocities = new Vector3[length];
_QueryResultNormal = new Vector3[length];
if(Water)
_waterRenderer = Water.GetComponent<WaterRenderer>();
} }
void FixedUpdate() void FixedUpdate()
{ {
if (Water == null) return; if (Water)
{
}
if (!_waterRenderer)
{
return;
}
GetWorldCapsule(out Vector3 a, out Vector3 b, out float radius); GetWorldCapsule(out Vector3 a, out Vector3 b, out float radius);
int n = Mathf.Max(3, samplePoints); int n = Mathf.Max(3, samplePoints);
@@ -70,12 +101,27 @@ public class CapsuleBuoyancyStable : MonoBehaviour
float subSum = 0f; float subSum = 0f;
int wetCount = 0; int wetCount = 0;
for (int i = 0; i < _QueryPoints.Length; i++)
{
float t = (float)i / (n - 1);
Vector3 p = Vector3.Lerp(a, b, t);
_QueryPoints[i] = p;
}
// 查询
var collisions = _waterRenderer.AnimatedWavesLod.Provider;
collisions.Query(GetHashCode(), _ObjectWidth, _QueryPoints, _QueryResultDisplacements,
_QueryResultNormal, _QueryResultVelocities, _Layer);
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
{ {
float t = (float)i / (n - 1); float t = (float)i / (n - 1);
Vector3 p = Vector3.Lerp(a, b, t); Vector3 p = Vector3.Lerp(a, b, t);
float waterH = Water.GetWaterHeight(p); float waterH =
_QueryResultDisplacements[i].y + _waterRenderer.SeaLevel; //GaiaConstants.Water.GetWaterHeight(p);
float depth = waterH - p.y; // >0 在水下 float depth = waterH - p.y; // >0 在水下
float sub = Mathf.InverseLerp(-radius, radius, depth); // 0..1 float sub = Mathf.InverseLerp(-radius, radius, depth); // 0..1
@@ -87,7 +133,8 @@ public class CapsuleBuoyancyStable : MonoBehaviour
Vector3 buoyDir = Vector3.up; Vector3 buoyDir = Vector3.up;
Vector3 waterVel = Water.GetWaterVelocity(p); // Vector3 waterVel = GaiaConstants.Water.GetWaterVelocity(p);
Vector3 waterVel = _QueryResultVelocities[i];
Vector3 pointVel = _rb.GetPointVelocity(p); Vector3 pointVel = _rb.GetPointVelocity(p);
Vector3 relVel = pointVel - waterVel; Vector3 relVel = pointVel - waterVel;