This commit is contained in:
2026-03-01 17:32:12 +08:00
parent 01a1c1c341
commit 7712c73be2
5 changed files with 314 additions and 180 deletions

View File

@@ -5,179 +5,144 @@
[RequireComponent(typeof(CapsuleCollider))]
public class BobberBuoyancyStable : MonoBehaviour
{
[Header("Water")]
public float waterLevelY = 0f;
[Header("Water Physics")] public float waterLevel = 0f;
[Tooltip("必须至少浸入这么深才开始产生浮力(防止还没入水就被顶)")]
public float enterWaterDepth = 0.003f; // 3mm按你的尺度改
[Tooltip("浮力倍数 (建议1.0-1.5)")] public float buoyancyMultiplier = 1.2f;
[Tooltip("在这个深度范围内做平滑过渡(越大越软)")]
public float smoothDepth = 0.02f;
[Tooltip("阻尼系数")] public float dampingFactor = 0.3f;
[Header("Buoyancy Spring")]
public float buoyancySpring = 30f;
public float buoyancyDamping = 8f;
[Header("Debug")] public bool showDebugInfo = true;
[Tooltip("最大上浮加速度限制(0=不限制)")]
public float maxUpAcceleration = 0f;
[Header("Water Drag")]
public float extraLinearDampingInWater = 2f;
public float extraAngularDampingInWater = 2f;
[Header("Center Of Mass")]
public bool driveCenterOfMassFromCapsule = true;
public Vector3 extraCenterOfMassOffset = new Vector3(0f, -0.01f, 0f); // 恢复原来的重心下移
[Header("Righting")]
public float rightingTorque = 3f; // 适中的归正扭矩
public float rightingDamping = 0.8f;
Rigidbody rb;
CapsuleCollider cap;
float airLinearDamping;
float airAngularDamping;
private Rigidbody rb;
private CapsuleCollider capsule;
void Awake()
{
rb = GetComponent<Rigidbody>();
cap = GetComponent<CapsuleCollider>();
capsule = GetComponent<CapsuleCollider>();
rb.useGravity = true;
airLinearDamping = rb.linearDamping;
airAngularDamping = rb.angularDamping;
ApplyCenterOfMass();
rb.maxAngularVelocity = 50f;
// 移除了强制设置物理参数的代码保留用户在Inspector中的设置
if (showDebugInfo)
{
Debug.Log($"Initial mass: {rb.mass}kg, Volume: {CalculateTotalVolume():F6}m³");
}
}
void FixedUpdate()
{
ApplyCenterOfMass();
ApplyCorrectedBuoyancy();
}
Bounds b = cap.bounds;
float bottomY = b.min.y;
float topY = b.max.y;
void ApplyCorrectedBuoyancy()
{
// 使用本地坐标系计算边界,支持任意旋转角度
Vector3 localUp = transform.up; // 获取物体的本地向上向量
Vector3 localDown = -localUp;
// 用"底部点"判定是否真正入水
float bottomSubmersion = waterLevelY - bottomY;
if (bottomSubmersion <= enterWaterDepth)
{
rb.linearDamping = airLinearDamping;
rb.angularDamping = airAngularDamping;
return;
}
// 正确获取世界坐标下的胶囊体边界(考虑旋转)
Vector3 worldBottom = transform.TransformPoint(capsule.center +
localDown * (capsule.height * 0.5f));
Vector3 worldTop = transform.TransformPoint(capsule.center +
localUp * (capsule.height * 0.5f));
// 进入水中:阻尼随浸入增强
float w = Smooth01((bottomSubmersion - enterWaterDepth) / Mathf.Max(1e-4f, smoothDepth));
rb.linearDamping = airLinearDamping + extraLinearDampingInWater * w;
rb.angularDamping = airAngularDamping + extraAngularDampingInWater * w;
float bottomY = worldBottom.y;
float topY = worldTop.y;
float totalHeight = Mathf.Abs(topY - bottomY); // 使用绝对值确保高度为正
// 关键修正:正确的浮力计算
float volume = Mathf.PI * cap.radius * cap.radius * cap.height;
float submergedVolume = volume * Mathf.Clamp01(bottomSubmersion / cap.height);
// 浮力 = 排开液体重量 = 体积 × 密度 × 重力
float buoyantForce = submergedVolume * 1000f * 9.81f;
// 物体重量
// 计算浸入深度
float immersionDepth = Mathf.Max(0, waterLevel - bottomY);
if (immersionDepth <= 0) return;
// 计算浸入比例
float immersionRatio = Mathf.Clamp01(immersionDepth / totalHeight);
// 计算总重量
float weight = rb.mass * 9.81f;
// 净浮力(浮力 - 重量
float netBuoyancy = buoyantForce - weight;
// 添加弹簧阻尼系统
float velocity = Vector3.Dot(rb.linearVelocity, Vector3.up);
float springForce = buoyancySpring * bottomSubmersion;
float dampingForce = buoyancyDamping * velocity;
float totalForce = netBuoyancy + springForce - dampingForce;
totalForce *= w; // 平滑过渡
// 限制向上的力
if (totalForce < 0) totalForce = 0;
// 限制最大加速度
if (maxUpAcceleration > 0f)
// 计算排水体积(关键修正
float totalVolume = CalculateTotalVolume();
float displacedVolume = totalVolume * immersionRatio;
if (displacedVolume < 0)
{
float maxForce = rb.mass * maxUpAcceleration;
if (totalForce > maxForce) totalForce = maxForce;
Debug.LogError($"入水深度异常={displacedVolume}");
displacedVolume = 0;
}
// 计算浮力
float buoyantForce = displacedVolume * 1000f * 9.81f * buoyancyMultiplier;
// 净浮力 = 浮力 - 重量
float netForce = buoyantForce - weight;
// 浮力作用点
float buoyY = Mathf.Min(waterLevelY - 0.001f, topY);
buoyY = Mathf.Max(buoyY, bottomY);
Vector3 buoyPoint = new Vector3(b.center.x, buoyY, b.center.z);
float buoyancyY = bottomY + (immersionDepth * 0.5f);
Vector3 buoyancyPoint = new Vector3(transform.position.x, buoyancyY, transform.position.z);
rb.AddForceAtPosition(Vector3.up * totalForce, buoyPoint, ForceMode.Force);
// 应用力
rb.AddForceAtPosition(Vector3.up * netForce, buoyancyPoint, ForceMode.Force);
// 简化的归正扭矩系统
SimpleRightingSystem(w);
}
// 添加阻尼稳定浮动
Vector3 velocity = rb.GetPointVelocity(buoyancyPoint);
Vector3 dampingForce = -velocity * dampingFactor * immersionRatio;
rb.AddForceAtPosition(dampingForce, buoyancyPoint, ForceMode.Force);
void SimpleRightingSystem(float weight)
{
Vector3 up = transform.up;
Vector3 axis = Vector3.Cross(up, Vector3.up);
float mag = axis.magnitude;
if (mag > 1e-4f)
if (showDebugInfo)
{
axis /= mag;
float angle = Mathf.Asin(Mathf.Clamp(mag, -1f, 1f)) * Mathf.Rad2Deg;
float angVelOnAxis = Vector3.Dot(rb.angularVelocity, axis);
// 归正扭矩
float torque = (rightingTorque * angle - rightingDamping * angVelOnAxis) * weight;
rb.AddTorque(axis * torque, ForceMode.Acceleration);
Debug.Log($"Immersion: {immersionRatio:P1}, " +
$"DispVol: {displacedVolume:F6}m³, " +
$"Buoyancy: {buoyantForce:F2}N, " +
$"NetForce: {netForce:F2}N");
}
}
void ApplyCenterOfMass()
float CalculateTotalVolume()
{
if (!driveCenterOfMassFromCapsule) return;
rb.centerOfMass = cap.center + extraCenterOfMassOffset;
// 胶囊体总体积 = 圆柱体 + 两个半球
float radius = capsule.radius;
float cylinderHeight = capsule.height - (2 * radius); // 减去两端球冠
// 圆柱体积
float cylinderVolume = Mathf.PI * radius * radius * cylinderHeight;
// 两个半球 = 一个完整球体
float sphereVolume = (4f / 3f) * Mathf.PI * Mathf.Pow(radius, 3);
float totalVolume = cylinderVolume + sphereVolume;
return totalVolume;
}
static float Smooth01(float t)
{
t = Mathf.Clamp01(t);
return t * t * (3f - 2f * t);
}
void OnDrawGizmos()
{
if (cap == null) return;
Gizmos.color = Color.blue;
Bounds bounds = cap.bounds;
// 绘制浸入部分
float bottomY = bounds.min.y;
float submergedHeight = Mathf.Max(0, waterLevelY - bottomY);
Vector3 submergedCenter = new Vector3(
bounds.center.x,
bottomY + submergedHeight * 0.5f,
bounds.center.z
);
Vector3 submergedSize = new Vector3(
bounds.size.x,
submergedHeight,
bounds.size.z
);
Gizmos.DrawWireCube(submergedCenter, submergedSize);
// 显示重心
if (rb != null)
if (capsule == null) return;
// 显示胶囊体边界
Vector3 worldBottom = transform.TransformPoint(capsule.center -
Vector3.up * (capsule.height * 0.5f));
Vector3 worldTop = transform.TransformPoint(capsule.center +
Vector3.up * (capsule.height * 0.5f));
Gizmos.color = Color.white;
Gizmos.DrawLine(worldBottom, worldTop);
// 显示浸入部分
float immersion = Mathf.Max(0, waterLevel - worldBottom.y);
if (immersion > 0)
{
Gizmos.color = Color.red;
Vector3 comWorld = transform.TransformPoint(rb.centerOfMass);
Gizmos.DrawSphere(comWorld, 0.005f);
Gizmos.color = new Color(0, 0, 1, 0.3f);
float submergedHeight = Mathf.Min(immersion, capsule.height);
Vector3 submergedCenter = new Vector3(
transform.position.x,
worldBottom.y + submergedHeight * 0.5f,
transform.position.z
);
Gizmos.DrawSphere(submergedCenter, capsule.radius);
}
// 显示水面
Gizmos.color = Color.blue;
Vector3 waterPos = new Vector3(transform.position.x, waterLevel, transform.position.z);
Gizmos.DrawWireSphere(waterPos, 0.05f);
}
}
}