浮漂测试代码1
This commit is contained in:
@@ -193,7 +193,7 @@ Rigidbody:
|
|||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_GameObject: {fileID: 154764976}
|
m_GameObject: {fileID: 154764976}
|
||||||
serializedVersion: 5
|
serializedVersion: 5
|
||||||
m_Mass: 0.01
|
m_Mass: 0.1
|
||||||
m_LinearDamping: 0
|
m_LinearDamping: 0
|
||||||
m_AngularDamping: 0.05
|
m_AngularDamping: 0.05
|
||||||
m_CenterOfMass: {x: 0, y: 0, z: 0}
|
m_CenterOfMass: {x: 0, y: 0, z: 0}
|
||||||
@@ -250,15 +250,16 @@ MonoBehaviour:
|
|||||||
waterLevelY: 0
|
waterLevelY: 0
|
||||||
enterWaterDepth: 0.003
|
enterWaterDepth: 0.003
|
||||||
smoothDepth: 0.02
|
smoothDepth: 0.02
|
||||||
buoyancySpring: 30
|
buoyancySpring: 100
|
||||||
buoyancyDamping: 8
|
buoyancyDamping: 8
|
||||||
maxUpAcceleration: 0
|
maxUpAcceleration: 0
|
||||||
extraLinearDampingInWater: 2
|
extraLinearDampingInWater: 2
|
||||||
extraAngularDampingInWater: 2
|
extraAngularDampingInWater: 2
|
||||||
driveCenterOfMassFromCapsule: 1
|
driveCenterOfMassFromCapsule: 0
|
||||||
extraCenterOfMassOffset: {x: 0, y: -0.01, z: 0}
|
extraCenterOfMassOffset: {x: 0, y: 0, z: 0}
|
||||||
rightingTorque: 1.5
|
rightingTorque: 1.5
|
||||||
rightingDamping: 0.5
|
rightingDamping: 0.5
|
||||||
|
buoyancyOffset: 0.02
|
||||||
--- !u!1 &203844586
|
--- !u!1 &203844586
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -3419,7 +3420,7 @@ GameObject:
|
|||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
m_NavMeshLayer: 0
|
m_NavMeshLayer: 0
|
||||||
m_StaticEditorFlags: 0
|
m_StaticEditorFlags: 0
|
||||||
m_IsActive: 0
|
m_IsActive: 1
|
||||||
--- !u!1 &1065788238234039155
|
--- !u!1 &1065788238234039155
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ public class BobberBuoyancyStable : MonoBehaviour
|
|||||||
|
|
||||||
[Header("Center Of Mass")]
|
[Header("Center Of Mass")]
|
||||||
public bool driveCenterOfMassFromCapsule = true;
|
public bool driveCenterOfMassFromCapsule = true;
|
||||||
public Vector3 extraCenterOfMassOffset = new Vector3(0f, -0.01f, 0f);
|
public Vector3 extraCenterOfMassOffset = new Vector3(0f, -0.01f, 0f); // 恢复原来的重心下移
|
||||||
|
|
||||||
[Header("Righting")]
|
[Header("Righting")]
|
||||||
public float rightingTorque = 1.5f;
|
public float rightingTorque = 3f; // 适中的归正扭矩
|
||||||
public float rightingDamping = 0.5f;
|
public float rightingDamping = 0.8f;
|
||||||
|
|
||||||
Rigidbody rb;
|
Rigidbody rb;
|
||||||
CapsuleCollider cap;
|
CapsuleCollider cap;
|
||||||
@@ -50,6 +50,7 @@ public class BobberBuoyancyStable : MonoBehaviour
|
|||||||
|
|
||||||
ApplyCenterOfMass();
|
ApplyCenterOfMass();
|
||||||
rb.maxAngularVelocity = 50f;
|
rb.maxAngularVelocity = 50f;
|
||||||
|
// 移除了强制设置物理参数的代码,保留用户在Inspector中的设置
|
||||||
}
|
}
|
||||||
|
|
||||||
void FixedUpdate()
|
void FixedUpdate()
|
||||||
@@ -60,57 +61,76 @@ public class BobberBuoyancyStable : MonoBehaviour
|
|||||||
float bottomY = b.min.y;
|
float bottomY = b.min.y;
|
||||||
float topY = b.max.y;
|
float topY = b.max.y;
|
||||||
|
|
||||||
// 用“底部点”判定是否真正入水(必须超过阈值)
|
// 用"底部点"判定是否真正入水
|
||||||
float bottomSubmersion = waterLevelY - bottomY; // >0 表示底部在水下
|
float bottomSubmersion = waterLevelY - bottomY;
|
||||||
if (bottomSubmersion <= enterWaterDepth)
|
if (bottomSubmersion <= enterWaterDepth)
|
||||||
{
|
{
|
||||||
// 认为未入水:不施加浮力,恢复空气阻尼
|
|
||||||
rb.linearDamping = airLinearDamping;
|
rb.linearDamping = airLinearDamping;
|
||||||
rb.angularDamping = airAngularDamping;
|
rb.angularDamping = airAngularDamping;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 进入水中:阻尼随浸入增强
|
// 进入水中:阻尼随浸入增强
|
||||||
// 这里用一个0~1的平滑权重,避免刚入水就“猛顶”
|
|
||||||
float w = Smooth01((bottomSubmersion - enterWaterDepth) / Mathf.Max(1e-4f, smoothDepth));
|
float w = Smooth01((bottomSubmersion - enterWaterDepth) / Mathf.Max(1e-4f, smoothDepth));
|
||||||
|
|
||||||
rb.linearDamping = airLinearDamping + extraLinearDampingInWater * w;
|
rb.linearDamping = airLinearDamping + extraLinearDampingInWater * w;
|
||||||
rb.angularDamping = airAngularDamping + extraAngularDampingInWater * w;
|
rb.angularDamping = airAngularDamping + extraAngularDampingInWater * w;
|
||||||
|
|
||||||
// 垂直速度(用刚体自身速度就够稳定)
|
// 关键修正:正确的浮力计算
|
||||||
float vY = rb.linearVelocity.y;
|
float volume = Mathf.PI * cap.radius * cap.radius * cap.height;
|
||||||
|
float submergedVolume = volume * Mathf.Clamp01(bottomSubmersion / cap.height);
|
||||||
// 弹簧+阻尼浮力(仅向上)
|
|
||||||
float forceY = buoyancySpring * bottomSubmersion - buoyancyDamping * vY;
|
// 浮力 = 排开液体重量 = 体积 × 密度 × 重力
|
||||||
if (forceY < 0f) forceY = 0f;
|
float buoyantForce = submergedVolume * 1000f * 9.81f;
|
||||||
|
|
||||||
// 平滑权重:刚入水时逐渐接管
|
// 物体重量
|
||||||
forceY *= w;
|
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)
|
if (maxUpAcceleration > 0f)
|
||||||
{
|
{
|
||||||
float maxForce = rb.mass * maxUpAcceleration;
|
float maxForce = rb.mass * maxUpAcceleration;
|
||||||
if (forceY > maxForce) forceY = maxForce;
|
if (totalForce > maxForce) totalForce = maxForce;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 浮力作用点:必须放在水面下(否则会出现奇怪力矩)
|
// 浮力作用点
|
||||||
float buoyY = Mathf.Min(waterLevelY - 0.001f, topY); // 强制在水面下1mm
|
float buoyY = Mathf.Min(waterLevelY - 0.001f, topY);
|
||||||
buoyY = Mathf.Max(buoyY, bottomY); // 不低于底部
|
buoyY = Mathf.Max(buoyY, bottomY);
|
||||||
Vector3 buoyPoint = new Vector3(b.center.x, buoyY, b.center.z);
|
Vector3 buoyPoint = new Vector3(b.center.x, buoyY, b.center.z);
|
||||||
|
|
||||||
rb.AddForceAtPosition(Vector3.up * forceY, buoyPoint, ForceMode.Force);
|
rb.AddForceAtPosition(Vector3.up * totalForce, buoyPoint, ForceMode.Force);
|
||||||
|
|
||||||
// 归正扭矩(只在水里生效)
|
// 简化的归正扭矩系统
|
||||||
|
SimpleRightingSystem(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleRightingSystem(float weight)
|
||||||
|
{
|
||||||
Vector3 up = transform.up;
|
Vector3 up = transform.up;
|
||||||
Vector3 axis = Vector3.Cross(up, Vector3.up);
|
Vector3 axis = Vector3.Cross(up, Vector3.up);
|
||||||
float mag = axis.magnitude;
|
float mag = axis.magnitude;
|
||||||
|
|
||||||
if (mag > 1e-4f)
|
if (mag > 1e-4f)
|
||||||
{
|
{
|
||||||
axis /= mag;
|
axis /= mag;
|
||||||
float angle = Mathf.Asin(Mathf.Clamp(mag, -1f, 1f));
|
float angle = Mathf.Asin(Mathf.Clamp(mag, -1f, 1f)) * Mathf.Rad2Deg;
|
||||||
float angVelOnAxis = Vector3.Dot(rb.angularVelocity, axis);
|
float angVelOnAxis = Vector3.Dot(rb.angularVelocity, axis);
|
||||||
float torque = (rightingTorque * angle - rightingDamping * angVelOnAxis) * w;
|
|
||||||
|
// 归正扭矩
|
||||||
|
float torque = (rightingTorque * angle - rightingDamping * angVelOnAxis) * weight;
|
||||||
rb.AddTorque(axis * torque, ForceMode.Acceleration);
|
rb.AddTorque(axis * torque, ForceMode.Acceleration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,7 +144,40 @@ public class BobberBuoyancyStable : MonoBehaviour
|
|||||||
static float Smooth01(float t)
|
static float Smooth01(float t)
|
||||||
{
|
{
|
||||||
t = Mathf.Clamp01(t);
|
t = Mathf.Clamp01(t);
|
||||||
// smoothstep
|
|
||||||
return t * t * (3f - 2f * 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
155
Assets/Scripts/Test/BobberBuoyancyStable_MultiPoint.cs
Normal file
155
Assets/Scripts/Test/BobberBuoyancyStable_MultiPoint.cs
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
[RequireComponent(typeof(Rigidbody))]
|
||||||
|
[RequireComponent(typeof(CapsuleCollider))]
|
||||||
|
public class BobberBuoyancyStable_MultiPoint : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("Water")]
|
||||||
|
public float waterLevelY = 0f;
|
||||||
|
public float enterWaterDepth = 0.003f;
|
||||||
|
public float smoothDepth = 0.02f;
|
||||||
|
|
||||||
|
[Header("Per-Point Buoyancy Spring (IMPORTANT)")]
|
||||||
|
[Tooltip("每个采样点的弹簧系数(N/m)。总浮力刚度≈ringPoints * perPointSpring")]
|
||||||
|
public float perPointSpring = 60f;
|
||||||
|
|
||||||
|
[Tooltip("每个采样点的阻尼(N·s/m),用点的竖直速度做阻尼")]
|
||||||
|
public float perPointDamping = 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;
|
||||||
|
|
||||||
|
[Tooltip("想更容易躺漂:把y调成正数(上移重心)。想更站漂:y调成负数(下移重心)。")]
|
||||||
|
public Vector3 extraCenterOfMassOffset = new Vector3(0f, 0.00f, 0f);
|
||||||
|
|
||||||
|
[Header("Sample Ring")]
|
||||||
|
[Range(4, 12)] public int ringPoints = 4;
|
||||||
|
[Range(0.2f, 1.2f)] public float ringRadiusScale = 0.9f;
|
||||||
|
|
||||||
|
[Tooltip("采样环离最低点的高度(米)。0=贴底;建议 0.001~0.003")]
|
||||||
|
public float ringLiftFromBottom = 0.0015f;
|
||||||
|
|
||||||
|
[Header("Righting (建议先关)")]
|
||||||
|
public float rightingTorque = 0f;
|
||||||
|
public float rightingDamping = 0f;
|
||||||
|
|
||||||
|
Rigidbody rb;
|
||||||
|
CapsuleCollider cap;
|
||||||
|
|
||||||
|
float airLinearDamping;
|
||||||
|
float airAngularDamping;
|
||||||
|
|
||||||
|
void Awake()
|
||||||
|
{
|
||||||
|
rb = GetComponent<Rigidbody>();
|
||||||
|
cap = GetComponent<CapsuleCollider>();
|
||||||
|
|
||||||
|
airLinearDamping = rb.linearDamping;
|
||||||
|
airAngularDamping = rb.angularDamping;
|
||||||
|
|
||||||
|
ApplyCenterOfMass();
|
||||||
|
rb.maxAngularVelocity = 50f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedUpdate()
|
||||||
|
{
|
||||||
|
ApplyCenterOfMass();
|
||||||
|
|
||||||
|
// === 1) 用胶囊几何算 bottom/top(不使用 cap.bounds) ===
|
||||||
|
// 注意:这里假设 Transform 的缩放是等比或至少 x/z 缩放差不多。
|
||||||
|
Vector3 centerW = transform.TransformPoint(cap.center);
|
||||||
|
|
||||||
|
float scaleY = Mathf.Abs(transform.lossyScale.y);
|
||||||
|
float scaleX = Mathf.Abs(transform.lossyScale.x);
|
||||||
|
float scaleZ = Mathf.Abs(transform.lossyScale.z);
|
||||||
|
float radiusW = cap.radius * Mathf.Max(scaleX, scaleZ);
|
||||||
|
float halfH_W = (cap.height * 0.5f) * scaleY;
|
||||||
|
|
||||||
|
float bottomY = centerW.y - halfH_W;
|
||||||
|
float topY = centerW.y + halfH_W;
|
||||||
|
|
||||||
|
// 用“最低点”判定入水
|
||||||
|
float bottomSub = waterLevelY - bottomY;
|
||||||
|
if (bottomSub <= enterWaterDepth)
|
||||||
|
{
|
||||||
|
rb.linearDamping = airLinearDamping;
|
||||||
|
rb.angularDamping = airAngularDamping;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float w = Smooth01((bottomSub - enterWaterDepth) / Mathf.Max(1e-4f, smoothDepth));
|
||||||
|
|
||||||
|
rb.linearDamping = airLinearDamping + extraLinearDampingInWater * w;
|
||||||
|
rb.angularDamping = airAngularDamping + extraAngularDampingInWater * w;
|
||||||
|
|
||||||
|
// === 2) 在胶囊底部附近放一圈采样点(姿态力矩来自这里) ===
|
||||||
|
// 环中心:在最低点上抬一点(ringLiftFromBottom)
|
||||||
|
float ringY = bottomY + ringLiftFromBottom;
|
||||||
|
Vector3 ringCenter = new Vector3(centerW.x, ringY, centerW.z);
|
||||||
|
|
||||||
|
float ringR = radiusW * ringRadiusScale;
|
||||||
|
|
||||||
|
// 每点加速限制
|
||||||
|
float maxForcePerPoint = float.PositiveInfinity;
|
||||||
|
if (maxUpAcceleration > 0f)
|
||||||
|
maxForcePerPoint = (rb.mass * maxUpAcceleration) / ringPoints;
|
||||||
|
|
||||||
|
for (int i = 0; i < ringPoints; i++)
|
||||||
|
{
|
||||||
|
float a = (i / (float)ringPoints) * Mathf.PI * 2f;
|
||||||
|
|
||||||
|
// 用世界方向的 right/forward(随姿态旋转)
|
||||||
|
Vector3 offset = (transform.right * Mathf.Cos(a) + transform.forward * Mathf.Sin(a)) * ringR;
|
||||||
|
Vector3 p = ringCenter + offset;
|
||||||
|
|
||||||
|
float sub = waterLevelY - p.y;
|
||||||
|
if (sub <= enterWaterDepth) continue;
|
||||||
|
|
||||||
|
float pw = Smooth01((sub - enterWaterDepth) / Mathf.Max(1e-4f, smoothDepth)) * w;
|
||||||
|
|
||||||
|
float vY = rb.GetPointVelocity(p).y;
|
||||||
|
float fY = perPointSpring * sub - perPointDamping * vY;
|
||||||
|
if (fY < 0f) fY = 0f;
|
||||||
|
fY *= pw;
|
||||||
|
|
||||||
|
if (fY > maxForcePerPoint) fY = maxForcePerPoint;
|
||||||
|
|
||||||
|
rb.AddForceAtPosition(Vector3.up * fY, p, ForceMode.Force);
|
||||||
|
}
|
||||||
|
|
||||||
|
// === 3) 可选:归正(建议最后再加) ===
|
||||||
|
if (rightingTorque > 0f)
|
||||||
|
{
|
||||||
|
Vector3 axis = Vector3.Cross(transform.up, Vector3.up);
|
||||||
|
float mag = axis.magnitude;
|
||||||
|
if (mag > 1e-5f)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
return t * t * (3f - 2f * t);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d18347d6a7a44aa8be1ea374a3d9067d
|
||||||
|
timeCreated: 1772295188
|
||||||
Reference in New Issue
Block a user