using System.Collections; using System.Collections.Generic; using UnityEngine; public class Explosion3DTestController : MonoBehaviour { [Header("Trigger")] [SerializeField] private bool enableDebugKey = true; [SerializeField] private KeyCode triggerKey = KeyCode.Alpha0; [Header("Target")] [SerializeField] private GameObject intactObject; [SerializeField] private GameObject fracturedPrefab; [SerializeField] private Transform spawnRoot; [SerializeField] private bool disableIntactObjectOnExplode = true; [SerializeField] private bool destroyPreviousFractureInstance = true; [Header("Explosion Force")] [SerializeField] private float explosionForce = 2.5f; [SerializeField] private float explosionRadius = 1.25f; [SerializeField] private float upwardModifier = 0.15f; [SerializeField] private float randomForceMultiplier = 0.2f; [SerializeField] private float randomTorque = 8f; [Header("Containment")] [SerializeField] private bool keepFragmentsClose = true; [SerializeField] private float maxFragmentSpeed = 1.2f; [SerializeField] private float fragmentDrag = 4f; [SerializeField] private float fragmentAngularDrag = 6f; [SerializeField] private bool disableFragmentGravity = false; [Header("Lifecycle")] [SerializeField] private bool autoCleanupFragments = false; [SerializeField] private float cleanupDelay = 5f; private GameObject spawnedFractureRoot; private bool exploded; private void Update() { if (!enableDebugKey) return; if (Input.GetKeyDown(triggerKey)) { TriggerExplosion(); } } [ContextMenu("Trigger Explosion")] public void TriggerExplosion() { if (exploded) return; if (fracturedPrefab == null) { Debug.LogWarning("[Explosion3DTest] Missing fracturedPrefab."); return; } Transform sourceTransform = intactObject != null ? intactObject.transform : transform; Transform parent = spawnRoot != null ? spawnRoot : sourceTransform.parent; if (destroyPreviousFractureInstance && spawnedFractureRoot != null) { Destroy(spawnedFractureRoot); spawnedFractureRoot = null; } spawnedFractureRoot = Instantiate( fracturedPrefab, sourceTransform.position, sourceTransform.rotation, parent); spawnedFractureRoot.name = fracturedPrefab.name + "_Exploded"; if (disableIntactObjectOnExplode && intactObject != null) { intactObject.SetActive(false); } ApplyExplosionToFragments(spawnedFractureRoot, sourceTransform.position); exploded = true; if (autoCleanupFragments && cleanupDelay > 0f) { StartCoroutine(CleanupAfterDelay(cleanupDelay)); } } [ContextMenu("Reset Explosion")] public void ResetExplosion() { exploded = false; if (spawnedFractureRoot != null) { Destroy(spawnedFractureRoot); spawnedFractureRoot = null; } if (intactObject != null) { intactObject.SetActive(true); } } private void ApplyExplosionToFragments(GameObject fractureRoot, Vector3 explosionCenter) { if (fractureRoot == null) return; Rigidbody[] rigidbodies = fractureRoot.GetComponentsInChildren(true); for (int i = 0; i < rigidbodies.Length; i++) { Rigidbody rb = rigidbodies[i]; if (rb == null) continue; rb.isKinematic = false; rb.useGravity = !disableFragmentGravity; rb.linearDamping = fragmentDrag; rb.angularDamping = fragmentAngularDrag; Vector3 fragmentCenter = rb.worldCenterOfMass; Vector3 direction = fragmentCenter - explosionCenter; if (direction.sqrMagnitude < 0.0001f) { direction = Random.onUnitSphere; } float randomScale = 1f + Random.Range(-randomForceMultiplier, randomForceMultiplier); Vector3 force = direction.normalized * Mathf.Max(0f, explosionForce * randomScale); force += Vector3.up * upwardModifier; rb.linearVelocity = Vector3.zero; rb.angularVelocity = Vector3.zero; rb.AddForce(force, ForceMode.Impulse); if (randomTorque > 0f) { Vector3 torqueAxis = Random.onUnitSphere * randomTorque; rb.AddTorque(torqueAxis, ForceMode.Impulse); } if (keepFragmentsClose) { LimitFragmentVelocity(rb); } } } private void LimitFragmentVelocity(Rigidbody rb) { if (rb == null) return; float maxSpeed = Mathf.Max(0.01f, maxFragmentSpeed); if (rb.linearVelocity.sqrMagnitude > maxSpeed * maxSpeed) { rb.linearVelocity = rb.linearVelocity.normalized * maxSpeed; } } private IEnumerator CleanupAfterDelay(float delay) { yield return new WaitForSeconds(delay); if (spawnedFractureRoot != null) { Destroy(spawnedFractureRoot); spawnedFractureRoot = null; } } }