184 lines
5.7 KiB
C#
184 lines
5.7 KiB
C#
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<Rigidbody>();
|
||
cap = GetComponent<CapsuleCollider>();
|
||
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);
|
||
}
|
||
}
|
||
}
|