首次提交

This commit is contained in:
Bob.Song
2026-03-05 18:07:55 +08:00
commit e125bb869e
4534 changed files with 563920 additions and 0 deletions

View File

@@ -0,0 +1,243 @@
using UnityEngine;
using WaveHarmonic.Crest;
namespace NBF
{
public sealed class BobberFloating : MonoBehaviour
{
public WaterRenderer _water;
[SerializeField] Rigidbody _RigidBody;
[Tooltip("要瞄准哪一层水的碰撞层。")] [SerializeField]
CollisionLayer _Layer = CollisionLayer.AfterAnimatedWaves;
[Header("浮力")]
[Header("力强度")]
[Tooltip("对于探测器而言,大致为 100 比 1 的质量与力的比例,以使质心保持在表面附近。对于“对齐法线”,默认值适用于具有默认刚体的默认球体。")]
[SerializeField]
float _BuoyancyForceStrength = 10f;
[Header("扭矩强度")] [Tooltip("使船体方向与水的法线方向一致时所施加扭矩的大小。")] [SerializeField]
float _BuoyancyTorqueStrength = 8f;
[Header("最大力矩")] [Tooltip("将浮力值固定在此数值上。\n\n适用于处理完全浸没的物体。")] [SerializeField]
float _MaximumBuoyancyForce = 100f;
[Header("高度偏移")] [Tooltip("从变换中心到船体底部的高度偏移(如果存在)。\n\n默认值适用于默认球体。该值无需精确测量从中心到底部的距离。")] [SerializeField]
float _CenterToBottomOffset = -1f;
[Tooltip("顺着波浪 “冲浪” 的近似流体动力学效果。")] [Range(0, 1)] [SerializeField]
float _AccelerateDownhill;
[Header("拖拽")] [Tooltip("在水中时使用拖拽功能。\n将此属性添加到刚体所声明的拖拽力上。")] [SerializeField]
Vector3 _Drag = new(2f, 3f, 1f);
[Tooltip("在水中会产生旋转阻力。\n\n将此阻力添加到刚体上已声明的旋转阻力值之上。")] [SerializeField]
float _AngularDrag = 0.2f;
[Tooltip("施加拉力的位置的垂直偏移量。")] [SerializeField]
float _ForceHeightOffset;
[Header("波响应")] [Tooltip("用于物理计算的物体宽度。\n\n此值越大波响应的滤波效果和平滑程度就越高。如果无法对较大波长进行滤波则应增加 LOD 级别。")] [SerializeField]
float _ObjectWidth = 3f;
// Debug
[Space(10)] [SerializeField] DebugFields _Debug = new();
[System.Serializable]
sealed class DebugFields
{
[SerializeField] internal bool _DrawQueries = false;
}
internal const string k_FixedUpdateMarker = "Crest.FloatingObject.FixedUpdate";
static Unity.Profiling.ProfilerMarker s_FixedUpdateMarker = new(k_FixedUpdateMarker);
/// <summary>
/// 这个物体的任何部分是否浸泡在水中?
/// </summary>
public bool InWater { get; private set; }
readonly SampleFlowHelper _SampleFlowHelper = new();
Vector3[] _QueryPoints;
Vector3[] _QueryResultDisplacements;
Vector3[] _QueryResultVelocities;
Vector3[] _QueryResultNormal;
// internal FloatingObjectProbe[] _Probe = new FloatingObjectProbe[] { new() { _Weight = 1f } };
public FloatingObjectProbe[] Probe = new FloatingObjectProbe[] { new() { _Weight = 1f } };
private void Start()
{
if (_RigidBody == null) TryGetComponent(out _RigidBody);
var points = Probe;
// Advanced 还需要为中心增设一个位置。
var length = points.Length;
_QueryPoints = new Vector3[length];
_QueryResultDisplacements = new Vector3[length];
_QueryResultVelocities = new Vector3[length];
_QueryResultNormal = new Vector3[length];
}
private void FixedUpdate()
{
s_FixedUpdateMarker.Begin(this);
var points = Probe;
// 查询
var collisions = _water.AnimatedWavesLod.Provider;
// 更新查询点。
for (var i = 0; i < points.Length; i++)
{
var point = points[i];
_QueryPoints[i] =
transform.TransformPoint(point._Position + new Vector3(0, _RigidBody.centerOfMass.y, 0));
}
_QueryPoints[^1] = transform.position + new Vector3(0, _RigidBody.centerOfMass.y, 0);
collisions.Query(GetHashCode(), _ObjectWidth, _QueryPoints, _QueryResultDisplacements,
_QueryResultNormal, _QueryResultVelocities, _Layer);
//我们可以将表面速度过滤为最近两帧中的较小值。
//存在一种极端情况:当波长被开启 / 关闭时,会产生单帧速度尖峰——
//因为此时水面确实会发生极快的运动。
var surfaceVelocity = _QueryResultVelocities[^1];
_SampleFlowHelper.Sample(transform.position, out var surfaceFlow, minimumLength: _ObjectWidth);
surfaceVelocity += new Vector3(surfaceFlow.x, 0, surfaceFlow.y);
if (_Debug._DrawQueries)
{
Debug.DrawLine(transform.position + 5f * Vector3.up,
transform.position + 5f * Vector3.up + surfaceVelocity, new(1, 1, 1, 0.6f));
}
var height = _QueryResultDisplacements[0].y + _water.SeaLevel;
var bottomDepth = height - transform.position.y - _CenterToBottomOffset;
var normal = _QueryResultNormal[0];
if (_Debug._DrawQueries)
{
var surfPos = transform.position;
surfPos.y = height;
DebugUtility.DrawCross(Debug.DrawLine, surfPos, normal, 1f, Color.red);
}
InWater = bottomDepth > 0f;
if (!InWater)
{
s_FixedUpdateMarker.End();
return;
}
var buoyancy = _BuoyancyForceStrength * bottomDepth * bottomDepth * bottomDepth *
-Physics.gravity.normalized;
if (_MaximumBuoyancyForce < Mathf.Infinity)
{
buoyancy = Vector3.ClampMagnitude(buoyancy, _MaximumBuoyancyForce);
}
_RigidBody.AddForce(buoyancy, ForceMode.Acceleration);
// 在水面上滑行的近似流体动力学
if (_AccelerateDownhill > 0f)
{
_RigidBody.AddForce(_AccelerateDownhill * -Physics.gravity.y * new Vector3(normal.x, 0f, normal.z),
ForceMode.Acceleration);
}
// // 朝向
// // 与水面垂直。默认使用一个垂直方向,但也可以使用单独的垂直方向。
// // 根据船的长度与宽度的比例。这会根据船只的不同而产生不同的旋转效果。
// // dimensions.
// {
// var normalLatitudinal = normal;
//
// if (_Debug._DrawQueries)
// Debug.DrawLine(transform.position, transform.position + 5f * normalLatitudinal, Color.green);
//
// var torqueWidth = Vector3.Cross(transform.up, normalLatitudinal);
// _RigidBody.AddTorque(torqueWidth * _BuoyancyTorqueStrength, ForceMode.Acceleration);
// _RigidBody.AddTorque(-_AngularDrag * _RigidBody.angularVelocity);
// }
// 朝向(改进的扭矩版本)
{
var normalLatitudinal = normal;
if (_Debug._DrawQueries)
Debug.DrawLine(transform.position, transform.position + 5f * normalLatitudinal, Color.green);
// 限制扭矩强度基于物体质量
var massFactor = Mathf.Clamp(_RigidBody.mass / 0.1f, 0.1f, 1f); // 质量越小,扭矩越弱
var adjustedTorqueStrength = _BuoyancyTorqueStrength * massFactor;
var torqueWidth = Vector3.Cross(transform.up, normalLatitudinal);
// 添加最大扭矩限制
var maxTorque = 5f; // 根据需要调整
var finalTorque = Vector3.ClampMagnitude(torqueWidth * adjustedTorqueStrength, maxTorque);
_RigidBody.AddTorque(finalTorque, ForceMode.Acceleration);
_RigidBody.AddTorque(-_AngularDrag * _RigidBody.angularVelocity, ForceMode.Acceleration);
}
// 相对于水进行拖拽操作
if (_Drag != Vector3.zero)
{
var velocityRelativeToWater = _RigidBody.linearVelocity - surfaceVelocity;
var forcePosition = _RigidBody.worldCenterOfMass + _ForceHeightOffset * Vector3.up;
_RigidBody.AddForceAtPosition(
_Drag.x * Vector3.Dot(transform.right, -velocityRelativeToWater) * transform.right, forcePosition,
ForceMode.Acceleration);
_RigidBody.AddForceAtPosition(_Drag.y * Vector3.Dot(Vector3.up, -velocityRelativeToWater) * Vector3.up,
forcePosition, ForceMode.Acceleration);
_RigidBody.AddForceAtPosition(
_Drag.z * Vector3.Dot(transform.forward, -velocityRelativeToWater) * transform.forward,
forcePosition, ForceMode.Acceleration);
}
s_FixedUpdateMarker.End();
}
}
static class DebugUtility
{
public delegate void DrawLine(Vector3 position, Vector3 up, Color color, float duration);
public static void DrawCross(DrawLine draw, Vector3 position, float r, Color color, float duration = 0f)
{
draw(position - Vector3.up * r, position + Vector3.up * r, color, duration);
draw(position - Vector3.right * r, position + Vector3.right * r, color, duration);
draw(position - Vector3.forward * r, position + Vector3.forward * r, color, duration);
}
public static void DrawCross(DrawLine draw, Vector3 position, Vector3 up, float r, Color color,
float duration = 0f)
{
up.Normalize();
var right = Vector3.Normalize(Vector3.Cross(up, Vector3.forward));
var forward = Vector3.Cross(up, right);
draw(position - up * r, position + up * r, color, duration);
draw(position - right * r, position + right * r, color, duration);
draw(position - forward * r, position + forward * r, color, duration);
}
}
}