Files
Fishing2/Assets/Scripts/Test/BobberBuoyancyStable.cs
2026-03-01 08:51:19 +08:00

184 lines
5.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}
}