209 lines
7.8 KiB
C#
209 lines
7.8 KiB
C#
using System;
|
||
using UnityEngine;
|
||
|
||
[ExecuteInEditMode]
|
||
[RequireComponent(typeof(Rigidbody))]
|
||
public class Buoyancy : MonoBehaviour
|
||
{
|
||
// ====== No water plugin dependency ======
|
||
[Header("Water (No Plugin)")]
|
||
[Tooltip("Fallback flat water level (Y). Set this to match your scene water height for now.")]
|
||
public float waterLevel = 0f;
|
||
|
||
[Tooltip("If false, buoyancy is disabled and rigidbody uses gravity.")]
|
||
public bool waterAvailable = true;
|
||
|
||
// ====== Original fields (kept) ======
|
||
public bool includeDeformation = true;
|
||
|
||
[Tooltip("Approxative radius of object for buoyancy.")]
|
||
public float sphereRadiusApproximation = 0.25f;
|
||
|
||
[Tooltip("Specifies the multiplier for the movement induced by other deformation (waves, swell... etc).")]
|
||
public float waveForceMultiplier = 1f;
|
||
|
||
[Tooltip("Specifies the multiplier for the movement induced by the current of the water surface.")]
|
||
public float currentSpeedMultiplier = 1f;
|
||
|
||
[Tooltip("Specifies the multiplier for the drag forces induced by the viscosity of the mediums.")]
|
||
public float dragMultiplier = 1f;
|
||
|
||
public float defaultRigidbodyDrag = 0.1f;
|
||
|
||
public float underwaterRigidbodyAngularDrag = 1f;
|
||
|
||
public float overwaterRigidbodyAngularDrag = 0.05f;
|
||
|
||
[Tooltip("Specifies the value for surface tension. A high value will stop the object bouncing faster on water.")]
|
||
public float surfaceTensionDamping = 10f;
|
||
|
||
[Tooltip("When enabled, the net force is applied with a random offset to create an angular velocity.")]
|
||
public bool applyForceWithRandomOffset;
|
||
|
||
// 原脚本里这个是 HDRP 的“自定义网格水面高度偏移”,这里保留字段但不再依赖水插件
|
||
[Tooltip("Optional extra offset added to sampled water height. Useful if your water mesh pivot isn't at surface level.")]
|
||
public float waterHeightOffset = 0f;
|
||
|
||
public bool drawDebug;
|
||
|
||
private Vector3 currentDirection;
|
||
private Vector3 A;
|
||
private Vector3 B;
|
||
private Vector3 C;
|
||
private Vector3 waterPosition;
|
||
private Vector3 normal;
|
||
private Vector3 deformationDirection;
|
||
|
||
private Rigidbody rigidbodyComponent;
|
||
|
||
private float h;
|
||
private float hNormalized;
|
||
|
||
private bool _isEnabled = true;
|
||
|
||
private void Start()
|
||
{
|
||
rigidbodyComponent = GetComponent<Rigidbody>();
|
||
rigidbodyComponent.useGravity = false;
|
||
rigidbodyComponent.linearDamping = defaultRigidbodyDrag;
|
||
}
|
||
|
||
public void EnablePhysics(bool set)
|
||
{
|
||
_isEnabled = set;
|
||
}
|
||
|
||
private void FixedUpdate()
|
||
{
|
||
// ✅ 关键:没有水 / 禁用时,回到“用Unity重力”
|
||
if (!_isEnabled || !waterAvailable)
|
||
{
|
||
h = (hNormalized = 0f);
|
||
rigidbodyComponent.useGravity = true;
|
||
return;
|
||
}
|
||
|
||
rigidbodyComponent.useGravity = false;
|
||
|
||
FetchWaterSurfaceData(transform.position, out waterPosition, out normal, out currentDirection);
|
||
|
||
deformationDirection = Vector3.ProjectOnPlane(normal, Vector3.up);
|
||
|
||
h = Mathf.Clamp(
|
||
waterPosition.y - (transform.position.y - sphereRadiusApproximation),
|
||
0f,
|
||
2f * sphereRadiusApproximation
|
||
);
|
||
|
||
hNormalized = h * 1f / (2f * sphereRadiusApproximation);
|
||
|
||
float num = MathF.PI * h * h / 3f * (3f * sphereRadiusApproximation - h);
|
||
|
||
rigidbodyComponent.angularDamping = Mathf.Lerp(overwaterRigidbodyAngularDrag, underwaterRigidbodyAngularDrag, hNormalized);
|
||
|
||
// === 以下保持原脚本单位/写法(尽量贴近原行为) ===
|
||
Vector3 b = rigidbodyComponent.mass * Physics.gravity;
|
||
Vector3 vector = Vector3.Lerp(Physics.gravity, b, hNormalized);
|
||
|
||
float num2 = 997f;
|
||
float num3 = 0.001293f;
|
||
float num4 = 1.81E-05f;
|
||
float num5 = 0.001f;
|
||
float num6 = 0.47f;
|
||
|
||
Vector3 vector2 = (0f - num2) * num * Physics.gravity;
|
||
|
||
Vector3 a = MathF.PI * 6f * sphereRadiusApproximation * num4 * -rigidbodyComponent.linearVelocity;
|
||
Vector3 b2 = MathF.PI * 6f * sphereRadiusApproximation * num5 * -rigidbodyComponent.linearVelocity;
|
||
Vector3 vector3 = Vector3.Lerp(a, b2, hNormalized) * dragMultiplier;
|
||
|
||
float num7 = Mathf.Lerp(
|
||
b: Mathf.Sqrt(2f * rigidbodyComponent.mass * (0f - Physics.gravity.y) /
|
||
(num2 * MathF.PI * Mathf.Pow(sphereRadiusApproximation, 2f) * num6)),
|
||
a: Mathf.Sqrt(2f * rigidbodyComponent.mass * (0f - Physics.gravity.y) /
|
||
(num3 * MathF.PI * Mathf.Pow(sphereRadiusApproximation, 2f) * num6)),
|
||
t: hNormalized
|
||
);
|
||
|
||
Vector3 force = vector + vector2 + vector3;
|
||
|
||
Vector3 vector4 = applyForceWithRandomOffset
|
||
? (new Vector3(
|
||
UnityEngine.Random.Range(-1f, 1f),
|
||
UnityEngine.Random.Range(-1f, 1f),
|
||
UnityEngine.Random.Range(-1f, 1f)
|
||
) * sphereRadiusApproximation / 5f)
|
||
: Vector3.zero;
|
||
|
||
rigidbodyComponent.AddForceAtPosition(force, transform.position + vector4, ForceMode.Acceleration);
|
||
|
||
if (hNormalized > 0f && hNormalized < 1f)
|
||
{
|
||
Vector3 force2 = -(Vector3.Dot(rigidbodyComponent.linearVelocity, Physics.gravity.normalized)
|
||
* Physics.gravity.normalized) * surfaceTensionDamping;
|
||
|
||
rigidbodyComponent.AddForce(force2, ForceMode.Acceleration);
|
||
|
||
if (includeDeformation)
|
||
{
|
||
rigidbodyComponent.AddForce(deformationDirection * waveForceMultiplier, ForceMode.Acceleration);
|
||
rigidbodyComponent.AddForce(currentDirection * currentSpeedMultiplier, ForceMode.Acceleration);
|
||
}
|
||
}
|
||
|
||
if (rigidbodyComponent.linearVelocity.magnitude > num7)
|
||
{
|
||
rigidbodyComponent.linearVelocity = rigidbodyComponent.linearVelocity.normalized * num7;
|
||
}
|
||
}
|
||
|
||
private Vector3 FetchWaterSurfaceData(Vector3 point, out Vector3 positionWS, out Vector3 normalWS, out Vector3 currentDirectionWS)
|
||
{
|
||
// ✅ 插件无关:统一从 GetWaterInfo 拿数据
|
||
GetWaterInfo(point, out float waterHeight, out normalWS, out currentDirectionWS);
|
||
|
||
positionWS = new Vector3(point.x, waterHeight + waterHeightOffset, point.z);
|
||
|
||
if (normalWS == Vector3.zero) normalWS = Vector3.up;
|
||
|
||
return positionWS;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Water provider (no plugin dependency).
|
||
/// 默认:水平水面,高度 = waterLevel
|
||
/// 以后接 Crest5:你只要改这个函数即可。
|
||
/// </summary>
|
||
protected virtual void GetWaterInfo(Vector3 worldPoint, out float waterHeight, out Vector3 waterNormal, out Vector3 waterCurrentDir)
|
||
{
|
||
waterHeight = waterLevel; // 默认水面高度(可在Inspector里调对齐)
|
||
waterNormal = Vector3.up; // 默认法线
|
||
waterCurrentDir = Vector3.zero; // 默认水流方向
|
||
}
|
||
|
||
public Vector3 GetCurrentWaterPosition() => waterPosition;
|
||
|
||
public float GetNormalizedHeightOfSphereBelowSurface() => hNormalized;
|
||
|
||
private void OnDrawGizmosSelected()
|
||
{
|
||
if (!drawDebug) return;
|
||
|
||
Gizmos.color = Color.magenta;
|
||
Gizmos.DrawLine(transform.position, transform.position + normal);
|
||
|
||
Gizmos.color = Color.green;
|
||
Gizmos.DrawLine(transform.position, transform.position + deformationDirection * 10f);
|
||
|
||
Gizmos.color = Color.red;
|
||
Gizmos.DrawLine(transform.position, transform.position + currentDirection);
|
||
|
||
Gizmos.color = Color.yellow;
|
||
Gizmos.DrawSphere(transform.position, sphereRadiusApproximation);
|
||
|
||
Gizmos.color = Color.blue;
|
||
Gizmos.DrawSphere(A, sphereRadiusApproximation / 10f);
|
||
Gizmos.DrawSphere(B, sphereRadiusApproximation / 10f);
|
||
Gizmos.DrawSphere(C, sphereRadiusApproximation / 10f);
|
||
}
|
||
} |