using UnityEngine; [DisallowMultipleComponent] [RequireComponent(typeof(Rigidbody))] [RequireComponent(typeof(CapsuleCollider))] public class BobberBuoyancyStable : MonoBehaviour { [Header("Water Physics")] public float waterLevel = 0f; [Tooltip("浮力倍数 (建议1.0-1.5)")] public float buoyancyMultiplier = 1.2f; [Tooltip("阻尼系数")] public float dampingFactor = 0.3f; [Header("Debug")] public bool showDebugInfo = true; private Rigidbody rb; private CapsuleCollider capsule; void Awake() { rb = GetComponent(); capsule = GetComponent(); rb.useGravity = true; if (showDebugInfo) { Debug.Log($"Initial mass: {rb.mass}kg, Volume: {CalculateTotalVolume():F6}m³"); } } void FixedUpdate() { ApplyCorrectedBuoyancy(); } void ApplyCorrectedBuoyancy() { // 使用本地坐标系计算边界,支持任意旋转角度 Vector3 localUp = transform.up; // 获取物体的本地向上向量 Vector3 localDown = -localUp; // 正确获取世界坐标下的胶囊体边界(考虑旋转) Vector3 worldBottom = transform.TransformPoint(capsule.center + localDown * (capsule.height * 0.5f)); Vector3 worldTop = transform.TransformPoint(capsule.center + localUp * (capsule.height * 0.5f)); float bottomY = worldBottom.y; float topY = worldTop.y; float totalHeight = Mathf.Abs(topY - bottomY); // 使用绝对值确保高度为正 // 计算浸入深度 float immersionDepth = Mathf.Max(0, waterLevel - bottomY); if (immersionDepth <= 0) return; // 计算浸入比例 float immersionRatio = Mathf.Clamp01(immersionDepth / totalHeight); // 计算总重量 float weight = rb.mass * 9.81f; // 计算排水体积(关键修正) float totalVolume = CalculateTotalVolume(); float displacedVolume = totalVolume * immersionRatio; if (displacedVolume < 0) { Debug.LogError($"入水深度异常={displacedVolume}"); displacedVolume = 0; } // 计算浮力 float buoyantForce = displacedVolume * 1000f * 9.81f * buoyancyMultiplier; // 净浮力 = 浮力 - 重量 float netForce = buoyantForce - weight; // 浮力作用点 float buoyancyY = bottomY + (immersionDepth * 0.5f); Vector3 buoyancyPoint = new Vector3(transform.position.x, buoyancyY, transform.position.z); // 应用力 rb.AddForceAtPosition(Vector3.up * netForce, buoyancyPoint, ForceMode.Force); // 添加阻尼稳定浮动 Vector3 velocity = rb.GetPointVelocity(buoyancyPoint); Vector3 dampingForce = -velocity * dampingFactor * immersionRatio; rb.AddForceAtPosition(dampingForce, buoyancyPoint, ForceMode.Force); if (showDebugInfo) { Debug.Log($"Immersion: {immersionRatio:P1}, " + $"DispVol: {displacedVolume:F6}m³, " + $"Buoyancy: {buoyantForce:F2}N, " + $"NetForce: {netForce:F2}N"); } } float CalculateTotalVolume() { // 胶囊体总体积 = 圆柱体 + 两个半球 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; } void OnDrawGizmos() { 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 = 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); } }