using UnityEngine; [DisallowMultipleComponent] [RequireComponent(typeof(Rigidbody))] [RequireComponent(typeof(CapsuleCollider))] public class BobberBuoyancyStable : MonoBehaviour { [Header("Water")] public float waterLevelY = 0f; [Tooltip("必须至少浸入这么深才开始产生浮力(防止还没入水就被顶)")] public float enterWaterDepth = 0.003f; // 3mm(按你的尺度改) [Tooltip("在这个深度范围内做平滑过渡(越大越软)")] public float smoothDepth = 0.02f; [Header("Buoyancy Spring")] public float buoyancySpring = 30f; public float buoyancyDamping = 8f; [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; void Awake() { rb = GetComponent(); cap = GetComponent(); rb.useGravity = true; airLinearDamping = rb.linearDamping; airAngularDamping = rb.angularDamping; ApplyCenterOfMass(); rb.maxAngularVelocity = 50f; // 移除了强制设置物理参数的代码,保留用户在Inspector中的设置 } void FixedUpdate() { ApplyCenterOfMass(); Bounds b = cap.bounds; float bottomY = b.min.y; float topY = b.max.y; // 用"底部点"判定是否真正入水 float bottomSubmersion = waterLevelY - bottomY; if (bottomSubmersion <= enterWaterDepth) { rb.linearDamping = airLinearDamping; rb.angularDamping = airAngularDamping; return; } // 进入水中:阻尼随浸入增强 float w = Smooth01((bottomSubmersion - enterWaterDepth) / Mathf.Max(1e-4f, smoothDepth)); rb.linearDamping = airLinearDamping + extraLinearDampingInWater * w; rb.angularDamping = airAngularDamping + extraAngularDampingInWater * w; // 关键修正:正确的浮力计算 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 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 maxForce = rb.mass * maxUpAcceleration; if (totalForce > maxForce) totalForce = maxForce; } // 浮力作用点 float buoyY = Mathf.Min(waterLevelY - 0.001f, topY); buoyY = Mathf.Max(buoyY, bottomY); Vector3 buoyPoint = new Vector3(b.center.x, buoyY, b.center.z); rb.AddForceAtPosition(Vector3.up * totalForce, buoyPoint, ForceMode.Force); // 简化的归正扭矩系统 SimpleRightingSystem(w); } void SimpleRightingSystem(float weight) { Vector3 up = transform.up; Vector3 axis = Vector3.Cross(up, Vector3.up); float mag = axis.magnitude; if (mag > 1e-4f) { 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); } } void ApplyCenterOfMass() { if (!driveCenterOfMassFromCapsule) return; rb.centerOfMass = cap.center + extraCenterOfMassOffset; } 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) { Gizmos.color = Color.red; Vector3 comWorld = transform.TransformPoint(rb.centerOfMass); Gizmos.DrawSphere(comWorld, 0.005f); } } }