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(); objCollider = GetComponent(); 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); } }