using UnityEngine; using MoreMountains.Tools; using System.Collections; using UnityEngine.UI; namespace MoreMountains.Tools { /// /// 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 /// [AddComponentMenu("More Mountains/Tools/GUI/MMHealthBar")] public class MMHealthBar : MonoBehaviour { /// the possible health bar types public enum HealthBarTypes { Prefab, Drawn } /// 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 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) 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 public MMProgressBar HealthBarPrefab; [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 public Vector2 Size = new Vector2(1f,0.2f); /// 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 public Vector3 InitialRotationAngles; /// 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 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 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 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 public string SortingLayerName = "UI"; /// the delay to apply to the delayed bar if drawn public float Delay = 0.5f; /// whether or not the front bar should lerp public bool LerpFrontBar = true; /// the speed at which the front bar lerps public float LerpFrontBarSpeed = 15f; /// whether or not the delayed bar should lerp public bool LerpDelayedBar = true; /// 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 public bool BumpScaleOnChange = true; /// the duration of the bump animation public float BumpDuration = 0.2f; /// 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 public MMFollowTarget.UpdateModes FollowTargetMode = MMFollowTarget.UpdateModes.LateUpdate; public bool NestDrawnHealthBar = false; [Header("Death")] /// 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 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 public bool AlwaysVisible = true; /// 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 public bool HideBarAtZero = true; /// 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; /// /// On Start, creates or sets the health bar up /// protected virtual void Awake() { Initialization(); } public virtual void Initialization() { _finalHideStarted = false; if (_progressBar != null) { _progressBar.gameObject.SetActive(AlwaysVisible); return; } if (HealthBarType == 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; _progressBar.transform.SetParent(this.transform); _progressBar.gameObject.name = "HealthBar"; } if (HealthBarType == HealthBarTypes.Drawn) { DrawHealthBar(); UpdateDrawnColors(); } if (!AlwaysVisible) { _progressBar.gameObject.SetActive(false); } if (_progressBar != null) { _progressBar.SetBar(100f, 0f, 100f); } } /// /// Draws the health bar. /// protected virtual void DrawHealthBar() { GameObject newGameObject = new GameObject(); newGameObject.name = "HealthBar|"+this.gameObject.name; if (NestDrawnHealthBar) { newGameObject.transform.SetParent(this.transform); } _progressBar = newGameObject.AddComponent(); _followTransform = newGameObject.AddComponent(); _followTransform.Offset = HealthBarOffset; _followTransform.Target = this.transform; _followTransform.InterpolatePosition = false; _followTransform.InterpolateRotation = false; _followTransform.UpdateMode = FollowTargetMode; Canvas newCanvas = newGameObject.AddComponent(); newCanvas.renderMode = RenderMode.WorldSpace; newCanvas.transform.localScale = Vector3.one; newCanvas.GetComponent().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(); _borderImage.transform.position = Vector3.zero; _borderImage.transform.localScale = Vector3.one; _borderImage.GetComponent().sizeDelta = Size; _borderImage.GetComponent().anchoredPosition = Vector3.zero; GameObject bgImageGameObject = new GameObject(); bgImageGameObject.transform.SetParent(container.transform); bgImageGameObject.name = "HealthBar Background"; _backgroundImage = bgImageGameObject.AddComponent(); _backgroundImage.transform.position = Vector3.zero; _backgroundImage.transform.localScale = Vector3.one; _backgroundImage.GetComponent().sizeDelta = Size - BackgroundPadding*2; _backgroundImage.GetComponent().anchoredPosition = -_backgroundImage.GetComponent().sizeDelta/2; _backgroundImage.GetComponent().pivot = Vector2.zero; GameObject delayedImageGameObject = new GameObject(); delayedImageGameObject.transform.SetParent(container.transform); delayedImageGameObject.name = "HealthBar Delayed Foreground"; _delayedImage = delayedImageGameObject.AddComponent(); _delayedImage.transform.position = Vector3.zero; _delayedImage.transform.localScale = Vector3.one; _delayedImage.GetComponent().sizeDelta = Size - BackgroundPadding*2; _delayedImage.GetComponent().anchoredPosition = -_delayedImage.GetComponent().sizeDelta/2; _delayedImage.GetComponent().pivot = Vector2.zero; GameObject frontImageGameObject = new GameObject(); frontImageGameObject.transform.SetParent(container.transform); frontImageGameObject.name = "HealthBar Foreground"; _foregroundImage = frontImageGameObject.AddComponent(); _foregroundImage.transform.position = Vector3.zero; _foregroundImage.transform.localScale = Vector3.one; _foregroundImage.color = ForegroundColor.Evaluate(1); _foregroundImage.GetComponent().sizeDelta = Size - BackgroundPadding*2; _foregroundImage.GetComponent().anchoredPosition = -_foregroundImage.GetComponent().sizeDelta/2; _foregroundImage.GetComponent().pivot = Vector2.zero; _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(); } /// /// On Update, we hide or show our healthbar based on our current status /// protected virtual void Update() { if (_progressBar == null) { return; } if (_finalHideStarted) { return; } UpdateDrawnColors(); if (AlwaysVisible) { return; } if (_showBar) { _progressBar.gameObject.SetActive(true); float currentTime = (TimeScale == TimeScales.UnscaledTime) ? Time.unscaledTime : Time.time; if (currentTime - _lastShowTimestamp > DisplayDurationOnHit) { _showBar = false; } } else { _progressBar.gameObject.SetActive(false); } } /// /// Hides the bar when it reaches zero /// /// The hide bar. protected virtual IEnumerator FinalHideBar() { _finalHideStarted = true; if (InstantiatedOnDeath != null) { Instantiate(InstantiatedOnDeath, this.transform.position + HealthBarOffset, this.transform.rotation); } if (HideBarAtZeroDelay == 0) { _showBar = false; _progressBar.gameObject.SetActive(false); yield return null; } else { _progressBar.HideBar(HideBarAtZeroDelay); } } /// /// Updates the colors of the different bars /// 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); } } /// /// Updates the bar /// /// Current health. /// Minimum health. /// Max health. /// Whether or not we should show the bar. 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(); } } } } }