You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CrowdControl/Assets/Feel/MMTools/Tools/MMGUI/MMHealthBar.cs

489 lines
18 KiB
C#

3 months ago
using System;
using UnityEngine;
using MoreMountains.Tools;
using System.Collections;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace MoreMountains.Tools
{
/// <summary>
/// Add this component to an object and it will show a healthbar above it
/// You can either use a prefab for it, or have the component draw one at the start
/// </summary>
[AddComponentMenu("More Mountains/Tools/GUI/MMHealthBar")]
public class MMHealthBar : MonoBehaviour
{
/// the possible health bar types
public enum HealthBarTypes { Prefab, Drawn, Existing }
/// the possible timescales the bar can work on
public enum TimeScales { UnscaledTime, Time }
[MMInformation("Add this component to an object and it'll add a healthbar next to it to reflect its health level in real time. You can decide here whether the health bar should be drawn automatically or use a prefab.",MoreMountains.Tools.MMInformationAttribute.InformationType.Info,false)]
/// whether the healthbar uses a prefab or is drawn automatically
[Tooltip("whether the healthbar uses a prefab or is drawn automatically")]
public HealthBarTypes HealthBarType = HealthBarTypes.Drawn;
/// defines whether the bar will work on scaled or unscaled time (whether or not it'll keep moving if time is slowed down for example)
[Tooltip("defines whether the bar will work on scaled or unscaled time (whether or not it'll keep moving if time is slowed down for example)")]
public TimeScales TimeScale = TimeScales.UnscaledTime;
[Header("Select a Prefab")]
[MMInformation("Select a prefab with a progress bar script on it. There is one example of such a prefab in Common/Prefabs/GUI.",MoreMountains.Tools.MMInformationAttribute.InformationType.Info,false)]
/// the prefab to use as the health bar
[Tooltip("the prefab to use as the health bar")]
public MMProgressBar HealthBarPrefab;
[Header("Existing MMProgressBar")]
/// the MMProgressBar this health bar should update
[Tooltip("the MMProgressBar this health bar should update")]
public MMProgressBar TargetProgressBar;
[Header("Drawn Healthbar Settings ")]
[MMInformation("Set the size (in world units), padding, back and front colors of the healthbar.",MoreMountains.Tools.MMInformationAttribute.InformationType.Info,false)]
/// if the healthbar is drawn, its size in world units
[Tooltip("if the healthbar is drawn, its size in world units")]
public Vector2 Size = new Vector2(1f,0.2f);
/// if the healthbar is drawn, the padding to apply to the foreground, in world units
[Tooltip("if the healthbar is drawn, the padding to apply to the foreground, in world units")]
public Vector2 BackgroundPadding = new Vector2(0.01f,0.01f);
/// the rotation to apply to the MMHealthBarContainer when drawing it
[Tooltip("the rotation to apply to the MMHealthBarContainer when drawing it")]
public Vector3 InitialRotationAngles;
/// if the healthbar is drawn, the color of its foreground
[Tooltip("if the healthbar is drawn, the color of its foreground")]
public Gradient ForegroundColor = new Gradient()
{
colorKeys = new GradientColorKey[2] {
new GradientColorKey(MMColors.BestRed, 0),
new GradientColorKey(MMColors.BestRed, 1f)
},
alphaKeys = new GradientAlphaKey[2] {new GradientAlphaKey(1, 0),new GradientAlphaKey(1, 1)}};
/// if the healthbar is drawn, the color of its delayed bar
[Tooltip("if the healthbar is drawn, the color of its delayed bar")]
public Gradient DelayedColor = new Gradient()
{
colorKeys = new GradientColorKey[2] {
new GradientColorKey(MMColors.Orange, 0),
new GradientColorKey(MMColors.Orange, 1f)
},
alphaKeys = new GradientAlphaKey[2] { new GradientAlphaKey(1, 0), new GradientAlphaKey(1, 1) }
};
/// if the healthbar is drawn, the color of its border
[Tooltip("if the healthbar is drawn, the color of its border")]
public Gradient BorderColor = new Gradient()
{
colorKeys = new GradientColorKey[2] {
new GradientColorKey(MMColors.AntiqueWhite, 0),
new GradientColorKey(MMColors.AntiqueWhite, 1f)
},
alphaKeys = new GradientAlphaKey[2] { new GradientAlphaKey(1, 0), new GradientAlphaKey(1, 1) }
};
/// if the healthbar is drawn, the color of its background
[Tooltip("if the healthbar is drawn, the color of its background")]
public Gradient BackgroundColor = new Gradient()
{
colorKeys = new GradientColorKey[2] {
new GradientColorKey(MMColors.Black, 0),
new GradientColorKey(MMColors.Black, 1f)
},
alphaKeys = new GradientAlphaKey[2] { new GradientAlphaKey(1, 0), new GradientAlphaKey(1, 1) }
};
/// the name of the sorting layer to put this health bar on
[Tooltip("the name of the sorting layer to put this health bar on")]
public string SortingLayerName = "UI";
/// the delay to apply to the delayed bar if drawn
[Tooltip("the delay to apply to the delayed bar if drawn")]
public float Delay = 0.5f;
/// whether or not the front bar should lerp
[Tooltip("whether or not the front bar should lerp")]
public bool LerpFrontBar = true;
/// the speed at which the front bar lerps
[Tooltip("the speed at which the front bar lerps")]
public float LerpFrontBarSpeed = 15f;
/// whether or not the delayed bar should lerp
[Tooltip("whether or not the delayed bar should lerp")]
public bool LerpDelayedBar = true;
/// the speed at which the delayed bar lerps
[Tooltip("the speed at which the delayed bar lerps")]
public float LerpDelayedBarSpeed = 15f;
/// if this is true, bumps the scale of the healthbar when its value changes
[Tooltip("if this is true, bumps the scale of the healthbar when its value changes")]
public bool BumpScaleOnChange = true;
/// the duration of the bump animation
[Tooltip("the duration of the bump animation")]
public float BumpDuration = 0.2f;
/// the animation curve to map the bump animation on
[Tooltip("the animation curve to map the bump animation on")]
public AnimationCurve BumpAnimationCurve = AnimationCurve.Constant(0,1,1);
/// the mode the bar should follow the target in
[Tooltip("the mode the bar should follow the target in")]
public MMFollowTarget.UpdateModes FollowTargetMode = MMFollowTarget.UpdateModes.LateUpdate;
/// if this is true, the drawn health bar will be nested below the MMHealthBar
[Tooltip("if this is true, the drawn health bar will be nested below the MMHealthBar")]
public bool NestDrawnHealthBar = false;
/// if this is true, a MMBillboard component will be added to the progress bar to make sure it always looks towards the camera
[Tooltip("if this is true, a MMBillboard component will be added to the progress bar to make sure it always looks towards the camera")]
public bool Billboard = false;
[Header("Death")]
/// a gameobject (usually a particle system) to instantiate when the healthbar reaches zero
[Tooltip("a gameobject (usually a particle system) to instantiate when the healthbar reaches zero")]
public GameObject InstantiatedOnDeath;
[Header("Offset")]
[MMInformation("Set the offset (in world units), relative to the object's center, to which the health bar will be displayed.",MoreMountains.Tools.MMInformationAttribute.InformationType.Info,false)]
/// the offset to apply to the healthbar compared to the object's center
[Tooltip("the offset to apply to the healthbar compared to the object's center")]
public Vector3 HealthBarOffset = new Vector3(0f,1f,0f);
[Header("Display")]
[MMInformation("Here you can define whether or not the healthbar should always be visible. If not, you can set here how long after a hit it'll remain visible.",MoreMountains.Tools.MMInformationAttribute.InformationType.Info,false)]
/// whether or not the bar should be permanently displayed
[Tooltip("whether or not the bar should be permanently displayed")]
public bool AlwaysVisible = true;
/// the duration (in seconds) during which to display the bar
[Tooltip("the duration (in seconds) during which to display the bar")]
public float DisplayDurationOnHit = 1f;
/// if this is set to true the bar will hide itself when it reaches zero
[Tooltip("if this is set to true the bar will hide itself when it reaches zero")]
public bool HideBarAtZero = true;
/// the delay (in seconds) after which to hide the bar
[Tooltip("the delay (in seconds) after which to hide the bar")]
public float HideBarAtZeroDelay = 1f;
protected MMProgressBar _progressBar;
protected MMFollowTarget _followTransform;
protected float _lastShowTimestamp = 0f;
protected bool _showBar = false;
protected Image _backgroundImage = null;
protected Image _borderImage = null;
protected Image _foregroundImage = null;
protected Image _delayedImage = null;
protected bool _finalHideStarted = false;
/// <summary>
/// On Start, creates or sets the health bar up
/// </summary>
protected virtual void Awake()
{
Initialization();
}
/// <summary>
/// On enable, initializes the bar again
/// </summary>
protected void OnEnable()
{
_finalHideStarted = false;
SetInitialActiveState();
}
/// <summary>
/// Forces the bar into its initial active state (hiding it if AlwaysVisible is false)
/// </summary>
public virtual void SetInitialActiveState()
{
if (!AlwaysVisible && (_progressBar != null))
{
ShowBar(false);
}
}
/// <summary>
/// Shows or hides the bar by changing its object's active state
/// </summary>
/// <param name="state"></param>
public virtual void ShowBar(bool state)
{
_progressBar.gameObject.SetActive(state);
}
/// <summary>
/// Whether or not the bar is currently active
/// </summary>
/// <returns></returns>
public virtual bool BarIsShown()
{
return _progressBar.gameObject.activeInHierarchy;
}
/// <summary>
/// Initializes the bar (handles visibility, parenting, initial value
/// </summary>
public virtual void Initialization()
{
_finalHideStarted = false;
if (_progressBar != null)
{
ShowBar(AlwaysVisible);
return;
}
switch (HealthBarType)
{
case HealthBarTypes.Prefab:
if (HealthBarPrefab == null)
{
Debug.LogWarning(this.name + " : the HealthBar has no prefab associated to it, nothing will be displayed.");
return;
}
_progressBar = Instantiate(HealthBarPrefab, transform.position + HealthBarOffset, transform.rotation) as MMProgressBar;
SceneManager.MoveGameObjectToScene(_progressBar.gameObject, this.gameObject.scene);
_progressBar.transform.SetParent(this.transform);
_progressBar.gameObject.name = "HealthBar";
break;
case HealthBarTypes.Drawn:
DrawHealthBar();
UpdateDrawnColors();
break;
case HealthBarTypes.Existing:
_progressBar = TargetProgressBar;
break;
}
if (!AlwaysVisible)
{
ShowBar(false);
}
if (_progressBar != null)
{
_progressBar.SetBar(100f, 0f, 100f);
}
}
/// <summary>
/// Draws the health bar.
/// </summary>
protected virtual void DrawHealthBar()
{
GameObject newGameObject = new GameObject();
SceneManager.MoveGameObjectToScene(newGameObject, this.gameObject.scene);
newGameObject.name = "HealthBar|"+this.gameObject.name;
if (NestDrawnHealthBar)
{
newGameObject.transform.SetParent(this.transform);
}
_progressBar = newGameObject.AddComponent<MMProgressBar>();
_followTransform = newGameObject.AddComponent<MMFollowTarget>();
_followTransform.Offset = HealthBarOffset;
_followTransform.Target = this.transform;
_followTransform.FollowRotation = false;
_followTransform.InterpolatePosition = false;
_followTransform.InterpolateRotation = false;
_followTransform.UpdateMode = FollowTargetMode;
Canvas newCanvas = newGameObject.AddComponent<Canvas>();
newCanvas.renderMode = RenderMode.WorldSpace;
newCanvas.transform.localScale = Vector3.one;
newCanvas.GetComponent<RectTransform>().sizeDelta = Size;
if (!string.IsNullOrEmpty(SortingLayerName))
{
newCanvas.sortingLayerName = SortingLayerName;
}
GameObject container = new GameObject();
container.transform.SetParent(newGameObject.transform);
container.name = "MMProgressBarContainer";
container.transform.localScale = Vector3.one;
GameObject borderImageGameObject = new GameObject();
borderImageGameObject.transform.SetParent(container.transform);
borderImageGameObject.name = "HealthBar Border";
_borderImage = borderImageGameObject.AddComponent<Image>();
_borderImage.transform.position = Vector3.zero;
_borderImage.transform.localScale = Vector3.one;
_borderImage.GetComponent<RectTransform>().sizeDelta = Size;
_borderImage.GetComponent<RectTransform>().anchoredPosition = Vector3.zero;
GameObject bgImageGameObject = new GameObject();
bgImageGameObject.transform.SetParent(container.transform);
bgImageGameObject.name = "HealthBar Background";
_backgroundImage = bgImageGameObject.AddComponent<Image>();
_backgroundImage.transform.position = Vector3.zero;
_backgroundImage.transform.localScale = Vector3.one;
_backgroundImage.GetComponent<RectTransform>().sizeDelta = Size - BackgroundPadding*2;
_backgroundImage.GetComponent<RectTransform>().anchoredPosition = -_backgroundImage.GetComponent<RectTransform>().sizeDelta/2;
_backgroundImage.GetComponent<RectTransform>().pivot = Vector2.zero;
GameObject delayedImageGameObject = new GameObject();
delayedImageGameObject.transform.SetParent(container.transform);
delayedImageGameObject.name = "HealthBar Delayed Foreground";
_delayedImage = delayedImageGameObject.AddComponent<Image>();
_delayedImage.transform.position = Vector3.zero;
_delayedImage.transform.localScale = Vector3.one;
_delayedImage.GetComponent<RectTransform>().sizeDelta = Size - BackgroundPadding*2;
_delayedImage.GetComponent<RectTransform>().anchoredPosition = -_delayedImage.GetComponent<RectTransform>().sizeDelta/2;
_delayedImage.GetComponent<RectTransform>().pivot = Vector2.zero;
GameObject frontImageGameObject = new GameObject();
frontImageGameObject.transform.SetParent(container.transform);
frontImageGameObject.name = "HealthBar Foreground";
_foregroundImage = frontImageGameObject.AddComponent<Image>();
_foregroundImage.transform.position = Vector3.zero;
_foregroundImage.transform.localScale = Vector3.one;
_foregroundImage.color = ForegroundColor.Evaluate(1);
_foregroundImage.GetComponent<RectTransform>().sizeDelta = Size - BackgroundPadding*2;
_foregroundImage.GetComponent<RectTransform>().anchoredPosition = -_foregroundImage.GetComponent<RectTransform>().sizeDelta/2;
_foregroundImage.GetComponent<RectTransform>().pivot = Vector2.zero;
if (Billboard)
{
_progressBar.gameObject.AddComponent<MMBillboard>();
}
_progressBar.LerpDecreasingDelayedBar = LerpDelayedBar;
_progressBar.LerpForegroundBar = LerpFrontBar;
_progressBar.LerpDecreasingDelayedBarSpeed = LerpDelayedBarSpeed;
_progressBar.LerpForegroundBarSpeedIncreasing = LerpFrontBarSpeed;
_progressBar.ForegroundBar = _foregroundImage.transform;
_progressBar.DelayedBarDecreasing = _delayedImage.transform;
_progressBar.DecreasingDelay = Delay;
_progressBar.BumpScaleOnChange = BumpScaleOnChange;
_progressBar.BumpDuration = BumpDuration;
_progressBar.BumpScaleAnimationCurve = BumpAnimationCurve;
_progressBar.TimeScale = (TimeScale == TimeScales.Time) ? MMProgressBar.TimeScales.Time : MMProgressBar.TimeScales.UnscaledTime;
container.transform.localEulerAngles = InitialRotationAngles;
_progressBar.Initialization();
}
/// <summary>
/// On Update, we hide or show our healthbar based on our current status
/// </summary>
protected virtual void Update()
{
if (_progressBar == null)
{
return;
}
if (_finalHideStarted)
{
return;
}
UpdateDrawnColors();
if (AlwaysVisible)
{
return;
}
if (_showBar)
{
ShowBar(true);
float currentTime = (TimeScale == TimeScales.UnscaledTime) ? Time.unscaledTime : Time.time;
if (currentTime - _lastShowTimestamp > DisplayDurationOnHit)
{
_showBar = false;
}
}
else
{
if (BarIsShown())
{
ShowBar(false);
}
}
}
/// <summary>
/// Hides the bar when it reaches zero
/// </summary>
/// <returns>The hide bar.</returns>
protected virtual IEnumerator FinalHideBar()
{
_finalHideStarted = true;
if (InstantiatedOnDeath != null)
{
GameObject instantiatedOnDeath = Instantiate(InstantiatedOnDeath, this.transform.position + HealthBarOffset, this.transform.rotation);
SceneManager.MoveGameObjectToScene(instantiatedOnDeath.gameObject, this.gameObject.scene);
}
if (HideBarAtZeroDelay == 0)
{
_showBar = false;
ShowBar(false);
yield return null;
}
else
{
_progressBar.HideBar(HideBarAtZeroDelay);
}
}
/// <summary>
/// Updates the colors of the different bars
/// </summary>
protected virtual void UpdateDrawnColors()
{
if (HealthBarType != HealthBarTypes.Drawn)
{
return;
}
if (_progressBar.Bumping)
{
return;
}
if (_borderImage != null)
{
_borderImage.color = BorderColor.Evaluate(_progressBar.BarProgress);
}
if (_backgroundImage != null)
{
_backgroundImage.color = BackgroundColor.Evaluate(_progressBar.BarProgress);
}
if (_delayedImage != null)
{
_delayedImage.color = DelayedColor.Evaluate(_progressBar.BarProgress);
}
if (_foregroundImage != null)
{
_foregroundImage.color = ForegroundColor.Evaluate(_progressBar.BarProgress);
}
}
/// <summary>
/// Updates the bar
/// </summary>
/// <param name="currentHealth">Current health.</param>
/// <param name="minHealth">Minimum health.</param>
/// <param name="maxHealth">Max health.</param>
/// <param name="show">Whether or not we should show the bar.</param>
public virtual void UpdateBar(float currentHealth, float minHealth, float maxHealth, bool show)
{
// if the healthbar isn't supposed to be always displayed, we turn it on for the specified duration
if (!AlwaysVisible && show)
{
_showBar = true;
_lastShowTimestamp = (TimeScale == TimeScales.UnscaledTime) ? Time.unscaledTime : Time.time;
}
if (_progressBar != null)
{
_progressBar.UpdateBar(currentHealth, minHealth, maxHealth) ;
if (HideBarAtZero && _progressBar.BarTarget <= 0)
{
StartCoroutine(FinalHideBar());
}
if (BumpScaleOnChange)
{
_progressBar.Bump();
}
}
}
}
}