2026-05-06 15:07:56 +02:00

416 lines
12 KiB
C#

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<GameObject> m_StepsGameObjects = new List<GameObject>();
[SerializeField] private List<StepFillInfo> m_OverrideFillList = new List<StepFillInfo>();
[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;
/// <summary>
/// Gets or sets the current step.
/// </summary>
/// <value>The value.</value>
public int step
{
get { return this.m_CurrentStep; }
set { this.GoToStep(value); }
}
/// <summary>
/// Determines whether this instance is active.
/// </summary>
/// <returns><c>true</c> if this instance is active; otherwise, <c>false</c>.</returns>
public virtual bool IsActive()
{
return this.enabled && this.gameObject.activeInHierarchy;
}
protected virtual void Start()
{
this.UpdateBubble();
}
#if UNITY_EDITOR
/// <summary>
/// Editor only!
/// </summary>
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();
}
/// <summary>
/// Raises the validate event.
/// </summary>
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
/// <summary>
/// Gets the fill override values list.
/// </summary>
/// <returns>The override fill list.</returns>
public List<StepFillInfo> GetOverrideFillList()
{
return this.m_OverrideFillList;
}
/// <summary>
/// Sets the fill override values list.
/// </summary>
/// <param name="list">List.</param>
public void SetOverrideFillList(List<StepFillInfo> list)
{
this.m_OverrideFillList = list;
}
/// <summary>
/// Validates the fill override values list.
/// </summary>
public void ValidateOverrideFillList()
{
// Create a temporary list
List<StepFillInfo> list = new List<StepFillInfo>();
// 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;
}
/// <summary>
/// Raises the rect transform dimensions change event.
/// </summary>
protected virtual void OnRectTransformDimensionsChange()
{
if (!this.IsActive())
return;
this.UpdateGridProperties();
}
/// <summary>
/// Goes to the specified step.
/// </summary>
/// <param name="step">Step.</param>
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();
}
/// <summary>
/// Updates the fill image.
/// </summary>
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);
}
/// <summary>
/// Updates the bubble.
/// </summary>
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);
}
}
/// <summary>
/// Gets the step fill amount.
/// </summary>
/// <returns>The step fill amount.</returns>
/// <param name="step">Step.</param>
public float GetStepFillAmount(int step)
{
return ((1f / (float)(this.m_StepsCount + 1)) * (float)step);
}
/// <summary>
/// Creates the steps grid.
/// </summary>
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<RectTransform>();
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<GridLayoutGroup>();
// 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();
}
/// <summary>
/// Updates the grid properties.
/// </summary>
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);
}
/// <summary>
/// Rebuilds the steps.
/// </summary>
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<Image>();
// Add to the list
this.m_StepsGameObjects.Add(step);
}
}
/// <summary>
/// Updates the steps properties.
/// </summary>
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<Image>();
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);
}
}
}
/// <summary>
/// Destroies the steps.
/// </summary>
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();
}
}
}