石头维修美术替换
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user