using System; using System.Collections.Generic; using System.Linq; using UnityEngine; // ReSharper disable once InlineOutVariableDeclaration namespace DestroyIt { /// /// Put this script on a destroyed object that has debris pieces you want to stay connected together with joints. (Note: if the object is a prefab, unpack it first.) /// The joints this script creates will give your destroyed object's debris pieces structural support until they are destroyed or knocked off by force. /// public class StructuralSupport : MonoBehaviour { [Tooltip("This is the maximum distance allowed to make a structural support connection. Reduce it if you're getting pieces that float in the air and defy physics. Increase it if too many pieces aren't connecting when they should be.")] public float maxConnectionDistance = 1.25f; [Tooltip("The force required to break a joint on the structure. Set to -1 for Infinity.")] public float breakForce = 1250f; [Tooltip("The torque required to break a joint on the structure. Set to -1 for Infinity.")] public float breakTorque = 3000f; private class StructuralPiece { public GameObject GameObject { get; set; } public Rigidbody Rigidbody { get; set; } public Vector3 CenterPoint { get; set; } } public void FixedUpdate() { // Inspect all the joints during every physics loop and remove any that have missing connected rigidbodies FixedJoint[] joints = gameObject.GetComponentsInChildren(); bool jointsRemoved = false; for (int i = 0; i < joints.Length; i++) { if (joints[i].connectedBody == null) { Destroy(joints[i]); // remove the joint jointsRemoved = true; } } // If any joints were removed, wake up the rigidbodies on the object if (jointsRemoved) { Rigidbody[] rbodies = gameObject.GetComponentsInChildren(); foreach (Rigidbody rbody in rbodies) rbody.WakeUp(); } } [ExecuteInEditMode] public void AddStructuralSupport() { #if UNITY_EDITOR List pieces = new List(); List otherPieces = new List(); // First, get a list of all rigidbodies on the object List rbodies = gameObject.GetComponentsInChildren().ToList(); // Clear off any old joints on the object so we can create new joint connections. foreach (var comp in gameObject.GetComponentsInChildren()) { if (comp is Joint) DestroyImmediate(comp); } // Next, get the mesh centerpoint of each rigidbody's game object foreach (Rigidbody rbody in rbodies) { Vector3 center = Vector3.zero; foreach (Collider coll in rbody.gameObject.GetComponentsInChildren()) center = center != Vector3.zero ? Vector3.Lerp(center, coll.bounds.center, 0.5f) : coll.bounds.center; pieces.Add(new StructuralPiece { GameObject = rbody.gameObject, Rigidbody = rbody, CenterPoint = center }); otherPieces.Add(new StructuralPiece { GameObject = rbody.gameObject, Rigidbody = rbody, CenterPoint = center }); } // Now, for each piece, try to linecast from the center of it to the center of every other piece. foreach (StructuralPiece piece in pieces) { for (int i = 0; i < otherPieces.Count; i++) { // skip if this piece is trying to linecast to itself. if (piece.GameObject.GetInstanceID() == otherPieces[i].GameObject.GetInstanceID()) continue; // If the connection distance is farther than our threshold will allow, exit. if (Vector3.Distance(piece.CenterPoint, otherPieces[i].CenterPoint) > maxConnectionDistance) continue; // Capture the layer this object is on, and move it to the IgnoreRaycast layer temporarily before doing the linecast, so it ignores itself. int originalLayer = piece.GameObject.layer; int ignoreLayer = LayerMask.NameToLayer("Ignore Raycast"); piece.GameObject.SetLayerRecursively(ignoreLayer); // If we hit a collider while linecasting to the other piece, check it and see if it IS the other piece. If so, that means it's adjacent, and we want to attach a joint connecting the two. RaycastHit hitInfo; if (Physics.Linecast(piece.CenterPoint, otherPieces[i].CenterPoint, out hitInfo)) { if (hitInfo.collider.attachedRigidbody == otherPieces[i].Rigidbody) { //Debug.Log($"{piece.GameObject.name} is connected to {otherPieces[i].GameObject.name}"); for (int j=0; j<8; j++) Debug.DrawLine(piece.CenterPoint, otherPieces[i].CenterPoint, Color.green, 10f); Vector3 midPoint = Vector3.Lerp(piece.CenterPoint, otherPieces[i].CenterPoint, 0.5f); if (breakForce <= -0.1f) breakForce = Single.PositiveInfinity; if (breakTorque <= -0.1f) breakTorque = Single.PositiveInfinity; piece.GameObject.AddStiffJoint(otherPieces[i].Rigidbody, midPoint, Vector3.zero, breakForce, breakTorque); } } // Set the layer on the piece back to what it was originally piece.GameObject.SetLayerRecursively(originalLayer); } // Remove this piece from the "other pieces" to check otherPieces.RemoveAll(x => x.GameObject.GetInstanceID() == piece.GameObject.GetInstanceID()); } #endif } [ExecuteInEditMode] public void RemoveStructuralSupport() { #if UNITY_EDITOR FixedJoint[] joints = gameObject.GetComponentsInChildren(); for (int i = 0; i < joints.Length; i++) DestroyImmediate(joints[i]); // remove the joint #endif } } public static class LayerExtensions { public static void SetLayerRecursively(this GameObject obj, int layer) { obj.layer = layer; foreach (Transform child in obj.transform) child.gameObject.SetLayerRecursively(layer); } } }