using UnityEngine; [RequireComponent(typeof(Rigidbody))] public class BobberBuoyancy : MonoBehaviour { [Header("Water")] public float waterLevel = 0f; // 水面是 0 public Vector3 waterFlow = Vector3.zero; [Header("Bobber Size (origin at bottom)")] public float bobberHeight = 0.18f; // 浮漂总高度(底部原点 → 顶部) [Header("Buoyancy")] public float maxBuoyancy = 12f; // 完全浸没浮力 public float buoyancyMultiplier = 1.0f; public float waterDrag = 0.7f; public float waterAngularDrag = 0.4f; [Header("Vertical Damping(控制“慢慢上浮”,避免弹跳)")] public float verticalDamping = 4.0f; // 数值越大,上浮越慢、越不弹 [Header("Upright")] public float uprightStrength = 8f; // 站漂恢复力 public float uprightDamping = 1.2f; [Header("Lay Down (躺漂)")] public float layDownThreshold = 0.15f; // 低于 15% 浸没 → 转入躺漂模式 public float layDownUprightMultiplier = 0.2f; // 躺漂时保留多少站立力度(越小越“翘头”) private Rigidbody rb; private void Awake() { rb = GetComponent(); rb.useGravity = true; // 重心稍微靠上,配合 AddForceAtPosition,才能让“底在水里、头上翘” rb.centerOfMass = new Vector3(0, bobberHeight * 0.4f, 0); } private void FixedUpdate() { ApplyBuoyancy(); ApplyFlowForce(); ApplyOrientationControl(); } void ApplyBuoyancy() { float bottom = rb.position.y; // 原点就是最底部 float depth = waterLevel - bottom; // 浸入深度 = 水面 - 底部 if (depth <= 0f) return; // 完全在水面上方,无浮力 // 当前浸没比例 float submersionRatio = Mathf.Clamp01(depth / bobberHeight); // 基础浮力(和你之前一致) float baseForce = maxBuoyancy * submersionRatio * buoyancyMultiplier; // ⭐ 竖直速度(用你原来习惯的 linearVelocity) float vy = rb.linearVelocity.y; // ⭐ 阻尼项:速度越快,反向力越大 // 下沉很快 → 给一个向上的阻尼(减缓下沉) // 向上很快 → 给一个向下的阻尼(防止弹出水面) float damping = -vy * verticalDamping; // 总的向上力:基础浮力 + 阻尼修正 float totalUpForce = baseForce + damping; // 物理上浮力不能把它“往下按”,所以最少为 0 if (totalUpForce < 0f) totalUpForce = 0f; // ⭐ 浮力作用点 = 浸没体积中心(不动,保持你之前的设计) float clampedDepth = Mathf.Clamp(depth, 0, bobberHeight); float centerY = bottom + clampedDepth * 0.5f; Vector3 buoyancyPoint = new Vector3(rb.position.x, centerY, rb.position.z); rb.AddForceAtPosition(Vector3.up * totalUpForce, buoyancyPoint, ForceMode.Acceleration); // 水中阻力(保留原逻辑) rb.AddForce(-rb.linearVelocity * waterDrag, ForceMode.Acceleration); rb.AddTorque(-rb.angularVelocity * waterAngularDrag, ForceMode.Acceleration); } void ApplyFlowForce() { if (waterFlow.sqrMagnitude <= 0.0001f) return; rb.AddForce((waterFlow - rb.linearVelocity) * 0.3f, ForceMode.Acceleration); } void ApplyOrientationControl() { float bottom = rb.position.y; float depth = waterLevel - bottom; float submersionRatio = Mathf.Clamp01(depth / bobberHeight); Vector3 up = transform.up; Vector3 worldUp = Vector3.up; if (submersionRatio > layDownThreshold) { // 深浸 → 正常站漂 Vector3 uprightTorqueVec = Vector3.Cross(up, worldUp) * uprightStrength - rb.angularVelocity * uprightDamping; rb.AddTorque(uprightTorqueVec, ForceMode.Acceleration); } else { // 浅浸 → “躺漂”,只保留少量站立力,让它自然斜、头上翘 float reduced = uprightStrength * layDownUprightMultiplier; Vector3 uprightTorqueVec = Vector3.Cross(up, worldUp) * reduced - rb.angularVelocity * uprightDamping; rb.AddTorque(uprightTorqueVec, ForceMode.Acceleration); } } }