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 onValueChanged = new UnityEvent(); /// /// 进度条值变化事件 /// public UnityEvent OnValueChanged => onValueChanged; private readonly List _spriteRenderers = new List(); private bool _isInitialized = false; 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(); } } /// /// 获取用于界面显示的值(限制在 [0, MaxValue] 范围内) /// public int DisplayValue => Mathf.Clamp(currentValue, 0, maxValue); public float SpriteCount => spriteRendererCount; /// /// 获取所有 SpriteRenderer 的只读列表 /// public IReadOnlyList SpriteRenderers => _spriteRenderers; private void Awake() { if (!_isInitialized) { Initialize(); } } private void Start() { if (!_isInitialized) { Initialize(); } UpdateProgress(); } private void Initialize() { if (sprite == null) { Debug.LogWarning($"[SpriteProgressBar] {gameObject.name}: Sprite 未设置,无法创建进度条"); return; } // 只在运行时创建实体对象,编辑器模式下使用 Gizmos 预览 if (!Application.isPlaying) return; ClearSpriteRenderers(); CreateSpriteRenderers(); _isInitialized = true; } 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(); 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); // 只在运行时更新排序层级,编辑器模式下使用 Gizmos 预览 if (Application.isPlaying && _spriteRenderers.Count > 0) { UpdateSortingLayers(); } } /// /// 在 Scene 视图中绘制进度条预览(仅在编辑器模式下) /// private void OnDrawGizmosSelected() { if (Application.isPlaying || sprite == null || spriteRendererCount <= 0) return; // 计算应该显示的数量 int displayValue = DisplayValue; float normalizedValue = (float)displayValue / maxValue; int activeCount = Mathf.CeilToInt(normalizedValue * spriteRendererCount); activeCount = Mathf.Clamp(activeCount, 0, spriteRendererCount); // 获取 Sprite 的世界尺寸 Vector3 spriteSize = Vector3.one * 0.5f; if (sprite != null) { spriteSize = new Vector3( sprite.bounds.size.x, sprite.bounds.size.y, 0.1f ); } Gizmos.matrix = transform.localToWorldMatrix; for (int i = 0; i < spriteRendererCount; i++) { // 计算位置 Vector3 position = startPosition; if (horizontal) { position.x += i * spacing; } else { position.y += i * spacing; } bool isActive = i < activeCount; // 绘制边框 Gizmos.color = isActive ? Color.green : Color.gray; Gizmos.DrawWireCube(position, spriteSize * 0.9f); // 激活的绘制填充 if (isActive) { Gizmos.color = new Color(0, 1, 0, 0.3f); Gizmos.DrawCube(position, spriteSize * 0.8f); } } // 绘制进度条标题 #if UNITY_EDITOR UnityEditor.Handles.Label( transform.TransformPoint(startPosition) + Vector3.up * 0.5f, $"Progress: {currentValue}/{maxValue} ({activeCount}/{spriteRendererCount})" ); #endif } #endif private void UpdateSortingLayers() { foreach (var sr in _spriteRenderers) { if (sr != null) { sr.sortingLayerID = sortingLayerID; sr.sortingOrder = sortingOrder; } } } } }