首次提交
This commit is contained in:
243
Assets/Scripts/Test/BobberFloating.cs
Normal file
243
Assets/Scripts/Test/BobberFloating.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Test/BobberFloating.cs.meta
Normal file
3
Assets/Scripts/Test/BobberFloating.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c103db8c4b014d87845103a400b1ace5
|
||||
timeCreated: 1772159688
|
||||
24
Assets/Scripts/Test/BobberTest.cs
Normal file
24
Assets/Scripts/Test/BobberTest.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using NBF;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
public class BobberTest : MonoBehaviour
|
||||
{
|
||||
public Rigidbody rb;
|
||||
public FLine line;
|
||||
|
||||
public float lineLength = 1f;
|
||||
public float floatLength = 0.5f;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
line.InitTest(rb);
|
||||
//有浮漂
|
||||
line.Lure.SetJointDistance(floatLength);
|
||||
line.Bobber.SetJointDistance(lineLength - floatLength);
|
||||
line.SetObiRopeStretch(lineLength - floatLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Test/BobberTest.cs.meta
Normal file
3
Assets/Scripts/Test/BobberTest.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3444ef411bcf4f7fa35c03ec7d800ff8
|
||||
timeCreated: 1772525731
|
||||
223
Assets/Scripts/Test/Buoyancy.cs
Normal file
223
Assets/Scripts/Test/Buoyancy.cs
Normal file
@@ -0,0 +1,223 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Test/Buoyancy.cs.meta
Normal file
3
Assets/Scripts/Test/Buoyancy.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f273ba33ebe49cd8b31b92db8288a90
|
||||
timeCreated: 1772356495
|
||||
209
Assets/Scripts/Test/BuoyancyCapsuleSphere.cs
Normal file
209
Assets/Scripts/Test/BuoyancyCapsuleSphere.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
using UnityEngine;
|
||||
|
||||
public interface IWaterProvider
|
||||
{
|
||||
float GetWaterHeight(Vector3 worldPos);
|
||||
Vector3 GetWaterNormal(Vector3 worldPos);
|
||||
Vector3 GetWaterVelocity(Vector3 worldPos);
|
||||
}
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 稳定优先的浮力(只支持 CapsuleCollider / SphereCollider)
|
||||
// /// - 竖直方向:目标吃水深度 + PD 控制(稳定,不抖、不弹飞)
|
||||
// /// - 姿态:Righting Torque 扶正(受 Rigidbody.centerOfMass 影响)
|
||||
// /// - 入水比例:带平滑(避免水面附近开关抖动)
|
||||
// /// </summary>
|
||||
// [DisallowMultipleComponent]
|
||||
// [RequireComponent(typeof(Rigidbody))]
|
||||
// public class BuoyancyCapsuleSphere : MonoBehaviour
|
||||
// {
|
||||
// [Header("Collider (choose one)")] public CapsuleCollider capsule;
|
||||
// public SphereCollider sphere;
|
||||
//
|
||||
// [Header("Water")] public MonoBehaviour waterProviderBehaviour; // 可选
|
||||
// private IWaterProvider waterProvider;
|
||||
//
|
||||
// [Tooltip("没有 provider 时的水面高度")] public float waterLevel = 0f;
|
||||
//
|
||||
// [Header("Density -> Draft")] [Tooltip("水密度 kg/m^3(淡水约1000)")]
|
||||
// public float waterDensity = 1000f;
|
||||
//
|
||||
// [Tooltip("物体等效密度 kg/m^3。越小越浮。浮漂可 80~400 之间调")]
|
||||
// public float objectDensity = 250f;
|
||||
//
|
||||
// [Tooltip("额外浮力比例(手感调整)。1=按密度算")] public float buoyancyScale = 1f;
|
||||
//
|
||||
// [Header("Vertical PD (Stability key)")] [Tooltip("竖直弹簧强度(越大越“顶住”目标吃水)")]
|
||||
// public float verticalKp = 35f;
|
||||
//
|
||||
// [Tooltip("竖直阻尼(越大越不弹、不抖)")] public float verticalKd = 12f;
|
||||
//
|
||||
// [Tooltip("最大向上加速度 m/s^2(防止从高处落下入水被顶飞)")]
|
||||
// public float maxUpAccel = 25f;
|
||||
//
|
||||
// [Tooltip("最大向下加速度 m/s^2(防止强行拉下去造成抖动)")]
|
||||
// public float maxDownAccel = 10f;
|
||||
//
|
||||
// [Header("Submergence smoothing")] [Tooltip("入水比例变化速度(1/s)。越大越快响应,越小越稳")]
|
||||
// public float submergenceSpeed = 8f;
|
||||
//
|
||||
// [Tooltip("水面外的缓冲(m),让浮力更平滑接管")] public float surfaceMargin = 0.01f;
|
||||
//
|
||||
// [Header("Righting (Rotation)")] [Tooltip("扶正强度(把 transform.up 拉向水面法线/世界上)")]
|
||||
// public float rightingKp = 8f;
|
||||
//
|
||||
// [Tooltip("扶正阻尼(抑制旋转抖动)")] public float rightingKd = 3f;
|
||||
//
|
||||
// [Header("Water drag (optional but helpful)")] [Tooltip("入水时额外线阻尼(通过 rb.drag 混合)")]
|
||||
// public float extraLinearDragInWater = 2.5f;
|
||||
//
|
||||
// [Tooltip("入水时额外角阻尼(通过 rb.angularDrag 混合)")]
|
||||
// public float extraAngularDragInWater = 2.0f;
|
||||
//
|
||||
// [Header("Center of Mass")] [Tooltip("本地重心偏移:例如 (0,-0.02,0) 让底部更重、更容易站漂")]
|
||||
// public Vector3 centerOfMassOffset = Vector3.zero;
|
||||
//
|
||||
// private Rigidbody rb;
|
||||
// private float baseDrag, baseAngularDrag;
|
||||
//
|
||||
// // 关键:入水比例必须有“记忆”(滤波),否则水面边界必抖
|
||||
// private float subFiltered = 0f;
|
||||
//
|
||||
// void Reset()
|
||||
// {
|
||||
// rb = GetComponent<Rigidbody>();
|
||||
// rb.useGravity = true;
|
||||
// rb.interpolation = RigidbodyInterpolation.Interpolate;
|
||||
// rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
|
||||
//
|
||||
// capsule = GetComponent<CapsuleCollider>();
|
||||
// sphere = GetComponent<SphereCollider>();
|
||||
// }
|
||||
//
|
||||
// void Awake()
|
||||
// {
|
||||
// rb = GetComponent<Rigidbody>();
|
||||
// rb.centerOfMass = centerOfMassOffset;
|
||||
//
|
||||
// baseDrag = rb.linearDamping;
|
||||
// baseAngularDrag = rb.angularDamping;
|
||||
//
|
||||
// waterProvider = waterProviderBehaviour as IWaterProvider;
|
||||
//
|
||||
// // 只允许一个
|
||||
// if (capsule != null && sphere != null)
|
||||
// sphere = null;
|
||||
// }
|
||||
//
|
||||
// void OnValidate()
|
||||
// {
|
||||
// objectDensity = Mathf.Max(1e-3f, objectDensity);
|
||||
// waterDensity = Mathf.Max(1e-3f, waterDensity);
|
||||
// submergenceSpeed = Mathf.Max(0.1f, submergenceSpeed);
|
||||
// surfaceMargin = Mathf.Max(0f, surfaceMargin);
|
||||
// maxUpAccel = Mathf.Max(0f, maxUpAccel);
|
||||
// maxDownAccel = Mathf.Max(0f, maxDownAccel);
|
||||
// }
|
||||
//
|
||||
// void FixedUpdate()
|
||||
// {
|
||||
// if (!capsule && !sphere) return;
|
||||
//
|
||||
// waterProvider = waterProviderBehaviour as IWaterProvider;
|
||||
//
|
||||
// GetCenterAndHeight(out var centerWorld, out var shapeHeight);
|
||||
//
|
||||
// // 水面信息
|
||||
// float waterH = waterProvider?.GetWaterHeight(centerWorld) ?? waterLevel;
|
||||
// Vector3 waterN = waterProvider?.GetWaterNormal(centerWorld) ?? Vector3.up;
|
||||
// if (waterN.sqrMagnitude < 1e-6f) waterN = Vector3.up;
|
||||
// waterN.Normalize();
|
||||
//
|
||||
// // 当前中心“浸没深度”(>0 表示中心在水下)
|
||||
// float centerDepth = waterH - centerWorld.y;
|
||||
//
|
||||
// // 近似入水比例:centerDepth = -H/2 -> 0; centerDepth = +H/2 -> 1
|
||||
// float rawSub = Mathf.Clamp01((centerDepth + (shapeHeight * 0.5f) + surfaceMargin) /
|
||||
// (shapeHeight + 2f * surfaceMargin));
|
||||
//
|
||||
// // 入水比例滤波(非常关键)
|
||||
// float dt = Time.fixedDeltaTime;
|
||||
// subFiltered = Mathf.MoveTowards(subFiltered, rawSub, submergenceSpeed * dt);
|
||||
//
|
||||
// // 混合拖拽(让水中更稳)
|
||||
// rb.linearDamping = Mathf.Lerp(baseDrag, baseDrag + extraLinearDragInWater, subFiltered);
|
||||
// rb.angularDamping = Mathf.Lerp(baseAngularDrag, baseAngularDrag + extraAngularDragInWater, subFiltered);
|
||||
//
|
||||
// if (subFiltered <= 1e-4f)
|
||||
// return; // 基本没入水,不做任何浮力/扶正
|
||||
//
|
||||
// // 目标吃水比例:理想静态平衡 ≈ objectDensity / waterDensity(<1 才会浮)
|
||||
// float desiredSub = Mathf.Clamp01((objectDensity / waterDensity) * buoyancyScale);
|
||||
//
|
||||
// // 把 desiredSub 转成目标中心深度
|
||||
// // desiredSub=0 -> centerDepthTarget = -H/2(完全出水)
|
||||
// // desiredSub=1 -> centerDepthTarget = +H/2(完全入水)
|
||||
// float centerDepthTarget = desiredSub * shapeHeight - shapeHeight * 0.5f;
|
||||
//
|
||||
// // 竖直 PD:只沿“世界上/重力反方向”控制,最稳
|
||||
// Vector3 up = (-Physics.gravity).sqrMagnitude > 1e-6f ? (-Physics.gravity).normalized : Vector3.up;
|
||||
// float vUp = Vector3.Dot(rb.linearVelocity, up);
|
||||
//
|
||||
// float error = centerDepth - centerDepthTarget; // 深了为正 -> 需要向上推
|
||||
// float accelUp = (-verticalKp * error) - (verticalKd * vUp);
|
||||
//
|
||||
// // 限制上下加速度,避免顶飞或强拉抖动
|
||||
// accelUp = Mathf.Clamp(accelUp, -maxDownAccel, maxUpAccel);
|
||||
//
|
||||
// // 随入水比例渐入(避免水面边界突然接管)
|
||||
// accelUp *= subFiltered;
|
||||
//
|
||||
// // 施加竖直加速度(Acceleration 不受质量影响,更稳定)
|
||||
// rb.AddForce(up * accelUp, ForceMode.Acceleration);
|
||||
//
|
||||
// // 扶正力矩:把物体 up 拉向 waterN(平静水就是 Vector3.up)
|
||||
// // 注意:这个扶正与重心偏移一起工作,会形成“站漂/躺漂”的稳定姿态
|
||||
// Vector3 currentUp = transform.up;
|
||||
// Vector3 axis = Vector3.Cross(currentUp, waterN);
|
||||
//
|
||||
// // 小角度近似:axis 的大小约等于 sin(theta)
|
||||
// // 扶正加速度型扭矩(同样用 Acceleration,减少质量/惯量差带来的抖动)
|
||||
// Vector3 angVel = rb.angularVelocity;
|
||||
// Vector3 torqueAccel = axis * rightingKp - angVel * rightingKd;
|
||||
//
|
||||
// torqueAccel *= subFiltered;
|
||||
//
|
||||
// rb.AddTorque(torqueAccel, ForceMode.Acceleration);
|
||||
// }
|
||||
//
|
||||
// private void GetCenterAndHeight(out Vector3 centerWorld, out float heightWorld)
|
||||
// {
|
||||
// if (sphere)
|
||||
// {
|
||||
// // sphere:center + 半径*缩放(近似取最大缩放)
|
||||
// Transform t = sphere.transform;
|
||||
// centerWorld = t.TransformPoint(sphere.center);
|
||||
//
|
||||
// Vector3 s = t.lossyScale;
|
||||
// float r = sphere.radius * Mathf.Max(Mathf.Abs(s.x), Mathf.Abs(s.y), Mathf.Abs(s.z));
|
||||
// heightWorld = Mathf.Max(1e-6f, r * 2f);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // capsule
|
||||
// Transform ct = capsule.transform;
|
||||
// centerWorld = ct.TransformPoint(capsule.center);
|
||||
//
|
||||
// Vector3 ls = ct.lossyScale;
|
||||
// float sx = Mathf.Abs(ls.x), sy = Mathf.Abs(ls.y), sz = Mathf.Abs(ls.z);
|
||||
//
|
||||
// float heightScale = capsule.direction switch
|
||||
// {
|
||||
// 0 => sx,
|
||||
// 1 => sy,
|
||||
// _ => sz,
|
||||
// };
|
||||
//
|
||||
// heightWorld = Mathf.Max(1e-6f, capsule.height * heightScale);
|
||||
// }
|
||||
// }
|
||||
3
Assets/Scripts/Test/BuoyancyCapsuleSphere.cs.meta
Normal file
3
Assets/Scripts/Test/BuoyancyCapsuleSphere.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bad586cd447d4271b97ce1cf1c81897a
|
||||
timeCreated: 1772460028
|
||||
24
Assets/Scripts/Test/BuoyancyWaterProvider.cs
Normal file
24
Assets/Scripts/Test/BuoyancyWaterProvider.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using UnityEngine;
|
||||
using WaveHarmonic.Crest;
|
||||
|
||||
public class BuoyancyWaterProvider : MonoBehaviour, IWaterProvider
|
||||
{
|
||||
public WaterRenderer Water;
|
||||
readonly SampleFlowHelper _SampleFlowHelper = new();
|
||||
public float waterLevel = 0f;
|
||||
|
||||
public float GetWaterHeight(Vector3 worldPos)
|
||||
{
|
||||
return waterLevel;
|
||||
}
|
||||
|
||||
public Vector3 GetWaterNormal(Vector3 worldPos)
|
||||
{
|
||||
return Vector3.up;
|
||||
}
|
||||
|
||||
public Vector3 GetWaterVelocity(Vector3 worldPos)
|
||||
{
|
||||
return Vector3.zero; // 关键!不要乱给
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Test/BuoyancyWaterProvider.cs.meta
Normal file
3
Assets/Scripts/Test/BuoyancyWaterProvider.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06d107cece7c4cbb9825557923be567f
|
||||
timeCreated: 1772380723
|
||||
247
Assets/Scripts/Test/CapsuleBuoyancyStable.cs
Normal file
247
Assets/Scripts/Test/CapsuleBuoyancyStable.cs
Normal file
@@ -0,0 +1,247 @@
|
||||
using System;
|
||||
using Gaia;
|
||||
using UnityEngine;
|
||||
using WaveHarmonic.Crest;
|
||||
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(Rigidbody), typeof(CapsuleCollider))]
|
||||
public class CapsuleBuoyancyStable : MonoBehaviour
|
||||
{
|
||||
[Header("Buoyancy")] [Tooltip("完全浸没时总浮力 = mass*g*buoyancyScale。>1 更浮。")]
|
||||
public float buoyancyScale = 1.6f;
|
||||
|
||||
[Tooltip("沿胶囊轴向采样点数量(建议 7~11)。")] [Range(3, 15)]
|
||||
public int samplePoints = 9;
|
||||
|
||||
[Tooltip("浸没比例曲线(0=刚碰水, 1=充分在水下)。")] public AnimationCurve submergenceCurve = AnimationCurve.Linear(0, 0, 1, 1);
|
||||
|
||||
[Header("Damping")] [Tooltip("上浮方向速度阻尼(越大越不弹)。")]
|
||||
public float verticalDamping = 3.0f;
|
||||
|
||||
[Tooltip("整体角速度阻尼(只施加一次,不要太大)。")] public float angularDamping = 0.6f;
|
||||
|
||||
[Header("Optional Upright Stabilizer (Recommended for bobber)")] [Tooltip("让胶囊轴向更倾向于对齐世界Up。0=关闭。")]
|
||||
public float uprightSpring = 0.0f;
|
||||
|
||||
[Tooltip("upright 的角速度阻尼。")] public float uprightDamping = 0.5f;
|
||||
|
||||
[Tooltip("胶囊轴向:0=X,1=Y,2=Z(通常 CapsuleCollider.direction 也一样)。")]
|
||||
public int uprightAxis = 1;
|
||||
|
||||
[Header("Water Drag")] public float extraDragInWater = 0.8f;
|
||||
public float extraAngularDragInWater = 0.8f;
|
||||
|
||||
#region Crest5相关信息
|
||||
|
||||
public Transform Water;
|
||||
|
||||
private WaterRenderer _waterRenderer;
|
||||
|
||||
[Tooltip("要瞄准哪一层水的碰撞层。")] [SerializeField]
|
||||
CollisionLayer _Layer = CollisionLayer.AfterAnimatedWaves;
|
||||
|
||||
[Header("波响应")] [Tooltip("用于物理计算的物体宽度。\n\n此值越大,波响应的滤波效果和平滑程度就越高。如果无法对较大波长进行滤波,则应增加 LOD 级别。")] [SerializeField]
|
||||
float _ObjectWidth = 3f;
|
||||
|
||||
readonly SampleFlowHelper _SampleFlowHelper = new();
|
||||
|
||||
/// <summary>
|
||||
/// 查询水面信息点位
|
||||
/// </summary>
|
||||
Vector3[] _QueryPoints;
|
||||
|
||||
Vector3[] _QueryResultDisplacements;
|
||||
Vector3[] _QueryResultVelocities;
|
||||
Vector3[] _QueryResultNormal;
|
||||
|
||||
#endregion
|
||||
|
||||
[Header("Debug")] public bool drawDebug = false;
|
||||
|
||||
Rigidbody _rb;
|
||||
CapsuleCollider _cap;
|
||||
float _baseDrag, _baseAngularDrag;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_rb = GetComponent<Rigidbody>();
|
||||
_cap = GetComponent<CapsuleCollider>();
|
||||
_baseDrag = _rb.linearDamping;
|
||||
_baseAngularDrag = _rb.angularDamping;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
int length = Mathf.Max(3, samplePoints);
|
||||
_QueryPoints = new Vector3[length];
|
||||
_QueryResultDisplacements = new Vector3[length];
|
||||
_QueryResultVelocities = new Vector3[length];
|
||||
_QueryResultNormal = new Vector3[length];
|
||||
if(Water)
|
||||
_waterRenderer = Water.GetComponent<WaterRenderer>();
|
||||
}
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
if (Water)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if (!_waterRenderer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
GetWorldCapsule(out Vector3 a, out Vector3 b, out float radius);
|
||||
|
||||
int n = Mathf.Max(3, samplePoints);
|
||||
float fullBuoyancy = _rb.mass * Physics.gravity.magnitude * buoyancyScale;
|
||||
float perPointMax = fullBuoyancy / n;
|
||||
|
||||
float subSum = 0f;
|
||||
int wetCount = 0;
|
||||
|
||||
for (int i = 0; i < _QueryPoints.Length; i++)
|
||||
{
|
||||
float t = (float)i / (n - 1);
|
||||
Vector3 p = Vector3.Lerp(a, b, t);
|
||||
_QueryPoints[i] = p;
|
||||
}
|
||||
|
||||
|
||||
// 查询
|
||||
var collisions = _waterRenderer.AnimatedWavesLod.Provider;
|
||||
collisions.Query(GetHashCode(), _ObjectWidth, _QueryPoints, _QueryResultDisplacements,
|
||||
_QueryResultNormal, _QueryResultVelocities, _Layer);
|
||||
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
float t = (float)i / (n - 1);
|
||||
Vector3 p = Vector3.Lerp(a, b, t);
|
||||
|
||||
float waterH =
|
||||
_QueryResultDisplacements[i].y + _waterRenderer.SeaLevel; //GaiaConstants.Water.GetWaterHeight(p);
|
||||
float depth = waterH - p.y; // >0 在水下
|
||||
|
||||
float sub = Mathf.InverseLerp(-radius, radius, depth); // 0..1
|
||||
if (sub <= 0f) continue;
|
||||
|
||||
sub = Mathf.Clamp01(submergenceCurve.Evaluate(sub));
|
||||
subSum += sub;
|
||||
wetCount++;
|
||||
|
||||
Vector3 buoyDir = Vector3.up;
|
||||
|
||||
// Vector3 waterVel = GaiaConstants.Water.GetWaterVelocity(p);
|
||||
Vector3 waterVel = _QueryResultVelocities[i];
|
||||
Vector3 pointVel = _rb.GetPointVelocity(p);
|
||||
Vector3 relVel = pointVel - waterVel;
|
||||
|
||||
// 浮力
|
||||
Vector3 buoyForce = buoyDir * (perPointMax * sub);
|
||||
|
||||
// 只阻尼上浮方向速度分量(防弹跳)
|
||||
float vUp = Vector3.Dot(relVel, buoyDir);
|
||||
Vector3 dampForce = -buoyDir * (vUp * verticalDamping * _rb.mass * sub);
|
||||
|
||||
_rb.AddForceAtPosition(buoyForce + dampForce, p, ForceMode.Force);
|
||||
|
||||
if (drawDebug)
|
||||
{
|
||||
Debug.DrawLine(p, p + buoyForce / (_rb.mass * 10f), Color.cyan, 0f, false);
|
||||
Debug.DrawLine(p, p + dampForce / (_rb.mass * 10f), Color.yellow, 0f, false);
|
||||
}
|
||||
}
|
||||
|
||||
float subAvg = (wetCount > 0) ? (subSum / wetCount) : 0f;
|
||||
|
||||
// 角阻尼:只加一次(关键修复点)
|
||||
if (subAvg > 0f)
|
||||
{
|
||||
_rb.AddTorque(-_rb.angularVelocity * (angularDamping * _rb.mass * subAvg), ForceMode.Force);
|
||||
}
|
||||
|
||||
// 可选:upright 稳定器(更像“浮漂自动立起来”)
|
||||
if (subAvg > 0f && uprightSpring > 0f)
|
||||
{
|
||||
Vector3 axisWorld = GetAxisWorld(uprightAxis);
|
||||
Vector3 targetUp = Vector3.up;
|
||||
|
||||
// 误差轴:axisWorld 需要对齐 targetUp(也可反过来按你浮漂模型选)
|
||||
Vector3 errorAxis = Vector3.Cross(axisWorld, targetUp);
|
||||
float errorMag = errorAxis.magnitude;
|
||||
|
||||
if (errorMag > 1e-6f)
|
||||
{
|
||||
errorAxis /= errorMag;
|
||||
|
||||
// “弹簧”力矩 + 阻尼(防止在两个角度间抽动)
|
||||
Vector3 springTorque = errorAxis * (uprightSpring * errorMag * _rb.mass);
|
||||
Vector3 dampTorque = -_rb.angularVelocity * (uprightDamping * _rb.mass);
|
||||
|
||||
_rb.AddTorque((springTorque + dampTorque) * subAvg, ForceMode.Force);
|
||||
}
|
||||
}
|
||||
|
||||
// 入水整体 drag
|
||||
if (subAvg > 0.001f)
|
||||
{
|
||||
_rb.linearDamping = _baseDrag + extraDragInWater * subAvg;
|
||||
_rb.angularDamping = _baseAngularDrag + extraAngularDragInWater * subAvg;
|
||||
}
|
||||
else
|
||||
{
|
||||
_rb.linearDamping = _baseDrag;
|
||||
_rb.angularDamping = _baseAngularDrag;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 GetAxisWorld(int axis)
|
||||
{
|
||||
return axis switch
|
||||
{
|
||||
0 => transform.right,
|
||||
2 => transform.forward,
|
||||
_ => transform.up,
|
||||
};
|
||||
}
|
||||
|
||||
void GetWorldCapsule(out Vector3 a, out Vector3 b, out float radius)
|
||||
{
|
||||
Vector3 lossy = transform.lossyScale;
|
||||
int dir = _cap.direction; // 0=X,1=Y,2=Z
|
||||
|
||||
float scaleAlong = (dir == 0) ? Mathf.Abs(lossy.x) : (dir == 1) ? Mathf.Abs(lossy.y) : Mathf.Abs(lossy.z);
|
||||
|
||||
float scaleR;
|
||||
if (dir == 0) scaleR = Mathf.Max(Mathf.Abs(lossy.y), Mathf.Abs(lossy.z));
|
||||
else if (dir == 1) scaleR = Mathf.Max(Mathf.Abs(lossy.x), Mathf.Abs(lossy.z));
|
||||
else scaleR = Mathf.Max(Mathf.Abs(lossy.x), Mathf.Abs(lossy.y));
|
||||
|
||||
radius = _cap.radius * scaleR;
|
||||
|
||||
Vector3 center = transform.TransformPoint(_cap.center);
|
||||
Vector3 axisWorld = (dir == 0) ? transform.right : (dir == 1) ? transform.up : transform.forward;
|
||||
|
||||
float heightWorld = Mathf.Max(0f, _cap.height * scaleAlong);
|
||||
float cylinderLen = Mathf.Max(0f, heightWorld - 2f * radius);
|
||||
|
||||
Vector3 half = axisWorld * (cylinderLen * 0.5f);
|
||||
a = center - half;
|
||||
b = center + half;
|
||||
}
|
||||
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (drawDebug)
|
||||
{
|
||||
// 绘制 Rigidbody 的重心点位
|
||||
Vector3 centerOfMassWorld = transform.TransformPoint(_rb != null ? _rb.centerOfMass : Vector3.zero);
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawSphere(centerOfMassWorld, 0.1f);
|
||||
Gizmos.DrawLine(centerOfMassWorld, centerOfMassWorld + Vector3.up * 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Test/CapsuleBuoyancyStable.cs.meta
Normal file
3
Assets/Scripts/Test/CapsuleBuoyancyStable.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f91c9d873c83492ca6d5e3e3a67c1760
|
||||
timeCreated: 1772522093
|
||||
Reference in New Issue
Block a user