223 lines
8.0 KiB
C#
223 lines
8.0 KiB
C#
using System;
|
||
using UnityEngine;
|
||
using WaveHarmonic.Crest;
|
||
|
||
[ExecuteInEditMode]
|
||
[RequireComponent(typeof(Rigidbody))]
|
||
public class Buoyancy : MonoBehaviour
|
||
{
|
||
// public WaterSurface targetSurface;
|
||
public WaterRenderer _water;
|
||
|
||
[Tooltip("物体浮力的近似半径")] public float sphereRadiusApproximation = 0.25f;
|
||
|
||
[Tooltip("指定由其他变形(波浪、涌浪等)引起的运动乘数。")] public float waveForceMultiplier = 1f;
|
||
|
||
[Tooltip("指定由水面水流引起的移动的乘数。")] public float currentSpeedMultiplier = 1f;
|
||
|
||
[Tooltip("指定由介质黏性所引起的阻力的乘数。")] public float dragMultiplier = 1f;
|
||
|
||
public float defaultRigidbodyDrag = 0.1f;
|
||
|
||
public float underwaterRigidbodyAngularDrag = 1f;
|
||
|
||
public float overwaterRigidbodyAngularDrag = 0.05f;
|
||
|
||
[Tooltip("指定表面张力的数值。数值过高时,物体在水面上弹跳的速度会减慢。")]
|
||
public float surfaceTensionDamping = 10f;
|
||
|
||
[Tooltip("启用后,净力会以随机偏移量施加出来,从而产生角速度。")]
|
||
public bool applyForceWithRandomOffset;
|
||
|
||
[Tooltip("启用调试绘制")] public bool drawDebug;
|
||
|
||
private Vector3 currentDirection;
|
||
|
||
|
||
private Vector3 waterPosition;
|
||
|
||
private Vector3 normal;
|
||
|
||
private Vector3 deformationDirection;
|
||
|
||
private Rigidbody rigidbodyComponent;
|
||
|
||
private float h;
|
||
|
||
private float hNormalized;
|
||
|
||
private bool _isEnabled = true;
|
||
|
||
readonly SampleFlowHelper _SampleFlowHelper = new();
|
||
|
||
private void Start()
|
||
{
|
||
rigidbodyComponent = GetComponent<Rigidbody>();
|
||
rigidbodyComponent.useGravity = false;
|
||
rigidbodyComponent.linearDamping = defaultRigidbodyDrag;
|
||
if (_water == null)
|
||
{
|
||
Debug.LogWarning("没有找到水组件");
|
||
}
|
||
}
|
||
|
||
public void EnablePhysics(bool set)
|
||
{
|
||
_isEnabled = set;
|
||
}
|
||
|
||
private void FixedUpdate()
|
||
{
|
||
if (!_isEnabled)
|
||
{
|
||
h = 0f;
|
||
hNormalized = 0f;
|
||
rigidbodyComponent.useGravity = true;
|
||
return;
|
||
}
|
||
|
||
if (_water == null)
|
||
return;
|
||
|
||
rigidbodyComponent.useGravity = false;
|
||
|
||
// 缓存常用值
|
||
Vector3 pos = transform.position;
|
||
Vector3 velocity = rigidbodyComponent.linearVelocity;
|
||
float radius = sphereRadiusApproximation;
|
||
float diameter = 2f * radius;
|
||
|
||
// 1) 当前位置的水面示例图
|
||
FetchWaterSurfaceData(pos, out waterPosition, out normal, out currentDirection);
|
||
|
||
// 水平“波浪推动”方向源自表面法线
|
||
deformationDirection = Vector3.ProjectOnPlane(normal, Vector3.up);
|
||
|
||
// 2) 近似球体的浸没深度(0 至 2R)
|
||
float bottomY = pos.y - radius;
|
||
h = Mathf.Clamp(waterPosition.y - bottomY, 0f, diameter);
|
||
hNormalized = (diameter > 0f) ? (h / diameter) : 0f;
|
||
|
||
// 3) 浸没体积(球冠形) V(h) = πh²/3 * (3R - h)
|
||
float submergedVolume = MathF.PI * h * h / 3f * (3f * radius - h);
|
||
|
||
// 4)角向阻尼从空气状态转变为水状态
|
||
rigidbodyComponent.angularDamping = Mathf.Lerp(
|
||
overwaterRigidbodyAngularDrag,
|
||
underwaterRigidbodyAngularDrag,
|
||
hNormalized
|
||
);
|
||
|
||
// 5) 力(保持与您原始的数学计算/单位一致)
|
||
Vector3 gravityAcceleration = Physics.gravity;
|
||
|
||
// 注意:保持原样(先将加速度和力相加,然后使用加速度模式)
|
||
Vector3 weightVector = rigidbodyComponent.mass * gravityAcceleration;
|
||
Vector3 gravityTerm = Vector3.Lerp(gravityAcceleration, weightVector, hNormalized);
|
||
|
||
// 物理常数(与原文相同)
|
||
const float rhoWater = 997f; // kg/m^3
|
||
const float rhoAir = 0.001293f; // kg/m^3
|
||
const float muAir = 0.0000181f; // Pa·s
|
||
const float muWater = 0.001f; // Pa·s
|
||
const float dragCoefficient = 0.47f;
|
||
|
||
// 浮力: -ρ * V * g
|
||
Vector3 buoyancyTerm = (-rhoWater) * submergedVolume * gravityAcceleration;
|
||
|
||
// 线性粘性阻力(类似斯托克斯效应):6πRμ(-v) -> 通过浸入使空气/水混合
|
||
Vector3 dragAir = MathF.PI * 6f * radius * muAir * (-velocity);
|
||
Vector3 dragWater = MathF.PI * 6f * radius * muWater * (-velocity);
|
||
Vector3 viscousDragTerm = Vector3.Lerp(dragAir, dragWater, hNormalized) * dragMultiplier;
|
||
|
||
// 合力(仍被视为加速度)
|
||
Vector3 netAcceleration = gravityTerm + buoyancyTerm + viscousDragTerm;
|
||
|
||
// 可选的随机偏移量,用于产生角速度
|
||
Vector3 randomOffset = Vector3.zero;
|
||
if (applyForceWithRandomOffset)
|
||
{
|
||
randomOffset = new Vector3(
|
||
UnityEngine.Random.Range(-1f, 1f),
|
||
UnityEngine.Random.Range(-1f, 1f),
|
||
UnityEngine.Random.Range(-1f, 1f)
|
||
) * (radius / 5f);
|
||
}
|
||
|
||
rigidbodyComponent.AddForceAtPosition(
|
||
netAcceleration,
|
||
pos + randomOffset,
|
||
ForceMode.Acceleration
|
||
);
|
||
|
||
// 6) 仅在表面过渡区域(0 < hN < 1)存在额外的力作用。
|
||
if (hNormalized > 0f && hNormalized < 1f)
|
||
{
|
||
Vector3 gravityDir = gravityAcceleration.normalized;
|
||
|
||
// 表面张力阻尼:抵消沿重力方向的速度
|
||
Vector3 verticalVelocity = Vector3.Dot(velocity, gravityDir) * gravityDir;
|
||
Vector3 surfaceTensionAccel = -verticalVelocity * surfaceTensionDamping;
|
||
|
||
rigidbodyComponent.AddForce(surfaceTensionAccel, ForceMode.Acceleration);
|
||
rigidbodyComponent.AddForce(deformationDirection * waveForceMultiplier, ForceMode.Acceleration);
|
||
rigidbodyComponent.AddForce(currentDirection * currentSpeedMultiplier, ForceMode.Acceleration);
|
||
}
|
||
|
||
// 7) 终端速度夹具(公式相同,只是命名不同)
|
||
float area = MathF.PI * radius * radius;
|
||
float g = -gravityAcceleration.y; // positive value
|
||
|
||
float terminalVelocityInAir = Mathf.Sqrt(2f * rigidbodyComponent.mass * g / (rhoAir * area * dragCoefficient));
|
||
float terminalVelocityInWater =
|
||
Mathf.Sqrt(2f * rigidbodyComponent.mass * g / (rhoWater * area * dragCoefficient));
|
||
float terminalVelocity = Mathf.Lerp(terminalVelocityInAir, terminalVelocityInWater, hNormalized);
|
||
|
||
float speed = rigidbodyComponent.linearVelocity.magnitude;
|
||
if (speed > terminalVelocity && speed > 1e-6f)
|
||
{
|
||
rigidbodyComponent.linearVelocity = rigidbodyComponent.linearVelocity / speed * terminalVelocity;
|
||
}
|
||
}
|
||
|
||
private Vector3 FetchWaterSurfaceData(Vector3 point, out Vector3 positionWS, out Vector3 normalWS,
|
||
out Vector3 currentDirectionWS)
|
||
{
|
||
currentDirectionWS = Vector3.zero;
|
||
positionWS = Vector3.zero;
|
||
normalWS = Vector3.up;
|
||
return point;
|
||
}
|
||
|
||
public Vector3 GetCurrentWaterPosition()
|
||
{
|
||
return waterPosition;
|
||
}
|
||
|
||
|
||
public float GetNormalizedHeightOfSphereBelowSurface()
|
||
{
|
||
return hNormalized;
|
||
}
|
||
|
||
private void OnDrawGizmosSelected()
|
||
{
|
||
if (drawDebug)
|
||
{
|
||
Gizmos.color = Color.magenta;
|
||
Gizmos.DrawLine(base.transform.position, base.transform.position + normal);
|
||
Gizmos.color = Color.green;
|
||
Gizmos.DrawLine(base.transform.position, base.transform.position + deformationDirection * 10f);
|
||
Gizmos.color = Color.red;
|
||
Gizmos.DrawLine(base.transform.position, base.transform.position + currentDirection);
|
||
Gizmos.color = Color.yellow;
|
||
Gizmos.DrawSphere(base.transform.position, sphereRadiusApproximation);
|
||
|
||
// 绘制 Rigidbody 的重心点位
|
||
Vector3 centerOfMassWorld = transform.TransformPoint(rigidbodyComponent != null ? rigidbodyComponent.centerOfMass : Vector3.zero);
|
||
Gizmos.color = Color.cyan;
|
||
Gizmos.DrawSphere(centerOfMassWorld, 0.1f);
|
||
Gizmos.DrawLine(centerOfMassWorld, centerOfMassWorld + Vector3.up * 0.5f);
|
||
}
|
||
}
|
||
} |