using System.Collections.Generic; using UnityEngine; public class Buoyancy : MonoBehaviour { public float Density = 700f; public int SlicesPerAxis = 2; public bool IsConcave; public int VoxelsLimit = 16; public float WaveVelocity = 0.05f; private const float Dampfer = 0.1f; private const float WaterDensity = 1000f; private float voxelHalfHeight; private float localArchimedesForce; private List voxels; private bool isMeshCollider; private List forces; private WaterRipples waterRipples; private void Start() { forces = new List(); Quaternion rotation = base.transform.rotation; Vector3 position = base.transform.position; base.transform.rotation = Quaternion.identity; base.transform.position = Vector3.zero; if (GetComponent() == null) { base.gameObject.AddComponent(); Debug.LogWarning(string.Format("[Buoyancy.cs] Object \"{0}\" had no collider. MeshCollider has been added.", base.name)); } isMeshCollider = GetComponent() != null; Bounds bounds = GetComponent().bounds; if (bounds.size.x < bounds.size.y) { voxelHalfHeight = bounds.size.x; } else { voxelHalfHeight = bounds.size.y; } if (bounds.size.z < voxelHalfHeight) { voxelHalfHeight = bounds.size.z; } voxelHalfHeight /= 2 * SlicesPerAxis; if (GetComponent() == null) { base.gameObject.AddComponent(); Debug.LogWarning(string.Format("[Buoyancy.cs] Object \"{0}\" had no Rigidbody. Rigidbody has been added.", base.name)); } GetComponent().centerOfMass = new Vector3(0f, (0f - bounds.extents.y) * 0f, 0f) + base.transform.InverseTransformPoint(bounds.center); voxels = SliceIntoVoxels(isMeshCollider && IsConcave); base.transform.rotation = rotation; base.transform.position = position; float num = GetComponent().mass / Density; WeldPoints(voxels, VoxelsLimit); float num2 = 1000f * Mathf.Abs(Physics.gravity.y) * num; localArchimedesForce = num2 / (float)voxels.Count; } private List SliceIntoVoxels(bool concave) { List list = new List(SlicesPerAxis * SlicesPerAxis * SlicesPerAxis); if (concave) { MeshCollider component = GetComponent(); bool convex = component.convex; component.convex = false; Bounds bounds = GetComponent().bounds; for (int i = 0; i < SlicesPerAxis; i++) { for (int j = 0; j < SlicesPerAxis; j++) { for (int k = 0; k < SlicesPerAxis; k++) { float x = bounds.min.x + bounds.size.x / (float)SlicesPerAxis * (0.5f + (float)i); float y = bounds.min.y + bounds.size.y / (float)SlicesPerAxis * (0.5f + (float)j); float z = bounds.min.z + bounds.size.z / (float)SlicesPerAxis * (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(bounds.center); } component.convex = convex; } else { Bounds bounds2 = GetComponent().bounds; for (int l = 0; l < SlicesPerAxis; l++) { for (int m = 0; m < SlicesPerAxis; m++) { for (int n = 0; n < SlicesPerAxis; n++) { float x2 = bounds2.min.x + bounds2.size.x / (float)SlicesPerAxis * (0.5f + (float)l); float y2 = bounds2.min.y + bounds2.size.y / (float)SlicesPerAxis * (0.5f + (float)m); float z2 = bounds2.min.z + bounds2.size.z / (float)SlicesPerAxis * (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 }; Vector3[] array2 = array; foreach (Vector3 vector in array2) { RaycastHit hitInfo; if (!c.Raycast(new Ray(p - vector * 1000f, vector), out hitInfo, 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) { int firstIndex; int secondIndex; FindClosestPoints(list, out firstIndex, out secondIndex); Vector3 item = (list[firstIndex] + list[secondIndex]) * 0.5f; list.RemoveAt(secondIndex); list.RemoveAt(firstIndex); list.Add(item); } } } private Vector3 GetNormal(Vector3 a, Vector3 b, Vector3 c) { Vector3 lhs = b - a; Vector3 rhs = c - a; return Vector3.Cross(lhs, rhs).normalized; } private void FixedUpdate() { if (waterRipples == null) { return; } forces.Clear(); int count = voxels.Count; Vector3[] array = new Vector3[count]; for (int i = 0; i < count; i++) { Vector3 position = base.transform.TransformPoint(voxels[i]); array[i] = waterRipples.GetOffsetByPosition(position); } Vector3 normalized = (GetNormal(array[0], array[1], array[2]) * WaveVelocity + Vector3.up).normalized; for (int j = 0; j < count; j++) { Vector3 vector = base.transform.TransformPoint(voxels[j]); float y = array[j].y; if (vector.y - voxelHalfHeight < y) { float num = (y - vector.y) / (2f * voxelHalfHeight) + 0.5f; if (num > 1f) { num = 1f; } else if (num < 0f) { num = 0f; } Vector3 pointVelocity = GetComponent().GetPointVelocity(vector); Vector3 vector2 = -pointVelocity * 0.1f * GetComponent().mass; Vector3 vector3 = vector2 + Mathf.Sqrt(num) * (normalized * localArchimedesForce); GetComponent().AddForceAtPosition(vector3, vector); forces.Add(new Vector3[2] { vector, vector3 }); } } } private void OnDrawGizmos() { if (voxels == null || forces == null) { return; } Gizmos.color = Color.yellow; foreach (Vector3 voxel in voxels) { Gizmos.DrawCube(base.transform.TransformPoint(voxel), new Vector3(0.05f, 0.05f, 0.05f)); } Gizmos.color = Color.cyan; foreach (Vector3[] force in forces) { Gizmos.DrawCube(force[0], new Vector3(0.05f, 0.05f, 0.05f)); Gizmos.DrawLine(force[0], force[0] + force[1] / GetComponent().mass); } } private void OnTriggerEnter(Collider collidedObj) { WaterRipples component = collidedObj.GetComponent(); if (component != null) { waterRipples = component; } } private void OnEnable() { waterRipples = null; } }