Files
BABA_YAGA/Assets/Opsive/UltimateCharacterController/Scripts/Traits/Health.cs
2026-06-09 02:05:00 +07:00

572 lines
29 KiB
C#

/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Traits
{
using Opsive.Shared.Events;
using Opsive.Shared.Game;
using Opsive.Shared.Utility;
using Opsive.UltimateCharacterController.Audio;
using Opsive.UltimateCharacterController.Events;
using Opsive.UltimateCharacterController.Game;
using Opsive.UltimateCharacterController.Objects;
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
using Opsive.UltimateCharacterController.Networking;
using Opsive.UltimateCharacterController.Networking.Traits;
#endif
using Opsive.UltimateCharacterController.StateSystem;
using Opsive.UltimateCharacterController.Utility;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Adds health and a shield to the object.
/// </summary>
[RequireComponent(typeof(AttributeManager))]
public class Health : StateBehavior
{
[Tooltip("Is the object invincible?")]
[SerializeField] protected bool m_Invincible;
[Tooltip("The amount of time that the object is invincible after respawning.")]
[SerializeField] protected float m_TimeInvincibleAfterSpawn;
[Tooltip("The name of the health attribute.")]
[SerializeField] protected string m_HealthAttributeName = "Health";
[Tooltip("The name of the shield attribute.")]
[SerializeField] protected string m_ShieldAttributeName;
[Tooltip("The list of Colldiers that should apply a multiplier when damaged.")]
[SerializeField] protected Hitbox[] m_Hitboxes;
[Tooltip("The maximum number of colliders that can be detected when determining if a hitbox was damaged.")]
[SerializeField] protected int m_MaxHitboxCollisionCount = 10;
[Tooltip("Any object that should spawn when the object dies.")]
[SerializeField] protected GameObject[] m_SpawnedObjectsOnDeath;
[Tooltip("Any object that should be destroyed when the object dies.")]
[SerializeField] protected GameObject[] m_DestroyedObjectsOnDeath;
[Tooltip("Should the object be deactivated on death?")]
[SerializeField] protected bool m_DeactivateOnDeath;
[Tooltip("If DeactivateOnDeath is enabled, specify a delay for the object to be deactivated.")]
[SerializeField] protected float m_DeactivateOnDeathDelay;
[Tooltip("The layer that the GameObject should switch to upon death.")]
[SerializeField] protected LayerMask m_DeathLayer;
[Tooltip("A set of AudioClips that can be played when the object takes damage.")]
[HideInInspector] [SerializeField] protected AudioClipSet m_TakeDamageAudioClipSet = new AudioClipSet();
[Tooltip("A set of AudioClips that can be played when the object is healed.")]
[HideInInspector] [SerializeField] protected AudioClipSet m_HealAudioClipSet = new AudioClipSet();
[Tooltip("A set of AudioClips that can be played when the object dies.")]
[HideInInspector] [SerializeField] protected AudioClipSet m_DeathAudioClipSet = new AudioClipSet();
[Tooltip("Unity event invoked when taking damage.")]
[SerializeField] protected UnityFloatVector3Vector3GameObjectEvent m_OnDamageEvent;
[Tooltip("Unity event invoked when healing.")]
[SerializeField] protected UnityFloatEvent m_OnHealEvent;
[Tooltip("Unity event invoked when the object dies.")]
[SerializeField] protected UnityVector3Vector3GameObjectEvent m_OnDeathEvent;
public bool Invincible { get { return m_Invincible; } set { m_Invincible = value; } }
public float TimeInvincibleAfterSpawn { get { return m_TimeInvincibleAfterSpawn; } set { m_TimeInvincibleAfterSpawn = value; } }
public string HealthAttributeName { get { return m_HealthAttributeName; }
set
{
m_HealthAttributeName = value;
if (Application.isPlaying) {
if (!string.IsNullOrEmpty(m_HealthAttributeName)) {
m_HealthAttribute = m_AttributeManager.GetAttribute(m_HealthAttributeName);
} else {
m_HealthAttribute = null;
}
}
}
}
public string ShieldAttributeName
{
get { return m_ShieldAttributeName; }
set
{
m_ShieldAttributeName = value;
if (Application.isPlaying) {
if (!string.IsNullOrEmpty(m_ShieldAttributeName)) {
m_ShieldAttribute = m_AttributeManager.GetAttribute(m_ShieldAttributeName);
} else {
m_ShieldAttribute = null;
}
}
}
}
[NonSerialized] public Hitbox[] Hitboxes { get { return m_Hitboxes; } set { m_Hitboxes = value; } }
public int MaxHitboxCollisionCount { get { return m_MaxHitboxCollisionCount; } set { m_MaxHitboxCollisionCount = value; } }
public GameObject[] SpawnedObjectsOnDeath { get { return m_SpawnedObjectsOnDeath; } set { m_SpawnedObjectsOnDeath = value; } }
public GameObject[] DestroyedObjectsOnDeath { get { return m_DestroyedObjectsOnDeath; } set { m_DestroyedObjectsOnDeath = value; } }
public bool DeactivateOnDeath { get { return m_DeactivateOnDeath; } set { m_DeactivateOnDeath = value; } }
public float DeactivateOnDeathDelay { get { return m_DeactivateOnDeathDelay; } set { m_DeactivateOnDeathDelay = value; } }
public LayerMask DeathLayer { get { return m_DeathLayer; } set { m_DeathLayer = value; } }
public AudioClipSet TakeDamageAudioClipSet { get { return m_TakeDamageAudioClipSet; } set { m_TakeDamageAudioClipSet = value; } }
public AudioClipSet HealAudioClipSet { get { return m_HealAudioClipSet; } set { m_HealAudioClipSet = value; } }
public AudioClipSet DeathAudioClipSet { get { return m_DeathAudioClipSet; } set { m_DeathAudioClipSet = value; } }
public UnityFloatVector3Vector3GameObjectEvent OnDamageEvent { get { return m_OnDamageEvent; } set { m_OnDamageEvent = value; } }
public UnityFloatEvent OnHealEvent { get { return m_OnHealEvent; } set { m_OnHealEvent = value; } }
public UnityVector3Vector3GameObjectEvent OnDeathEvent { get { return m_OnDeathEvent; } set { m_OnDeathEvent = value; } }
protected GameObject m_GameObject;
protected Transform m_Transform;
private IForceObject m_ForceObject;
private Rigidbody m_Rigidbody;
private AttributeManager m_AttributeManager;
private Attribute m_HealthAttribute;
private Attribute m_ShieldAttribute;
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
private INetworkInfo m_NetworkInfo;
private INetworkHealthMonitor m_NetworkHealthMonitor;
#endif
private float m_SpawnTime;
private int m_AliveLayer;
private Dictionary<Collider, Hitbox> m_ColliderHitboxMap;
private RaycastHit[] m_RaycastHits;
private UnityEngineUtility.RaycastHitComparer m_RaycastHitComparer;
public float HealthValue { get { return (m_HealthAttribute != null ? m_HealthAttribute.Value : 0); } }
public float ShieldValue { get { return (m_ShieldAttribute != null ? m_ShieldAttribute.Value : 0); } }
public float Value { get { return HealthValue + ShieldValue; } }
/// <summary>
/// Initialize the default values.
/// </summary>
protected override void Awake()
{
base.Awake();
m_GameObject = gameObject;
m_Transform = transform;
m_ForceObject = m_GameObject.GetCachedComponent<IForceObject>();
m_Rigidbody = m_GameObject.GetCachedComponent<Rigidbody>();
m_AttributeManager = GetComponent<AttributeManager>();
if (!string.IsNullOrEmpty(m_HealthAttributeName)) {
m_HealthAttribute = m_AttributeManager.GetAttribute(m_HealthAttributeName);
}
if (!string.IsNullOrEmpty(m_ShieldAttributeName)) {
m_ShieldAttribute = m_AttributeManager.GetAttribute(m_ShieldAttributeName);
}
m_AliveLayer = m_GameObject.layer;
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
m_NetworkInfo = m_GameObject.GetCachedComponent<INetworkInfo>();
m_NetworkHealthMonitor = m_GameObject.GetCachedComponent<INetworkHealthMonitor>();
if (m_NetworkInfo != null && m_NetworkHealthMonitor == null) {
Debug.LogError("Error: The object " + m_GameObject.name + " must have a NetworkHealthMonitor component.");
}
#endif
if (m_Hitboxes != null && m_Hitboxes.Length > 0) {
m_ColliderHitboxMap = new Dictionary<Collider, Hitbox>();
for (int i = 0; i < m_Hitboxes.Length; ++i) {
m_ColliderHitboxMap.Add(m_Hitboxes[i].Collider, m_Hitboxes[i]);
}
m_RaycastHits = new RaycastHit[m_MaxHitboxCollisionCount];
m_RaycastHitComparer = new UnityEngineUtility.RaycastHitComparer();
}
EventHandler.RegisterEvent(m_GameObject, "OnRespawn", OnRespawn);
}
/// <summary>
/// The object has been damaged.
/// </summary>
/// <param name="amount">The amount of damage taken.</param>
public void Damage(float amount)
{
Damage(amount, m_Transform.position, Vector3.zero, 1, 0, 0, null, null);
}
/// <summary>
/// The object has been damaged.
/// </summary>
/// <param name="amount">The amount of damage taken.</param>
/// <param name="position">The position of the damage.</param>
/// <param name="direction">The direction that the object took damage from.</param>
/// <param name="forceMagnitude">The magnitude of the force that is applied to the object.</param>
public void Damage(float amount, Vector3 position, Vector3 direction, float forceMagnitude)
{
Damage(amount, position, direction, forceMagnitude, 1, 0, null, null);
}
/// <summary>
/// The object has been damaged.
/// </summary>
/// <param name="amount">The amount of damage taken.</param>
/// <param name="position">The position of the damage.</param>
/// <param name="direction">The direction that the object took damage from.</param>
/// <param name="forceMagnitude">The magnitude of the force that is applied to the object.</param>
/// <param name="radius">The radius of the explosive damage. If 0 then a non-exposive force will be used.</param>
public void Damage(float amount, Vector3 position, Vector3 direction, float forceMagnitude, float radius)
{
Damage(amount, position, direction, forceMagnitude, 1, radius, null, null);
}
/// <summary>
/// The object has been damaged.
/// </summary>
/// <param name="amount">The amount of damage taken.</param>
/// <param name="position">The position of the damage.</param>
/// <param name="direction">The direction that the object took damage from.</param>
/// <param name="forceMagnitude">The magnitude of the force that is applied to the object.</param>
/// <param name="attacker">The GameObject that did the damage.</param>
public void Damage(float amount, Vector3 position, Vector3 direction, float forceMagnitude, GameObject attacker)
{
Damage(amount, position, direction, forceMagnitude, 1, 0, attacker, null);
}
/// <summary>
/// The object has been damaged.
/// </summary>
/// <param name="amount">The amount of damage taken.</param>
/// <param name="position">The position of the damage.</param>
/// <param name="direction">The direction that the object took damage from.</param>
/// <param name="forceMagnitude">The magnitude of the force that is applied to the object.</param>
/// <param name="frames">The number of frames to add the force to.</param>
/// <param name="radius">The radius of the explosive damage. If 0 then a non-exposive force will be used.</param>
/// <param name="attacker">The GameObject that did the damage.</param>
public void Damage(float amount, Vector3 position, Vector3 direction, float forceMagnitude, int frames, float radius, GameObject attacker)
{
Damage(amount, position, direction, forceMagnitude, frames, radius, attacker, null);
}
/// <summary>
/// The object has been damaged.
/// </summary>
/// <param name="amount">The amount of damage taken.</param>
/// <param name="position">The position of the damage.</param>
/// <param name="direction">The direction that the object took damage from.</param>
/// <param name="forceMagnitude">The magnitude of the force that is applied to the object.</param>
/// <param name="frames">The number of frames to add the force to.</param>
/// <param name="radius">The radius of the explosive damage. If 0 then a non-explosive force will be used.</param>
/// <param name="attacker">The GameObject that did the damage.</param>
/// <param name="hitCollider">The Collider that was hit.</param>
public void Damage(float amount, Vector3 position, Vector3 direction, float forceMagnitude, int frames, float radius, GameObject attacker, Collider hitCollider)
{
Damage(amount, position, direction, forceMagnitude, frames, radius, attacker, null, hitCollider);
}
/// <summary>
/// The object has been damaged.
/// </summary>
/// <param name="amount">The amount of damage taken.</param>
/// <param name="position">The position of the damage.</param>
/// <param name="direction">The direction that the object took damage from.</param>
/// <param name="forceMagnitude">The magnitude of the force that is applied to the object.</param>
/// <param name="frames">The number of frames to add the force to.</param>
/// <param name="radius">The radius of the explosive damage. If 0 then a non-explosive force will be used.</param>
/// <param name="attacker">The GameObject that did the damage.</param>
/// <param name="attackerObject">The object that did the damage.</param>
/// <param name="hitCollider">The Collider that was hit.</param>
public void Damage(float amount, Vector3 position, Vector3 direction, float forceMagnitude, int frames, float radius, GameObject attacker, object attackerObject, Collider hitCollider)
{
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
if (m_NetworkInfo != null && !m_NetworkInfo.IsLocalPlayer()) {
return;
}
#endif
// Don't take any damage if the object is invincible, already dead, or just spawned and is invincible for a small amount of time.
if (m_Invincible || !IsAlive() || m_SpawnTime + m_TimeInvincibleAfterSpawn > Time.time || amount == 0) {
return;
}
OnDamage(amount, position, direction, forceMagnitude, frames, radius, attacker, attackerObject, hitCollider);
}
/// <summary>
/// The object has taken been damaged.
/// </summary>
/// <param name="amount">The amount of damage taken.</param>
/// <param name="position">The position of the damage.</param>
/// <param name="direction">The direction that the object took damage from.</param>
/// <param name="forceMagnitude">The magnitude of the force that is applied to the object.</param>
/// <param name="frames">The number of frames to add the force to.</param>
/// <param name="radius">The radius of the explosive damage. If 0 then a non-explosive force will be used.</param>
/// <param name="attacker">The GameObject that did the damage.</param>
/// <param name="attackerObject">The object that did the damage.</param>
/// <param name="hitCollider">The Collider that was hit.</param>
public virtual void OnDamage(float amount, Vector3 position, Vector3 direction, float forceMagnitude, int frames, float radius, GameObject attacker, object attackerObject, Collider hitCollider)
{
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
if (m_NetworkInfo != null && m_NetworkInfo.IsLocalPlayer()) {
m_NetworkHealthMonitor.OnDamage(amount, position, direction, forceMagnitude, frames, radius, attacker, hitCollider);
}
#endif
// Add a multiplier if a particular collider was hit. Do not apply a multiplier if the damage is applied through a radius because multiple
// collider are hit.
if (radius == 0 && direction != Vector3.zero && hitCollider != null) {
Hitbox hitbox;
if (m_ColliderHitboxMap != null && m_ColliderHitboxMap.Count > 0) {
if (m_ColliderHitboxMap.TryGetValue(hitCollider, out hitbox)) {
amount *= hitbox.DamageMultiplier;
} else {
// The main collider may be overlapping child hitbox colliders. Perform one more raycast to ensure a hitbox collider shouldn't be hit.
float distance = 0.2f;
if (hitCollider is CapsuleCollider) {
distance = (hitCollider as CapsuleCollider).radius;
} else if (hitCollider is SphereCollider) {
distance = (hitCollider as SphereCollider).radius;
}
// The hitbox collider may be underneath the base collider. Fire a raycast to detemine if there are any colliders underneath the hit collider
// that should apply a multiplier.
var hitCount = Physics.RaycastNonAlloc(position, direction, m_RaycastHits, distance,
~(1 << LayerManager.IgnoreRaycast | 1 << LayerManager.Overlay | 1 << LayerManager.VisualEffect), QueryTriggerInteraction.Ignore);
for (int i = 0; i < hitCount; ++i) {
var closestRaycastHit = QuickSelect.SmallestK(m_RaycastHits, hitCount, i, m_RaycastHitComparer);
if (closestRaycastHit.collider == hitCollider) {
continue;
}
// A new collider has been found - stop iterating if the hitbox map exists and use the hitbox multiplier.
if (m_ColliderHitboxMap.TryGetValue(closestRaycastHit.collider, out hitbox)) {
amount *= hitbox.DamageMultiplier;
hitCollider = hitbox.Collider;
break;
}
}
}
}
}
// Apply the damage to the shield first because the shield can regenrate.
if (m_ShieldAttribute != null && m_ShieldAttribute.Value > m_ShieldAttribute.MinValue) {
var shieldAmount = Mathf.Min(amount, m_ShieldAttribute.Value - m_ShieldAttribute.MinValue);
amount -= shieldAmount;
m_ShieldAttribute.Value -= shieldAmount;
}
// Decrement the health by remaining amount after the shield has taken damage.
if (m_HealthAttribute != null && m_HealthAttribute.Value > m_HealthAttribute.MinValue) {
m_HealthAttribute.Value -= Mathf.Min(amount, m_HealthAttribute.Value - m_HealthAttribute.MinValue);
}
var force = direction * forceMagnitude;
if (forceMagnitude > 0) {
// Apply a force to the object.
if (m_ForceObject != null) {
m_ForceObject.AddForce(force, frames);
} else {
// Apply a force to the rigidbody if the object isn't a character.
if (m_Rigidbody != null && !m_Rigidbody.isKinematic) {
if (radius == 0) {
m_Rigidbody.AddForceAtPosition(force * MathUtility.RigidbodyForceMultiplier, position);
} else {
m_Rigidbody.AddExplosionForce(force.magnitude * MathUtility.RigidbodyForceMultiplier, position, radius);
}
}
}
}
// Let other interested objects know that the object took damage.
EventHandler.ExecuteEvent<float, Vector3, Vector3, GameObject, Collider>(m_GameObject, "OnHealthDamage", amount, position, force, attacker, hitCollider);
if (m_OnDamageEvent != null) {
m_OnDamageEvent.Invoke(amount, position, force, attacker);
}
// The object is dead when there is no more health or shield.
if (!IsAlive()) {
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
if (m_NetworkInfo == null || m_NetworkInfo.IsLocalPlayer()) {
#endif
Die(position, force, attacker);
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
}
#endif
} else {
// Play any take damage audio if the object did not die. If the object died then the death audio will play.
m_TakeDamageAudioClipSet.PlayAudioClip(m_GameObject);
}
}
/// <summary>
/// Is the object currently alive?
/// </summary>
/// <returns>True if the object is currently alive.</returns>
public bool IsAlive()
{
return (m_HealthAttribute != null && m_HealthAttribute.Value > m_HealthAttribute.MinValue) ||
(m_ShieldAttribute != null && m_ShieldAttribute.Value > m_ShieldAttribute.MinValue);
}
/// <summary>
/// The object is no longer alive.
/// </summary>
/// <param name="position">The position of the damage.</param>
/// <param name="force">The amount of force applied to the object while taking the damage.</param>
/// <param name="attacker">The GameObject that killed the character.</param>
public virtual void Die(Vector3 position, Vector3 force, GameObject attacker)
{
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
if (m_NetworkInfo != null && m_NetworkInfo.IsLocalPlayer()) {
m_NetworkHealthMonitor.Die(position, force, attacker);
}
#endif
// Spawn any objects on death, such as an explosion if the object is an explosive barrel.
if (m_SpawnedObjectsOnDeath != null) {
for (int i = 0; i < m_SpawnedObjectsOnDeath.Length; ++i) {
var spawnedObject = ObjectPool.Instantiate(m_SpawnedObjectsOnDeath[i], transform.position, transform.rotation);
Explosion explosion;
if ((explosion = spawnedObject.GetCachedComponent<Explosion>()) != null) {
explosion.Explode(gameObject);
}
var rigidbodies = spawnedObject.GetComponentsInChildren<Rigidbody>();
for (int j = 0; j < rigidbodies.Length; ++j) {
rigidbodies[j].AddForceAtPosition(force, position);
}
}
}
// Destroy any objects on death. The objects will be placed back in the object pool if they were created within it otherwise the object will be destroyed.
if (m_DestroyedObjectsOnDeath != null) {
for (int i = 0; i < m_DestroyedObjectsOnDeath.Length; ++i) {
if (ObjectPool.InstantiatedWithPool(m_DestroyedObjectsOnDeath[i])) {
ObjectPool.Destroy(m_DestroyedObjectsOnDeath[i]);
} else {
Object.Destroy(m_DestroyedObjectsOnDeath[i]);
}
}
}
// Change the layer to a death layer.
if (m_DeathLayer.value != 0) {
m_AliveLayer = m_GameObject.layer;
m_GameObject.layer = m_DeathLayer;
}
// Play any take death audio. Use PlayAtPosition because the audio won't play if the GameObject is inactive.
m_DeathAudioClipSet.PlayAtPosition(m_Transform.position);
// Deactivate the object if requested.
if (m_DeactivateOnDeath) {
Scheduler.Schedule(m_DeactivateOnDeathDelay, Deactivate);
}
// The attributes shouldn't regenerate.
if (m_ShieldAttribute != null) {
m_ShieldAttribute.CancelAutoUpdate();
}
if (m_HealthAttribute != null) {
m_HealthAttribute.CancelAutoUpdate();
}
// Notify those interested.
EventHandler.ExecuteEvent<Vector3, Vector3, GameObject>(m_GameObject, "OnDeath", position, force, attacker);
if (m_OnDeathEvent != null) {
m_OnDeathEvent.Invoke(position, force, attacker);
}
}
/// <summary>
/// Kills the object immediately.
/// </summary>
public void ImmediateDeath()
{
ImmediateDeath(m_Transform.position, Vector3.zero, 0);
}
/// <summary>
/// Kills the object immediately.
/// </summary>
/// <param name="position">The position the character died.</param>
/// <param name="direction">The direction that the object took damage from.</param>
/// <param name="forceMagnitude">The magnitude of the force that is applied to the object.</param>
public void ImmediateDeath(Vector3 position, Vector3 direction, float forceMagnitude)
{
var amount = 0f;
if (m_HealthAttribute != null) {
amount += m_HealthAttribute.Value;
}
if (m_ShieldAttribute != null) {
amount += m_ShieldAttribute.Value;
}
// If ImmediateDeath is called then the object should die even if it is invincible.
var invincible = m_Invincible;
m_Invincible = false;
Damage(amount, position, direction, forceMagnitude);
m_Invincible = invincible;
}
/// <summary>
/// Adds amount to health and then to the shield if there is still an amount remaining. Will not go over the maximum health or shield value.
/// </summary>
/// <param name="amount">The amount of health or shield to add.</param>
/// <returns>True if the object was healed.</returns>
public virtual bool Heal(float amount)
{
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
if (m_NetworkInfo != null && m_NetworkInfo.IsLocalPlayer()) {
m_NetworkHealthMonitor.Heal(amount);
}
#endif
var healAmount = 0f;
// Contribute the amount of the health first.
if (m_HealthAttribute != null && m_HealthAttribute.Value < m_HealthAttribute.MaxValue) {
var healthAmount = Mathf.Min(amount, m_HealthAttribute.MaxValue - m_HealthAttribute.Value);
amount -= healthAmount;
m_HealthAttribute.Value += healthAmount;
healAmount += healthAmount;
}
// Add any remaining amount to the shield.
if (m_ShieldAttribute != null && amount > 0 && m_ShieldAttribute.Value < m_ShieldAttribute.MaxValue) {
var shieldAmount = Mathf.Min(amount, m_ShieldAttribute.MaxValue - m_ShieldAttribute.Value);
m_ShieldAttribute.Value += shieldAmount;
healAmount += shieldAmount;
}
// Don't play any effects if the object wasn't healed.
if (healAmount == 0) {
return false;
}
// Play any heal audio.
m_HealAudioClipSet.PlayAudioClip(m_GameObject);
EventHandler.ExecuteEvent<float>(m_GameObject, "OnHealthHeal", healAmount);
if (m_OnHealEvent != null) {
m_OnHealEvent.Invoke(healAmount);
}
return true;
}
/// <summary>
/// The object doesn't have any health or shield left and should be deactivated.
/// </summary>
private void Deactivate()
{
m_GameObject.SetActive(false);
}
/// <summary>
/// The object has spawned again. Set the health and shield back to their starting values.
/// </summary>
protected virtual void OnRespawn()
{
if (m_HealthAttribute != null) {
m_HealthAttribute.ResetValue();
}
if (m_ShieldAttribute != null) {
m_ShieldAttribute.ResetValue();
}
// Change the layer back to the alive layer.
if (m_DeathLayer.value != 0) {
m_GameObject.layer = m_AliveLayer;
}
m_SpawnTime = Time.time;
}
/// <summary>
/// The GameObject has been destroyed.
/// </summary>
private void OnDestroy()
{
EventHandler.UnregisterEvent(m_GameObject, "OnRespawn", OnRespawn);
}
}
}