Files
Fishing2/Assets/Scripts/Test/BobberBuoyancyStable.cs
2026-02-28 17:45:08 +08:00

130 lines
4.2 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 = 1.5f;
public float rightingDamping = 0.5f;
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;
}
void FixedUpdate()
{
ApplyCenterOfMass();
Bounds b = cap.bounds;
float bottomY = b.min.y;
float topY = b.max.y;
// 用“底部点”判定是否真正入水(必须超过阈值)
float bottomSubmersion = waterLevelY - bottomY; // >0 表示底部在水下
if (bottomSubmersion <= enterWaterDepth)
{
// 认为未入水:不施加浮力,恢复空气阻尼
rb.linearDamping = airLinearDamping;
rb.angularDamping = airAngularDamping;
return;
}
// 进入水中:阻尼随浸入增强
// 这里用一个0~1的平滑权重避免刚入水就“猛顶”
float w = Smooth01((bottomSubmersion - enterWaterDepth) / Mathf.Max(1e-4f, smoothDepth));
rb.linearDamping = airLinearDamping + extraLinearDampingInWater * w;
rb.angularDamping = airAngularDamping + extraAngularDampingInWater * w;
// 垂直速度(用刚体自身速度就够稳定)
float vY = rb.linearVelocity.y;
// 弹簧+阻尼浮力(仅向上)
float forceY = buoyancySpring * bottomSubmersion - buoyancyDamping * vY;
if (forceY < 0f) forceY = 0f;
// 平滑权重:刚入水时逐渐接管
forceY *= w;
// 限制最大上浮加速度(可选)
if (maxUpAcceleration > 0f)
{
float maxForce = rb.mass * maxUpAcceleration;
if (forceY > maxForce) forceY = maxForce;
}
// 浮力作用点:必须放在水面下(否则会出现奇怪力矩)
float buoyY = Mathf.Min(waterLevelY - 0.001f, topY); // 强制在水面下1mm
buoyY = Mathf.Max(buoyY, bottomY); // 不低于底部
Vector3 buoyPoint = new Vector3(b.center.x, buoyY, b.center.z);
rb.AddForceAtPosition(Vector3.up * forceY, buoyPoint, ForceMode.Force);
// 归正扭矩(只在水里生效)
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));
float angVelOnAxis = Vector3.Dot(rb.angularVelocity, axis);
float torque = (rightingTorque * angle - rightingDamping * angVelOnAxis) * w;
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);
// smoothstep
return t * t * (3f - 2f * t);
}
}