Files
Fishing2NetTest/Assets/Scripts/Test/Buoyancy.cs
2026-03-05 18:07:55 +08:00

223 lines
8.0 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 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);
}
}
}