提交测试脚本

This commit is contained in:
2026-03-02 00:03:29 +08:00
parent 7712c73be2
commit f07a52f2f0
12 changed files with 1450 additions and 117 deletions

View File

@@ -0,0 +1,388 @@
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Collider))]
public class Buoyancy2 : MonoBehaviour
{
[Header("物理参数")] [SerializeField] private float waterDensity = 1000f; // 水的密度 (kg/m³)
[SerializeField] private float gravity = 9.81f; // 重力加速度 (m/s²)
[SerializeField] private float buoyancyMultiplier = 1f; // 浮力倍数
[Header("阻尼设置")] [SerializeField] private float linearDragInWater = 1f; // 水中线性阻尼
[SerializeField] private float angularDragInWater = 0.5f; // 水中角阻尼
[SerializeField] private float airLinearDrag = 0.1f; // 空气中线性阻尼
[SerializeField] private float airAngularDrag = 0.05f; // 空气中角阻尼
[Header("水面设置")] [SerializeField] private float waterSurfaceLevel = 0f; // 水面高度
[SerializeField] private bool autoDetectWaterLevel = true; // 自动检测水面高度
[Header("采样精度")] [SerializeField] private int sampleResolution = 5; // 采样分辨率 (每维采样点数)
[SerializeField] private bool useAdaptiveSampling = true; // 使用自适应采样
[SerializeField] private int maxSamples = 125; // 最大采样点数
[Header("稳定性控制")] [SerializeField] private float stabilityFactor = 0.3f; // 稳定性因子
[SerializeField] private float rightingTorqueStrength = 2f; // 扶正扭矩强度
[SerializeField] private bool enableMetacentricStability = true; // 启用稳心稳定性
[Header("调试显示")] [SerializeField] private bool showDebugInfo = false;
[SerializeField] private bool drawGizmos = true;
[SerializeField] private Color immersedColor = Color.blue;
[SerializeField] private Color surfaceColor = Color.cyan;
// 私有变量
private Rigidbody rb;
private Collider objCollider;
private Vector3[] samplePoints;
private Vector3 centerOfBuoyancy; // 浮心位置
private Vector3 centerOfGravity; // 重心位置
private float totalVolume; // 总体积 (m³)
private float immersedVolume; // 浸入体积 (m³)
private float immersedRatio; // 浸入比例
private Vector3 metacenter; // 稳心位置
private bool isPartiallySubmerged;
// 缓存值
private Vector3 lastPosition;
private Quaternion lastRotation;
private float lastWaterLevel;
void Start()
{
InitializeComponents();
CalculatePhysicalProperties();
InitializeSamplingGrid();
ValidateSetup();
}
void FixedUpdate()
{
UpdateWaterLevel();
CalculateBuoyancy();
ApplyForces();
UpdateDrag();
ApplyStabilityCorrection();
}
void InitializeComponents()
{
rb = GetComponent<Rigidbody>();
objCollider = GetComponent<Collider>();
if (rb == null)
{
Debug.LogError($"{gameObject.name}: 需要 Rigidbody 组件!", this);
enabled = false;
return;
}
if (objCollider == null)
{
Debug.LogError($"{gameObject.name}: 需要 Collider 组件!", this);
enabled = false;
return;
}
// 保存初始重心位置
centerOfGravity = rb.centerOfMass;
}
void CalculatePhysicalProperties()
{
// 计算物体的真实体积
totalVolume = CalculateRealVolume();
if (showDebugInfo)
{
Debug.Log(
$"{gameObject.name} - 体积: {totalVolume:F4} m³, 质量: {rb.mass:F2} kg, 密度: {rb.mass / totalVolume:F2} kg/m³");
}
}
float CalculateRealVolume()
{
// 根据碰撞器类型精确计算体积
if (objCollider is BoxCollider box)
{
Vector3 size = Vector3.Scale(box.size, transform.lossyScale);
return size.x * size.y * size.z;
}
else if (objCollider is SphereCollider sphere)
{
float radius = sphere.radius *
Mathf.Max(transform.lossyScale.x, transform.lossyScale.y, transform.lossyScale.z);
return (4f / 3f) * Mathf.PI * Mathf.Pow(radius, 3);
}
else if (objCollider is CapsuleCollider capsule)
{
float radius = capsule.radius * Mathf.Max(transform.lossyScale.x, transform.lossyScale.z);
float height = capsule.height * transform.lossyScale.y;
return Mathf.PI * radius * radius * height;
}
else if (objCollider is MeshCollider mesh)
{
// 对于网格碰撞器,使用包围盒近似计算
Bounds bounds = mesh.bounds;
Vector3 size = Vector3.Scale(bounds.size, transform.lossyScale);
return size.x * size.y * size.z * 0.6f; // 乘以填充系数
}
else
{
// 其他类型使用包围盒
Bounds bounds = objCollider.bounds;
return bounds.size.x * bounds.size.y * bounds.size.z;
}
}
void InitializeSamplingGrid()
{
int resolution = sampleResolution;
// 根据物体大小自适应调整采样精度
if (useAdaptiveSampling)
{
float objectSize = objCollider.bounds.size.magnitude;
resolution = Mathf.Clamp(Mathf.RoundToInt(objectSize * 10), 3, 8);
}
// 限制最大采样点数
while (resolution * resolution * resolution > maxSamples && resolution > 2)
{
resolution--;
}
int totalSamples = resolution * resolution * resolution;
samplePoints = new Vector3[totalSamples];
int index = 0;
for (int x = 0; x < resolution; x++)
{
for (int y = 0; y < resolution; y++)
{
for (int z = 0; z < resolution; z++)
{
// 生成标准化坐标 [-0.5, 0.5]
float nx = ((float)x / (resolution - 1)) - 0.5f;
float ny = ((float)y / (resolution - 1)) - 0.5f;
float nz = ((float)z / (resolution - 1)) - 0.5f;
samplePoints[index] = new Vector3(nx, ny, nz);
index++;
}
}
}
if (showDebugInfo)
{
Debug.Log($"{gameObject.name} - 采样点数: {totalSamples}, 分辨率: {resolution}");
}
}
void UpdateWaterLevel()
{
if (autoDetectWaterLevel)
{
// 可以在这里添加自动检测水面高度的逻辑
// 例如射线检测或其他方法
}
}
void CalculateBuoyancy()
{
immersedVolume = 0f;
centerOfBuoyancy = Vector3.zero;
int immersedCount = 0;
Bounds bounds = objCollider.bounds;
Vector3 boundsCenter = bounds.center;
Vector3 boundsExtents = bounds.extents;
foreach (Vector3 normalizedPoint in samplePoints)
{
// 将标准化坐标转换为世界坐标
Vector3 worldPoint = boundsCenter + Vector3.Scale(normalizedPoint, boundsExtents * 2f);
if (worldPoint.y < waterSurfaceLevel)
{
immersedVolume += 1f;
centerOfBuoyancy += worldPoint;
immersedCount++;
}
}
// 计算浸入比例
immersedRatio = (float)immersedCount / samplePoints.Length;
// 计算实际浸入体积
immersedVolume = totalVolume * immersedRatio;
// 计算浮心位置
if (immersedCount > 0)
{
centerOfBuoyancy /= immersedCount;
}
else
{
centerOfBuoyancy = rb.worldCenterOfMass;
}
isPartiallySubmerged = immersedRatio > 0f && immersedRatio < 1f;
}
void ApplyForces()
{
if (immersedRatio <= 0f) return;
// 计算浮力: F = ρ × V × g
float buoyancyForceMagnitude = waterDensity * immersedVolume * gravity * buoyancyMultiplier;
// 浮力方向始终向上
Vector3 buoyancyForce = Vector3.up * buoyancyForceMagnitude;
// 在浮心位置施加浮力
rb.AddForceAtPosition(buoyancyForce, centerOfBuoyancy, ForceMode.Force);
// 添加由于浮力分布不均产生的扭矩
if (isPartiallySubmerged)
{
Vector3 buoyancyTorque = Vector3.Cross(centerOfBuoyancy - rb.worldCenterOfMass, buoyancyForce);
rb.AddTorque(buoyancyTorque * stabilityFactor, ForceMode.Force);
}
if (showDebugInfo && immersedRatio > 0.1f)
{
Debug.DrawRay(centerOfBuoyancy, buoyancyForce * 0.01f, Color.blue, Time.fixedDeltaTime);
}
}
void UpdateDrag()
{
// 根据浸入程度插值阻尼系数
float currentLinearDrag = Mathf.Lerp(airLinearDrag, linearDragInWater, immersedRatio);
float currentAngularDrag = Mathf.Lerp(airAngularDrag, angularDragInWater, immersedRatio);
rb.linearDamping = currentLinearDrag;
rb.angularDamping = currentAngularDrag;
}
void ApplyStabilityCorrection()
{
if (!enableMetacentricStability || immersedRatio <= 0f) return;
// 计算稳心位置(简化模型)
CalculateMetacenter();
// 应用扶正扭矩
if (isPartiallySubmerged)
{
Vector3 rightingArm = metacenter - rb.worldCenterOfMass;
Vector3 restoringTorque = Vector3.Cross(rightingArm, Vector3.down) * rightingTorqueStrength * immersedRatio;
rb.AddTorque(restoringTorque, ForceMode.Force);
}
}
void CalculateMetacenter()
{
// 简化的稳心计算:假设稳心在浮心上方一定距离
float metacentricHeight = Mathf.Max(objCollider.bounds.size.y * 0.3f, 0.1f);
metacenter = centerOfBuoyancy + Vector3.up * metacentricHeight;
}
void ValidateSetup()
{
// 检查物体密度是否合理
float objectDensity = rb.mass / totalVolume;
if (objectDensity < waterDensity * 0.1f)
{
Debug.LogWarning($"{gameObject.name}: 物体密度过低 ({objectDensity:F2} kg/m³),可能导致异常行为");
}
else if (objectDensity > waterDensity * 3f)
{
Debug.LogWarning($"{gameObject.name}: 物体密度过高 ({objectDensity:F2} kg/m³),可能快速沉没");
}
}
// 公共接口方法
public float GetImmersedRatio() => immersedRatio;
public float GetImmersedVolume() => immersedVolume;
public Vector3 GetCenterOfBuoyancy() => centerOfBuoyancy;
public bool IsSubmerged() => immersedRatio >= 0.99f;
public bool IsFloating() => immersedRatio > 0f && immersedRatio < 0.99f;
public void SetWaterLevel(float level)
{
waterSurfaceLevel = level;
autoDetectWaterLevel = false;
}
public void SetBuoyancyMultiplier(float multiplier)
{
buoyancyMultiplier = Mathf.Clamp(multiplier, 0.1f, 5f);
}
void OnDrawGizmosSelected()
{
if (!drawGizmos || !Application.isPlaying) return;
// 绘制浮心
Gizmos.color = Color.blue;
Gizmos.DrawSphere(centerOfBuoyancy, 0.05f);
// 绘制重心
if (rb != null)
{
Gizmos.color = Color.red;
Gizmos.DrawSphere(rb.worldCenterOfMass, 0.05f);
}
// 绘制稳心
if (enableMetacentricStability)
{
Gizmos.color = Color.green;
Gizmos.DrawSphere(metacenter, 0.03f);
}
// 绘制采样点
if (samplePoints != null && objCollider != null)
{
Bounds bounds = objCollider.bounds;
Vector3 boundsCenter = bounds.center;
Vector3 boundsExtents = bounds.extents;
foreach (Vector3 normalizedPoint in samplePoints)
{
Vector3 worldPoint = boundsCenter + Vector3.Scale(normalizedPoint, boundsExtents * 2f);
if (worldPoint.y < waterSurfaceLevel)
{
Gizmos.color = immersedColor;
}
else
{
Gizmos.color = surfaceColor;
}
Gizmos.DrawSphere(worldPoint, 0.01f);
}
}
// 绘制水面线
if (Camera.current != null)
{
Vector3 cameraPos = Camera.current.transform.position;
Vector3 waterCenter = new Vector3(cameraPos.x, waterSurfaceLevel, cameraPos.z);
Vector3 waterSize = new Vector3(10f, 0f, 10f);
Gizmos.color = Color.cyan;
Gizmos.DrawWireCube(waterCenter, waterSize);
}
}
void OnValidate()
{
// 参数验证
sampleResolution = Mathf.Clamp(sampleResolution, 2, 10);
buoyancyMultiplier = Mathf.Clamp(buoyancyMultiplier, 0.1f, 5f);
stabilityFactor = Mathf.Clamp(stabilityFactor, 0f, 1f);
rightingTorqueStrength = Mathf.Clamp(rightingTorqueStrength, 0f, 10f);
}
}