Files
2026-03-04 10:03:45 +08:00

282 lines
7.0 KiB
C#

using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Collider))]
public class BouncyInteract : MonoBehaviour
{
public enum ModelSourceEnum
{
Collider = 0,
Mesh = 1
}
public ModelSourceEnum VolumeSource = ModelSourceEnum.Mesh;
public Transform OvverideCenterOfMass;
[Range(100f, 1000f)]
public float Density = 450f;
[Range(1f, 6f)]
public int SlicesPerAxisX = 2;
[Range(1f, 6f)]
public int SlicesPerAxisY = 2;
[Range(1f, 6f)]
public int SlicesPerAxisZ = 2;
public bool isConcave;
[Range(2f, 32f)]
public int VoxelsLimit = 16;
public float AngularDrag = 0.25f;
public float Drag = 0.25f;
[Range(0f, 1f)]
public float NormalForce = 0.2f;
public bool DebugForces;
private const float DAMPFER = 0.1f;
private const float WATER_DENSITY = 800f;
private Vector3 localArchimedesForce;
private List<Vector3> voxels;
private Vector3[] currentForces;
private bool isMeshCollider;
private List<Vector3[]> debugForces;
private Rigidbody rigidBody;
private Collider collider;
private float bounceMaxSize;
private void OnEnable()
{
debugForces = new List<Vector3[]>();
rigidBody = GetComponent<Rigidbody>();
collider = GetComponent<Collider>();
Quaternion rotation = base.transform.rotation;
Vector3 position = base.transform.position;
base.transform.rotation = Quaternion.identity;
base.transform.position = Vector3.zero;
Bounds bounds = collider.bounds;
bounceMaxSize = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z);
isMeshCollider = GetComponent<MeshCollider>() != null;
if ((bool)OvverideCenterOfMass)
{
rigidBody.centerOfMass = base.transform.InverseTransformPoint(OvverideCenterOfMass.transform.position);
}
rigidBody.angularDrag = AngularDrag;
rigidBody.drag = Drag;
voxels = SliceIntoVoxels(isMeshCollider && isConcave);
currentForces = new Vector3[voxels.Count];
base.transform.rotation = rotation;
base.transform.position = position;
float num = rigidBody.mass / Density;
WeldPoints(voxels, VoxelsLimit);
float y = 800f * Mathf.Abs(Physics.gravity.y) * num;
localArchimedesForce = new Vector3(0f, y, 0f) / voxels.Count;
}
private void OnDisable()
{
}
private Bounds GetCurrentBounds()
{
Bounds result = default(Bounds);
if (VolumeSource == ModelSourceEnum.Mesh)
{
return GetComponent<Renderer>().bounds;
}
if (VolumeSource == ModelSourceEnum.Collider)
{
MeshCollider component = GetComponent<MeshCollider>();
if (component != null)
{
return component.sharedMesh.bounds;
}
return GetComponent<Collider>().bounds;
}
return result;
}
private List<Vector3> SliceIntoVoxels(bool concave)
{
List<Vector3> list = new List<Vector3>(SlicesPerAxisX * SlicesPerAxisY * SlicesPerAxisZ);
Bounds currentBounds = GetCurrentBounds();
if (concave)
{
MeshCollider component = GetComponent<MeshCollider>();
bool convex = component.convex;
component.convex = false;
for (int i = 0; i < SlicesPerAxisX; i++)
{
for (int j = 0; j < SlicesPerAxisY; j++)
{
for (int k = 0; k < SlicesPerAxisZ; k++)
{
float x = currentBounds.min.x + currentBounds.size.x / (float)SlicesPerAxisX * (0.5f + (float)i);
float y = currentBounds.min.y + currentBounds.size.y / (float)SlicesPerAxisY * (0.5f + (float)j);
float z = currentBounds.min.z + currentBounds.size.z / (float)SlicesPerAxisZ * (0.5f + (float)k);
Vector3 vector = base.transform.InverseTransformPoint(new Vector3(x, y, z));
if (PointIsInsideMeshCollider(component, vector))
{
list.Add(vector);
}
}
}
}
if (list.Count == 0)
{
list.Add(currentBounds.center);
}
component.convex = convex;
}
else
{
for (int l = 0; l < SlicesPerAxisX; l++)
{
for (int m = 0; m < SlicesPerAxisY; m++)
{
for (int n = 0; n < SlicesPerAxisZ; n++)
{
float x2 = currentBounds.min.x + currentBounds.size.x / (float)SlicesPerAxisX * (0.5f + (float)l);
float y2 = currentBounds.min.y + currentBounds.size.y / (float)SlicesPerAxisY * (0.5f + (float)m);
float z2 = currentBounds.min.z + currentBounds.size.z / (float)SlicesPerAxisZ * (0.5f + (float)n);
Vector3 item = base.transform.InverseTransformPoint(new Vector3(x2, y2, z2));
list.Add(item);
}
}
}
}
return list;
}
private static bool PointIsInsideMeshCollider(Collider c, Vector3 p)
{
Vector3[] array = new Vector3[6]
{
Vector3.up,
Vector3.down,
Vector3.left,
Vector3.right,
Vector3.forward,
Vector3.back
};
foreach (Vector3 vector in array)
{
if (!c.Raycast(new Ray(p - vector * 1000f, vector), out var _, 1000f))
{
return false;
}
}
return true;
}
private static void FindClosestPoints(IList<Vector3> list, out int firstIndex, out int secondIndex)
{
float num = float.MaxValue;
float num2 = float.MinValue;
firstIndex = 0;
secondIndex = 1;
for (int i = 0; i < list.Count - 1; i++)
{
for (int j = i + 1; j < list.Count; j++)
{
float num3 = Vector3.Distance(list[i], list[j]);
if (num3 < num)
{
num = num3;
firstIndex = i;
secondIndex = j;
}
if (num3 > num2)
{
num2 = num3;
}
}
}
}
private static void WeldPoints(IList<Vector3> list, int targetCount)
{
if (list.Count > 2 && targetCount >= 2)
{
while (list.Count > targetCount)
{
FindClosestPoints(list, out var firstIndex, out var secondIndex);
Vector3 item = (list[firstIndex] + list[secondIndex]) * 0.5f;
list.RemoveAt(secondIndex);
list.RemoveAt(firstIndex);
list.Add(item);
}
}
}
private void FixedUpdate()
{
if (DebugForces)
{
debugForces.Clear();
}
for (int i = 0; i < voxels.Count; i++)
{
Vector3 vector = base.transform.TransformPoint(voxels[i]);
Vector3 zero = Vector3.zero;
Vector3 vector2 = -rigidBody.GetPointVelocity(vector) * 0.1f * rigidBody.mass;
float num = 0f - vector.y;
if (num > 1f)
{
num = 1f;
}
else if (num < 0f)
{
num = 0f;
vector2 *= 0.2f;
}
zero = vector2 + Mathf.Sqrt(num) * localArchimedesForce;
Vector3 normalized = Vector3.Cross(rhs: 0f * new Vector3(0.01f, 0f, 0f), lhs: 0f * new Vector3(0f, 0f, 0.01f)).normalized;
zero.x += normalized.x * NormalForce * rigidBody.mass;
zero.z += normalized.z * NormalForce * rigidBody.mass;
currentForces[i] = zero * 2f;
rigidBody.AddForceAtPosition(zero, vector);
if (DebugForces)
{
debugForces.Add(new Vector3[2] { vector, zero });
}
}
}
private void OnDrawGizmos()
{
if (!DebugForces || voxels == null || debugForces == null)
{
return;
}
float num = 0.02f * bounceMaxSize;
Gizmos.color = Color.yellow;
foreach (Vector3 voxel in voxels)
{
Gizmos.DrawCube(base.transform.TransformPoint(voxel), new Vector3(num, num, num));
}
Gizmos.color = Color.cyan;
foreach (Vector3[] debugForce in debugForces)
{
Gizmos.DrawCube(debugForce[0], new Vector3(num, num, num));
Gizmos.DrawRay(debugForce[0], debugForce[1] / rigidBody.mass * bounceMaxSize * 0.25f);
}
}
}