using System; using UnityEngine; using UnityEngine.UI; using UnityEngine.Events; namespace DuloGames.UI { [AddComponentMenu("UI/Bars/Progress Bar")] public class UIProgressBar : MonoBehaviour, IUIProgressBar { [Serializable] public class ChangeEvent : UnityEvent { } public enum Type { Filled, Resize, Sprites } public enum FillSizing { Parent, Fixed } [SerializeField] private Type m_Type = Type.Filled; [SerializeField] private Image m_TargetImage; [SerializeField] private Sprite[] m_Sprites; [SerializeField] private RectTransform m_TargetTransform; [SerializeField] private FillSizing m_FillSizing = FillSizing.Parent; [SerializeField] private float m_MinWidth = 0f; [SerializeField] private float m_MaxWidth = 100f; [SerializeField][Range(0f, 1f)] private float m_FillAmount = 1f; [SerializeField] private int m_Steps = 0; public ChangeEvent onChange = new ChangeEvent(); /// /// Gets or sets the type. /// /// The type. public Type type { get { return this.m_Type; } set { this.m_Type = value; } } /// /// Gets or sets the target image. /// /// The target image. public Image targetImage { get { return this.m_TargetImage; } set { this.m_TargetImage = value; } } /// /// Gets or sets array with the animation sprites. /// public Sprite[] sprites { get { return this.m_Sprites; } set { this.m_Sprites = value; } } /// /// Gets or sets the target transform. /// /// The target transform. public RectTransform targetTransform { get { return this.m_TargetTransform; } set { this.m_TargetTransform = value; } } /// /// Gets or sets the minimum width (Used for the resize type bar). /// /// The minimum width. public float minWidth { get { return this.m_MinWidth; } set { this.m_MinWidth = value; this.UpdateBarFill(); } } /// /// Gets or sets the maximum width (Used for the resize type bar). /// /// The maximum width. public float maxWidth { get { return this.m_MaxWidth; } set { this.m_MaxWidth = value; this.UpdateBarFill(); } } /// /// Gets or sets the fill amount. /// /// The fill amount. public float fillAmount { get { return this.m_FillAmount; } set { if (this.m_FillAmount != Mathf.Clamp01(value)) { this.m_FillAmount = Mathf.Clamp01(value); this.UpdateBarFill(); this.onChange.Invoke(this.m_FillAmount); } } } /// /// Gets or sets the steps (Zero for no stepping). /// /// The steps. public int steps { get { return this.m_Steps; } set { this.m_Steps = value; } } /// /// Gets or sets the current step. /// /// The current step. public int currentStep { get { if (this.m_Steps == 0) return 0; float perStep = 1f / (this.m_Steps - 1); return Mathf.RoundToInt(this.fillAmount / perStep); } set { if (this.m_Steps > 0) { float perStep = 1f / (this.m_Steps - 1); this.fillAmount = Mathf.Clamp(value, 0, this.m_Steps) * perStep; } } } protected virtual void Start() { // Make sure the fill anchor reflects the pivot if (this.m_Type == Type.Resize && this.m_FillSizing == FillSizing.Parent && this.m_TargetTransform != null) { float height = this.m_TargetTransform.rect.height; this.m_TargetTransform.anchorMin = this.m_TargetTransform.pivot; this.m_TargetTransform.anchorMax = this.m_TargetTransform.pivot; this.m_TargetTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height); } // Update the bar fill this.UpdateBarFill(); } protected virtual void OnRectTransformDimensionsChange() { // Update the bar fill this.UpdateBarFill(); } #if UNITY_EDITOR protected void OnValidate() { // Make sure the fill anchor reflects the pivot if (this.m_Type == Type.Resize && this.m_FillSizing == FillSizing.Parent && this.m_TargetTransform != null) { float height = this.m_TargetTransform.rect.height; this.m_TargetTransform.anchorMin = this.m_TargetTransform.pivot; this.m_TargetTransform.anchorMax = this.m_TargetTransform.pivot; this.m_TargetTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height); } } protected void Reset() { this.onChange = new ChangeEvent(); } #endif /// /// Updates the bar fill. /// public void UpdateBarFill() { if (!this.isActiveAndEnabled) return; if (this.m_Type == Type.Filled && this.m_TargetImage == null) return; if (this.m_Type == Type.Resize && this.m_TargetTransform == null) return; if (this.m_Type == Type.Sprites && this.m_Sprites.Length == 0) return; // Get the fill amount float fill = this.m_FillAmount; // Check for steps if (this.m_Steps > 0) fill = Mathf.Round(this.m_FillAmount * (this.m_Steps - 1)) / (this.m_Steps - 1); if (this.m_Type == Type.Resize) { // Update the bar fill by changing it's width // we are doing it this way because we are using a mask on the bar and have it's fill inside with static width and position if (this.m_FillSizing == FillSizing.Fixed) { this.m_TargetTransform.SetSizeWithCurrentAnchors( RectTransform.Axis.Horizontal, (this.m_MinWidth + ((this.m_MaxWidth - this.m_MinWidth) * fill)) ); } else { this.m_TargetTransform.SetSizeWithCurrentAnchors( RectTransform.Axis.Horizontal, ((this.m_TargetTransform.parent as RectTransform).rect.width * fill) ); } } else if (this.m_Type == Type.Sprites) { int spriteIndex = Mathf.RoundToInt(fill * (float)this.m_Sprites.Length) - 1; if (spriteIndex > -1) { this.targetImage.overrideSprite = this.m_Sprites[spriteIndex]; this.targetImage.canvasRenderer.SetAlpha(1f); } else { this.targetImage.overrideSprite = null; this.targetImage.canvasRenderer.SetAlpha(0f); } } else { // Update the image fill amount this.m_TargetImage.fillAmount = fill; } } /// /// Adds to the fill (Used for buttons). /// public void AddFill() { if (this.m_Steps > 0) { this.currentStep = this.currentStep + 1; } else { this.fillAmount = this.fillAmount + 0.1f; } } /// /// Removes from the fill (Used for buttons). /// public void RemoveFill() { if (this.m_Steps > 0) { this.currentStep = this.currentStep - 1; } else { this.fillAmount = this.fillAmount - 0.1f; } } } }