Files
aibis-dream/Assets/Scripts/UI/Components/SpriteProgressBar.cs
T
2026-02-03 20:23:17 +08:00

318 lines
9.8 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace AibisDream.UI
{
public class SpriteProgressBar : MonoBehaviour
{
[Header("进度条设置")]
[SerializeField] private int spriteRendererCount = 10;
[SerializeField] private int maxValue = 100;
[SerializeField] private int currentValue = 0;
[Header("显示设置")]
[SerializeField] private Sprite sprite;
[SerializeField] private float spacing = 1f;
[SerializeField] private Vector3 startPosition = Vector3.zero;
[SerializeField] private bool horizontal = true;
[Header("渲染层级设置")]
[CustomSortingLayer]
[SerializeField] private int sortingLayerID = 0;
[SerializeField] private int sortingOrder = 0;
private UnityEvent<int> onValueChanged = new UnityEvent<int>();
/// <summary>
/// 进度条值变化事件
/// </summary>
public UnityEvent<int> OnValueChanged => onValueChanged;
private readonly List<SpriteRenderer> _spriteRenderers = new List<SpriteRenderer>();
private bool _isInitialized = false;
#if UNITY_EDITOR
private bool _editorUpdateScheduled = false;
#endif
public int CurrentValue
{
get => currentValue;
set
{
if (currentValue == value)
return;
// 允许超出范围,不进行限制
currentValue = value;
UpdateProgress();
}
}
public int MaxValue
{
get => maxValue;
set
{
if (maxValue == value)
return;
maxValue = Mathf.Max(1, value);
// 不再自动限制 currentValue,允许超出范围
UpdateProgress();
}
}
/// <summary>
/// 获取用于界面显示的值(限制在 [0, MaxValue] 范围内)
/// </summary>
public int DisplayValue => Mathf.Clamp(currentValue, 0, maxValue);
public float SpriteCount => spriteRendererCount;
/// <summary>
/// 获取所有 SpriteRenderer 的只读列表
/// </summary>
public IReadOnlyList<SpriteRenderer> SpriteRenderers => _spriteRenderers;
private void Awake()
{
#if UNITY_EDITOR
// 运行时启动时,清理编辑器模式下创建的子对象
if (Application.isPlaying)
{
ClearEditorCreatedChildren();
}
#endif
if (!_isInitialized)
{
Initialize();
}
}
private void Start()
{
if (!_isInitialized)
{
Initialize();
}
UpdateProgress();
}
private void Initialize()
{
if (sprite == null)
{
Debug.LogWarning($"[SpriteProgressBar] {gameObject.name}: Sprite 未设置,无法创建进度条");
return;
}
ClearSpriteRenderers();
CreateSpriteRenderers();
_isInitialized = true;
}
#if UNITY_EDITOR
/// <summary>
/// 清理编辑器模式下创建的子对象(仅在运行时调用)
/// </summary>
private void ClearEditorCreatedChildren()
{
// 清理所有子对象,避免编辑器模式下创建的对象与运行时对象冲突
while (transform.childCount > 0)
{
var child = transform.GetChild(0);
if (Application.isPlaying)
{
Destroy(child.gameObject);
}
else
{
DestroyImmediate(child.gameObject);
}
}
_spriteRenderers.Clear();
_isInitialized = false;
}
#endif
private void CreateSpriteRenderers()
{
if (sprite == null) return;
for (int i = 0; i < spriteRendererCount; i++)
{
GameObject spriteObj = new GameObject($"ProgressItem_{i + 1}");
spriteObj.transform.SetParent(transform);
#if UNITY_EDITOR
// 编辑器模式下创建的对象不保存到场景
if (!Application.isPlaying)
{
spriteObj.hideFlags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor;
}
#endif
// 计算位置
Vector3 position = startPosition;
if (horizontal)
{
position.x += i * spacing;
}
else
{
position.y += i * spacing;
}
spriteObj.transform.localPosition = position;
SpriteRenderer sr = spriteObj.AddComponent<SpriteRenderer>();
sr.sprite = sprite;
sr.sortingLayerID = sortingLayerID;
sr.sortingOrder = sortingOrder;
_spriteRenderers.Add(sr);
}
}
private void ClearSpriteRenderers()
{
#if UNITY_EDITOR
// 在编辑器中,需要特殊处理
if (!Application.isPlaying)
{
// 清理子对象
while (transform.childCount > 0)
{
DestroyImmediate(transform.GetChild(0).gameObject);
}
}
else
#endif
{
// 运行时清理
foreach (var sr in _spriteRenderers)
{
if (sr != null && sr.gameObject != null)
{
Destroy(sr.gameObject);
}
}
// 同时清理可能残留的子对象
while (transform.childCount > 0)
{
var child = transform.GetChild(0);
Destroy(child.gameObject);
}
}
_spriteRenderers.Clear();
}
private void UpdateProgress()
{
if (_spriteRenderers.Count == 0 || maxValue <= 0)
return;
// 使用限制后的值来计算界面显示(不影响实际存储的值)
int displayValue = DisplayValue;
// 计算应该显示的 SpriteRenderer 数量(向上取整)
float normalizedValue = (float)displayValue / maxValue;
int activeCount = Mathf.CeilToInt(normalizedValue * spriteRendererCount);
activeCount = Mathf.Clamp(activeCount, 0, spriteRendererCount);
// 更新显示状态和排序层级
for (int i = 0; i < _spriteRenderers.Count; i++)
{
if (_spriteRenderers[i] != null)
{
_spriteRenderers[i].gameObject.SetActive(i < activeCount);
// 确保排序层级始终是最新的
_spriteRenderers[i].sortingLayerID = sortingLayerID;
_spriteRenderers[i].sortingOrder = sortingOrder;
}
}
// 触发值变化事件
onValueChanged?.Invoke(currentValue);
}
#if UNITY_EDITOR
private void OnValidate()
{
// 确保值有效(但不限制 currentValue,允许超出范围)
spriteRendererCount = Mathf.Max(1, spriteRendererCount);
maxValue = Mathf.Max(1, maxValue);
// currentValue 不再限制,允许超出 [0, maxValue] 范围
spacing = Mathf.Max(0, spacing);
// 如果不在播放模式,在编辑器中预览
if (!Application.isPlaying)
{
// 如果 Sprite 已设置,在编辑器中预览
if (sprite != null)
{
// 延迟执行,避免在序列化过程中出现问题
if (!_editorUpdateScheduled)
{
_editorUpdateScheduled = true;
EditorApplication.delayCall += OnEditorUpdate;
}
}
else
{
// 如果没有 Sprite,清理现有的渲染器
if (_spriteRenderers.Count > 0)
{
ClearSpriteRenderers();
_isInitialized = false;
}
}
}
else
{
// 运行时如果排序层级改变,更新所有 SpriteRenderer
if (_spriteRenderers.Count > 0)
{
UpdateSortingLayers();
}
}
}
private void OnEditorUpdate()
{
_editorUpdateScheduled = false;
if (this == null || gameObject == null)
return;
// 移除延迟调用
EditorApplication.delayCall -= OnEditorUpdate;
if (!Application.isPlaying && sprite != null)
{
_isInitialized = false;
Initialize();
UpdateProgress();
}
}
#endif
private void UpdateSortingLayers()
{
foreach (var sr in _spriteRenderers)
{
if (sr != null)
{
sr.sortingLayerID = sortingLayerID;
sr.sortingOrder = sortingOrder;
}
}
}
}
}