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 voxels; private Vector3[] currentForces; private bool isMeshCollider; private List debugForces; private Rigidbody rigidBody; private Collider collider; private float bounceMaxSize; private void OnEnable() { debugForces = new List(); rigidBody = GetComponent(); collider = GetComponent(); 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() != 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().bounds; } if (VolumeSource == ModelSourceEnum.Collider) { MeshCollider component = GetComponent(); if (component != null) { return component.sharedMesh.bounds; } return GetComponent().bounds; } return result; } private List SliceIntoVoxels(bool concave) { List list = new List(SlicesPerAxisX * SlicesPerAxisY * SlicesPerAxisZ); Bounds currentBounds = GetCurrentBounds(); if (concave) { MeshCollider component = GetComponent(); 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 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 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); } } }