148 lines
5.0 KiB
C#
148 lines
5.0 KiB
C#
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<Rigidbody>();
|
|
capsule = GetComponent<CapsuleCollider>();
|
|
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);
|
|
}
|
|
} |