using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; namespace DuloGames.UI { [AddComponentMenu("UI/Bars/Step Bar")] public class UIStepBar : MonoBehaviour { [System.Serializable] public struct StepFillInfo { public int index; public float amount; } [SerializeField] private List m_StepsGameObjects = new List(); [SerializeField] private List m_OverrideFillList = new List(); [SerializeField] private GameObject m_StepsGridGameObject; [SerializeField] private RectTransform m_StepsGridRect; [SerializeField] private GridLayoutGroup m_StepsGrid; [SerializeField] private int m_CurrentStep = 0; [SerializeField] private int m_StepsCount = 1; [SerializeField] private RectOffset m_StepsGridPadding = new RectOffset(); [SerializeField] private Sprite m_SeparatorSprite; [SerializeField] private Sprite m_SeparatorSpriteActive; [SerializeField] private Color m_SeparatorSpriteColor = Color.white; [SerializeField] private bool m_SeparatorAutoSize = true; [SerializeField] private Vector2 m_SeparatorSize = Vector2.zero; [SerializeField] private Image m_FillImage; [SerializeField] private RectTransform m_BubbleRect; [SerializeField] private Vector2 m_BubbleOffset = Vector2.zero; [SerializeField] private Text m_BubbleText; /// /// Gets or sets the current step. /// /// The value. public int step { get { return this.m_CurrentStep; } set { this.GoToStep(value); } } /// /// Determines whether this instance is active. /// /// true if this instance is active; otherwise, false. public virtual bool IsActive() { return this.enabled && this.gameObject.activeInHierarchy; } protected virtual void Start() { this.UpdateBubble(); } #if UNITY_EDITOR /// /// Editor only! /// public void RebuildSteps_Editor() { // Create the steps grid if required this.CreateStepsGrid(); // Update the grid properties this.UpdateGridProperties(); // Rebuild the steps if required this.RebuildSteps(); // Update the steps properties this.UpdateStepsProperties(); // Update the bubble this.UpdateBubble(); } /// /// Raises the validate event. /// protected virtual void OnValidate() { // Validate the step integers if (this.m_CurrentStep < 0) this.m_CurrentStep = 0; if (this.m_StepsCount < 1) this.m_StepsCount = 1; if (this.m_CurrentStep > this.m_StepsCount) this.m_CurrentStep = this.m_StepsCount + 1; // Create the steps grid if required //this.CreateStepsGrid(); // Update the grid properties this.UpdateGridProperties(); // Rebuild the steps if required //this.RebuildSteps(); // Update the steps properties this.UpdateStepsProperties(); // Validate the fill image if (this.m_FillImage != null) { // Validate the image type if (this.m_FillImage.type != Image.Type.Filled) this.m_FillImage.type = Image.Type.Filled; // Validate the fill method if (this.m_FillImage.fillMethod != Image.FillMethod.Horizontal) this.m_FillImage.fillMethod = Image.FillMethod.Horizontal; // Update the fill amount this.UpdateFillImage(); } // Update the bubble this.UpdateBubble(); } #endif /// /// Gets the fill override values list. /// /// The override fill list. public List GetOverrideFillList() { return this.m_OverrideFillList; } /// /// Sets the fill override values list. /// /// List. public void SetOverrideFillList(List list) { this.m_OverrideFillList = list; } /// /// Validates the fill override values list. /// public void ValidateOverrideFillList() { // Create a temporary list List list = new List(); // Copy the current list to array StepFillInfo[] tempArr = this.m_OverrideFillList.ToArray(); // Loop foreach (StepFillInfo info in tempArr) { // Add all the valid step infos to the temporary list if (info.index > 1 && info.index <= this.m_StepsCount && info.amount > 0f) list.Add(info); } // Set the temporary list as the override list this.m_OverrideFillList = list; } /// /// Raises the rect transform dimensions change event. /// protected virtual void OnRectTransformDimensionsChange() { if (!this.IsActive()) return; this.UpdateGridProperties(); } /// /// Goes to the specified step. /// /// Step. public void GoToStep(int step) { // Validate the step if (step < 0) step = 0; if (step > this.m_StepsCount) step = this.m_StepsCount + 1; // Set the step this.m_CurrentStep = step; // Update the steps properties this.UpdateStepsProperties(); // Update the fill amount this.UpdateFillImage(); // Update the bubble this.UpdateBubble(); } /// /// Updates the fill image. /// public void UpdateFillImage() { if (this.m_FillImage == null) return; int overrideIndex = this.m_OverrideFillList.FindIndex(x => x.index == this.m_CurrentStep); // Apply fill amount this.m_FillImage.fillAmount = (overrideIndex >= 0) ? this.m_OverrideFillList[overrideIndex].amount : this.GetStepFillAmount(this.m_CurrentStep); } /// /// Updates the bubble. /// public void UpdateBubble() { if (this.m_BubbleRect == null) return; // Determine if the bubble should be visible if (this.m_CurrentStep > 0 && this.m_CurrentStep <= this.m_StepsCount) { // Activate if required if (!this.m_BubbleRect.gameObject.activeSelf) this.m_BubbleRect.gameObject.SetActive(true); // Get the step game object GameObject stepObject = this.m_StepsGameObjects[this.m_CurrentStep]; if (stepObject != null) { RectTransform stepRect = stepObject.transform as RectTransform; if (stepRect.anchoredPosition.x != 0f) { // Update the bubble position based on the location of the step this.m_BubbleRect.anchoredPosition = new Vector2(this.m_BubbleOffset.x + (stepRect.anchoredPosition.x + (stepRect.rect.width / 2f)), this.m_BubbleOffset.y); } } // Update the bubble text if (this.m_BubbleText != null) this.m_BubbleText.text = this.m_CurrentStep.ToString(); } else { // Deactivate if required if (this.m_BubbleRect.gameObject.activeSelf) this.m_BubbleRect.gameObject.SetActive(false); } } /// /// Gets the step fill amount. /// /// The step fill amount. /// Step. public float GetStepFillAmount(int step) { return ((1f / (float)(this.m_StepsCount + 1)) * (float)step); } /// /// Creates the steps grid. /// protected void CreateStepsGrid() { if (this.m_StepsGridGameObject != null) return; // Create new game object this.m_StepsGridGameObject = new GameObject("Steps Grid", typeof(RectTransform), typeof(GridLayoutGroup)); this.m_StepsGridGameObject.layer = this.gameObject.layer; this.m_StepsGridGameObject.transform.SetParent(this.transform, false); this.m_StepsGridGameObject.transform.localScale = new Vector3(1f, 1f, 1f); this.m_StepsGridGameObject.transform.localPosition = Vector3.zero; this.m_StepsGridGameObject.transform.SetAsLastSibling(); // Get the rect transform this.m_StepsGridRect = this.m_StepsGridGameObject.GetComponent(); this.m_StepsGridRect.sizeDelta = new Vector2(0f, 0f); this.m_StepsGridRect.anchorMin = new Vector2(0f, 0f); this.m_StepsGridRect.anchorMax = new Vector2(1f, 1f); this.m_StepsGridRect.pivot = new Vector2(0f, 1f); this.m_StepsGridRect.anchoredPosition = new Vector2(0f, 0f); // Get the grid layout group this.m_StepsGrid = this.m_StepsGridGameObject.GetComponent(); // Set the bubble as last sibling if (this.m_BubbleRect != null) this.m_BubbleRect.SetAsLastSibling(); // Clear the steps game objects list this.m_StepsGameObjects.Clear(); } /// /// Updates the grid properties. /// public void UpdateGridProperties() { if (this.m_StepsGrid == null) return; int seps = this.m_StepsCount + 2; // Grid Padding if (!this.m_StepsGrid.padding.Equals(this.m_StepsGridPadding)) this.m_StepsGrid.padding = this.m_StepsGridPadding; // Auto sizing if (this.m_SeparatorAutoSize && this.m_SeparatorSprite != null) { this.m_SeparatorSize = new Vector2(this.m_SeparatorSprite.rect.width, this.m_SeparatorSprite.rect.height); } if (!this.m_StepsGrid.cellSize.Equals(this.m_SeparatorSize)) this.m_StepsGrid.cellSize = this.m_SeparatorSize; // Grid spacing float spacingX = Mathf.Floor((this.m_StepsGridRect.rect.width - (float)this.m_StepsGridPadding.horizontal - ((float)seps * this.m_SeparatorSize.x)) / (float)(seps - 1) / 2f) * 2f; if (this.m_StepsGrid.spacing.x != spacingX) this.m_StepsGrid.spacing = new Vector2(spacingX, 0f); } /// /// Rebuilds the steps. /// public void RebuildSteps() { if (this.m_StepsGridGameObject == null) return; // Check if we already have the steps if (this.m_StepsGameObjects.Count == (this.m_StepsCount + 2)) return; // Destroy the steps this.DestroySteps(); int seps = this.m_StepsCount + 2; // Create the steps for (int i = 0; i < seps; i++) { GameObject step = new GameObject("Step " + i.ToString(), typeof(RectTransform)); step.layer = this.gameObject.layer; step.transform.localScale = new Vector3(1f, 1f, 1f); step.transform.localPosition = Vector3.zero; step.transform.SetParent(this.m_StepsGridGameObject.transform, false); if (i > 0 && i < (seps - 1)) step.AddComponent(); // Add to the list this.m_StepsGameObjects.Add(step); } } /// /// Updates the steps properties. /// protected void UpdateStepsProperties() { // Loop through the options foreach (GameObject stepObject in this.m_StepsGameObjects) { int index = this.m_StepsGameObjects.IndexOf(stepObject) + 1; bool active = index <= this.m_CurrentStep; // Image Image image = stepObject.GetComponent(); if (image != null) { image.sprite = this.m_SeparatorSprite; image.overrideSprite = (active) ? this.m_SeparatorSpriteActive : null; image.color = this.m_SeparatorSpriteColor; image.rectTransform.pivot = new Vector2(0f, 1f); } } } /// /// Destroies the steps. /// protected void DestroySteps() { if (Application.isPlaying) { foreach (GameObject g in this.m_StepsGameObjects) Destroy(g); } else { #if UNITY_EDITOR GameObject[] objects = this.m_StepsGameObjects.ToArray(); UnityEditor.EditorApplication.delayCall += ()=> { foreach (GameObject g in objects) DestroyImmediate(g); }; #endif } // Clear the list this.m_StepsGameObjects.Clear(); } } }