// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using UltimateXR.Animation.GameObjects; using UltimateXR.Audio; using UltimateXR.Core.Components; using UnityEngine; namespace UltimateXR.Mechanics.Weapons { /// /// Component that allows to explode a GameObject and all its rigidbody children. /// If the component is attached to a GameObject that also has a component the explosion will /// be triggered when the actor dies. /// The explosion can also be called explicitly using and . /// public class UxrExplodeHierarchy : UxrComponent { #region Inspector Properties/Serialized Fields [SerializeField] private UxrAudioSample[] _audioExplodePool; [SerializeField] private float _minExplodeSpeed = 5.0f; [SerializeField] private float _maxExplodeSpeed = 20.0f; [SerializeField] private float _minExplodeAngularSpeed = 1.0f; [SerializeField] private float _maxExplodeAngularSpeed = 8.0f; [SerializeField] private float _secondsToExplode = -1.0f; [SerializeField] private float _piecesLifeSeconds = 5.0f; [SerializeField] private float _piecesFadeoutSeconds = 1.0f; #endregion #region Public Types & Data /// /// Gets the explode timer. A negative value will either indicate that it is not using any timer (if /// is false) or that it already exploded (if is true). /// public float ExplodeTimer { get; private set; } = -1.0f; /// /// Gets whether the object has exploded. /// public bool HasExploded { get; private set; } /// /// Gets or sets the minimum random speed that the chunks will have when the object explodes. /// public float MinExplodeSpeed { get => _minExplodeSpeed; set => _minExplodeSpeed = value; } /// /// Gets or sets the maximum random speed that the chunks will have when the object explodes. /// public float MaxExplodeSpeed { get => _maxExplodeSpeed; set => _maxExplodeSpeed = value; } /// /// Gets or sets the minimum random angular speed that the chunks will have when the object explodes. /// public float MinExplodeAngularSpeed { get => _minExplodeAngularSpeed; set => _minExplodeAngularSpeed = value; } /// /// Gets or sets the maximum random angular speed that the chunks will have when the object explodes. /// public float MaxExplodeAngularSpeed { get => _maxExplodeAngularSpeed; set => _maxExplodeAngularSpeed = value; } /// /// Gets or sets the seconds it will take for the object to explode after the component is enabled. A negative value /// will disable the user of a timer and will only explode when the object was added to an that /// dies. /// public float SecondsToExplode { get => _secondsToExplode; set { ExplodeTimer = value; _secondsToExplode = value; } } /// /// Gets or sets the seconds it will take for the chunks to disappear after the object explodes. /// public float PiecesLifeSeconds { get => _piecesLifeSeconds; set => _piecesLifeSeconds = value; } /// /// Gets or sets the seconds it will take for the chunks to fade-out when the chunks disappear after /// . /// public float PiecesFadeoutSeconds { get => _piecesFadeoutSeconds; set => _piecesFadeoutSeconds = value; } #endregion #region Public Methods /// /// Explodes an object. /// /// Root object to explode /// Minimum random velocity assigned to the chunks /// Maximum random velocity assigned to the chunks /// Seconds to wait before exploding /// Life in seconds to assign to the chunks, after which they will be destroyed public static void Explode(GameObject root, float minExplodeVelocity, float maxExplodeVelocity, float secondsToExplode, float piecesLifeSeconds) { UxrExplodeHierarchy explodeHierarchy = root.AddComponent(); explodeHierarchy.MinExplodeSpeed = minExplodeVelocity; explodeHierarchy.MaxExplodeSpeed = maxExplodeVelocity; explodeHierarchy.SecondsToExplode = secondsToExplode; explodeHierarchy.PiecesLifeSeconds = piecesLifeSeconds; } /// /// Explodes an object immediately using the current parameters. /// public void ExplodeNow() { if (HasExploded) { return; } foreach (Collider chunkCollider in _colliders) { chunkCollider.enabled = true; chunkCollider.transform.SetParent(null); if (!chunkCollider.gameObject.TryGetComponent(out var rigidBodyChunk)) { rigidBodyChunk = chunkCollider.gameObject.AddComponent(); } rigidBodyChunk.isKinematic = false; rigidBodyChunk.velocity = Random.insideUnitSphere * Random.Range(MinExplodeSpeed, MaxExplodeSpeed); rigidBodyChunk.angularVelocity = Random.insideUnitSphere * Random.Range(MinExplodeAngularSpeed, MaxExplodeAngularSpeed); float startAlpha = 1.0f; Renderer renderer = chunkCollider.gameObject.GetComponent(); if (renderer) { startAlpha = renderer.material.color.a; } UxrObjectFade.Fade(chunkCollider.gameObject, startAlpha, 0.0f, PiecesLifeSeconds - PiecesFadeoutSeconds, PiecesFadeoutSeconds); Destroy(rigidBodyChunk.gameObject, PiecesLifeSeconds); } HasExploded = true; ExplodeTimer = -1.0f; if (_audioExplodePool != null) { int i = Random.Range(0, _audioExplodePool.Length); _audioExplodePool[i].Play(transform.position); } Destroy(gameObject); } #endregion #region Unity /// /// Initializes the component. /// protected override void Awake() { base.Awake(); _colliders = gameObject.GetComponentsInChildren(true); HasExploded = false; } /// /// Subscribes to events and initializes the explosion timer. /// protected override void OnEnable() { base.OnEnable(); if (!HasExploded) { ExplodeTimer = SecondsToExplode; } UxrActor actor = GetCachedComponent(); if (actor) { actor.DamageReceived += Actor_Damaged; } } /// /// Unsubscribes from events. /// protected override void OnDisable() { base.OnDisable(); UxrActor actor = GetCachedComponent(); if (actor) { actor.DamageReceived -= Actor_Damaged; } } /// /// Updates the explosion timer and checks if the object needs to explode. /// private void Update() { if (ExplodeTimer >= 0.0f && !HasExploded) { ExplodeTimer -= Time.deltaTime; if (ExplodeTimer < 0.0f) { ExplodeNow(); } } } #endregion #region Event Handling Methods /// /// Called when the component was added to an object with an component and it took damage. /// /// Event sender /// Event parameters private void Actor_Damaged(object sender, UxrDamageEventArgs e) { if (e.Dies) { ExplodeNow(); } } #endregion #region Private Types & Data private Collider[] _colliders; #endregion } }