using Nova; using System.Text; using UnityEngine; namespace NovaSamples.UIControls { /// /// The style of a progress bar /// public enum ProgressBarStyle { /// /// Radial, expands clockwise as Percent increases /// Clockwise, /// /// Radial, expands counterclockwise as Percent increases /// CounterClockwise, /// /// Linear, expands vertically as Percent increases /// Vertical, /// /// Linear, expands horizontally as Percent increases /// Horizontal, } /// /// A simple example of a ProgressBar component which can be used to display a value between [0, 1] in a variety of s. /// public class ProgressBar : MonoBehaviour { const int MinDecimals = 0; const int MaxDecimals = 4; // Special handling of 0% and 100% text formats private const string ZeroFormat = "0"; private const string OneHundredFormat = "100"; private const string ZeroPercentFormat = "0%"; private const string OneHundredPercentFormat = "100%"; [SerializeField, Range(0, 1), Tooltip("The current percent/progress amount.")] private float percent = 0.5f; [Header("Progress Indicator")] [Tooltip("The UI Block 2D whose properties will be modified to indicate the progress state.")] public UIBlock2D Fill = null; [Tooltip("How the Percent will be visualized.")] public ProgressBarStyle Style = ProgressBarStyle.CounterClockwise; [Header("Text Format")] [Tooltip("The Text Block use to display the fill percent.")] public TextBlock Text = null; [SerializeField, Range(MinDecimals, MaxDecimals), Tooltip("The decimal precision of the text string.")] private int decimalCount = 0; [SerializeField, Tooltip("Should \"%\" be appended onto the text string?")] private bool displayPercentSymbol = true; /// /// A string builder used internal to generate the string. /// private StringBuilder textFormatBuilder = new StringBuilder(); /// /// The format to use when displaying the current /// progress state on the visual. /// private string textFormat = string.Empty; /// /// Assign the current progress percent /// and update visuals accordingly. /// public float Percent { get => percent; set { percent = Mathf.Clamp01(value); UpdateProgressVisuals(); } } /// /// Should '%' be appended to the displayed text? /// public bool DisplayPercentSymbol { get => displayPercentSymbol; set { if (displayPercentSymbol != value) { displayPercentSymbol = value; UpdateTextFormat(); } UpdateTextVisual(); } } /// /// The decimal precision of the displayed text. /// public int DecimalCount { get => decimalCount; set { if (decimalCount != value) { decimalCount = value; UpdateTextFormat(); } UpdateTextVisual(); } } private void Awake() { UpdateTextFormat(); } /// /// Update the fill and text visuals /// private void UpdateProgressVisuals() { UpdateFillVisual(); UpdateTextVisual(); } /// /// Update the visual based on . /// private void UpdateFillVisual() { if (Fill == null) { return; } if (Percent == GetDisplayedPercent()) { // Currently displaying the right percent. return; } switch (Style) { case ProgressBarStyle.Clockwise: Fill.RadialFill.Enabled = true; Fill.RadialFill.FillAngle = Percent * -360; break; case ProgressBarStyle.CounterClockwise: Fill.RadialFill.Enabled = true; Fill.RadialFill.FillAngle = Percent * 360; break; case ProgressBarStyle.Vertical: Fill.AutoSize.Y = AutoSize.None; Fill.Size.Y = Length.Percentage(Percent * (1 - Fill.CalculatedMargin.Y.Sum().Percent)); break; case ProgressBarStyle.Horizontal: Fill.AutoSize.X = AutoSize.None; Fill.Size.X = Length.Percentage(Percent * (1 - Fill.CalculatedMargin.X.Sum().Percent)); break; } } /// /// Update the visual based on . /// private void UpdateTextVisual() { if (Text == null) { return; } string format = textFormat; // Special case 0 and 1 for "cleaner" visuals if (Percent == 0) { format = DisplayPercentSymbol ? ZeroPercentFormat : ZeroFormat; } else if (Percent == 1) { format = DisplayPercentSymbol ? OneHundredPercentFormat : OneHundredFormat; } // The percent string formats handle the 100x multiplication, // but the non-percent formats do not. float scaledPercent = DisplayPercentSymbol ? Percent : Percent * 100; string text = string.Format(format, scaledPercent); if (Text.Text == text) { return; } Text.Text = text; } /// /// Update the desired text format based on the and . /// private void UpdateTextFormat() { textFormatBuilder.Clear(); // If decimal count is 0, don't append a '.' textFormatBuilder.Append(DecimalCount == 0 ? "{0:00" : "{0:00."); // Append however many decimals we want for (int i = 0; i < DecimalCount; ++i) { textFormatBuilder.Append("0"); } // Append the % symbol if we want it if (DisplayPercentSymbol) { textFormatBuilder.Append("%"); } // End the format textFormatBuilder.Append("}"); // Apply to the text format textFormat = textFormatBuilder.ToString(); } /// /// Get the percent currently displayed based on the properties of the visual. /// private float GetDisplayedPercent() { switch (Style) { case ProgressBarStyle.Clockwise: return Fill.RadialFill.FillAngle / -360; case ProgressBarStyle.CounterClockwise: return Fill.RadialFill.FillAngle / 360; case ProgressBarStyle.Vertical: return Fill.CalculatedSize.Y.Percent / Mathf.Clamp01(1 - Fill.CalculatedMargin.Y.Sum().Percent); default: // ProgressStyle.Horizontal return Fill.CalculatedSize.X.Percent / Mathf.Clamp01(1 - Fill.CalculatedMargin.X.Sum().Percent); } } /// /// Update the visual state in editor /// private void OnValidate() { UpdateTextFormat(); // This handles ensuring percent is clamped // and will update the visuals in edit mode. Percent = percent; } } }