using System; using System.Collections.Generic; using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif namespace Amanotes.Core { public enum Direction { Right, Left, Up, Down } [Serializable] public class SpriteTarget { public SpriteRenderer spriteRenderer; public Vector4 paddingPixels; [Range(0.1f, 20f)] public float adjustScale = 1f; [Range(0f, 1f)] public float fillAmount = 1f; public Direction fillDirection = Direction.Up; } public class SpriteRendererWidget : Widget2D { [SerializeField] private List _sprites = new List(); // Legacy single-target field (kept for backward compat / FormerlySerializedAs migration) [SerializeField, HideInInspector] private SpriteRenderer _spriteRenderer; [SerializeField, HideInInspector] private Vector4 paddingPixels; [SerializeField, HideInInspector] [Range(0.1f, 20f)] private float adjustScale = 1f; [SerializeField, HideInInspector] [Range(0f, 1f)] private float fillAmount = 1f; [SerializeField, HideInInspector] public Direction fillDirection = Direction.Up; /// Returns the first sprite target's SpriteRenderer (for legacy code: fill.target) public SpriteRenderer target => _sprites.Count > 0 ? _sprites[0].spriteRenderer : _spriteRenderer; public List sprites => _sprites; private bool _isInitialized; private void Awake() { Init(); } private void Init() { if (_isInitialized) return; _isInitialized = true; MigrateLegacyFields(); } /// Migrate old single-target serialized data into the new list. private void MigrateLegacyFields() { if (_spriteRenderer != null && _sprites.Count == 0) { _sprites.Add(new SpriteTarget { spriteRenderer = _spriteRenderer, paddingPixels = paddingPixels, adjustScale = adjustScale, fillAmount = fillAmount, fillDirection = fillDirection }); _spriteRenderer = null; } } public override void Refresh() { MigrateLegacyFields(); for (int i = 0; i < _sprites.Count; i++) { var st = _sprites[i]; if (st.spriteRenderer == null) continue; FitSprite(st); } ApplyRotation(); base.Refresh(); } private void ApplyRotation() { for (int i = 0; i < _sprites.Count; i++) { var sr = _sprites[i].spriteRenderer; if (sr == null) continue; var childTransform = sr.transform; if (_size.useZ) childTransform.localRotation = Quaternion.Euler(90f, 0f, 0f); else childTransform.localRotation = Quaternion.identity; } } /// Set fill progress on all sprites. public void SetProgress(float value) { for (int i = 0; i < _sprites.Count; i++) { var st = _sprites[i]; if (st.spriteRenderer == null) continue; // st.spriteRenderer.enabled = value > 0; st.fillAmount = value; } Refresh(); } /// Set fill progress on a specific sprite by index. public void SetProgress(int index, float value) { if (index < 0 || index >= _sprites.Count) return; var st = _sprites[index]; // if (st.spriteRenderer != null) st.spriteRenderer.enabled = value > 0; st.fillAmount = value; Refresh(); } private Rect GetTargetFullRect() { var pivot = _size.pivot; var sz = _size._size; return new Rect(-pivot.x * sz.x, -pivot.y * sz.y, sz.x, sz.y); } private Rect GetFillRect(SpriteTarget st) { var result = GetTargetFullRect(); switch (st.fillDirection) { case Direction.Up: result.height *= st.fillAmount; break; case Direction.Down: result.y += result.height * (1f - st.fillAmount); result.height *= st.fillAmount; break; case Direction.Right: result.width *= st.fillAmount; break; case Direction.Left: result.x += result.width * (1f - st.fillAmount); result.width *= st.fillAmount; break; } return result; } private void FitSprite(SpriteTarget st) { var sr = st.spriteRenderer; if (sr == null || sr.sprite == null) return; var sprite = sr.sprite; var sz = sprite.rect.size; var pv = sprite.pivot; var ppu = sprite.pixelsPerUnit; if (ppu <= 0f) ppu = 100f; var w = sz.x; var h = sz.y; var border = sprite.border; var bx = border.x; var by = border.y; var bz = border.z; var bw = border.w; var padding = st.paddingPixels; var l = Mathf.Min(padding.x, bx); var t = Mathf.Min(padding.y, by); var r = Mathf.Min(padding.z, bz); var b = Mathf.Min(padding.w, bw); var fillRect = GetFillRect(st); var scale = st.adjustScale; var newW = fillRect.width / scale + (l + r) / ppu; var newH = fillRect.height / scale + (t + b) / ppu; var selfSize = new Vector2(newW, newH); var selfOffset = new Vector2(pv.x / w * newW - l / ppu, pv.y / h * newH - b / ppu) * scale; var x = selfOffset.x + fillRect.x; var y = selfOffset.y + fillRect.y; sr.transform.localScale = Vector3.one * scale; sr.drawMode = SpriteDrawMode.Sliced; sr.size = selfSize; var lcPos = sr.transform.localPosition; sr.transform.localPosition = _size.useZ ? new Vector3(x, lcPos.y, y) : new Vector3(x, y, lcPos.z); } #if UNITY_EDITOR private void OnValidate() { if (PrefabUtility.IsPartOfPrefabAsset(this)) return; EditorApplication.delayCall += () => { if (this == null) return; Init(); Refresh(); }; } private void Reset() { _sprites.Clear(); var sr = GetComponentInChildren(); if (sr != null) { _sprites.Add(new SpriteTarget { spriteRenderer = sr, adjustScale = 1f, fillAmount = 1f, fillDirection = Direction.Up }); } } /// /// Merges every other SpriteRendererWidget on this GameObject into this instance: /// moves their sprite targets (with all settings intact) into this._sprites, /// then destroys the now-empty duplicate components. /// [ContextMenu("Merge Duplicate Sprite Renderer Widgets")] private void MergeDuplicateWidgets() { var all = GetComponents(); if (all.Length <= 1) { Debug.Log($"[{name}] No duplicate SpriteRendererWidget found to merge.", this); return; } MigrateLegacyFields(); Undo.RegisterCompleteObjectUndo(this, "Merge Sprite Renderer Widgets"); var mergedCount = 0; var skippedCount = 0; foreach (var other in all) { if (other == this) continue; other.MigrateLegacyFields(); foreach (var st in other._sprites) { if (st.spriteRenderer == null) continue; var isDuplicate = _sprites.Exists(existing => existing.spriteRenderer == st.spriteRenderer); if (isDuplicate) { Debug.LogWarning( $"[{name}] Skipped '{st.spriteRenderer.name}' — already present in the surviving widget.", this); skippedCount++; continue; } _sprites.Add(new SpriteTarget { spriteRenderer = st.spriteRenderer, paddingPixels = st.paddingPixels, adjustScale = st.adjustScale, fillAmount = st.fillAmount, fillDirection = st.fillDirection }); mergedCount++; } Undo.DestroyObjectImmediate(other); } EditorUtility.SetDirty(this); Init(); Refresh(); Debug.Log( $"[{name}] Merged {mergedCount} sprite target(s) from {all.Length - 1} duplicate widget(s)" + (skippedCount > 0 ? $", skipped {skippedCount} duplicate target(s)." : "."), this); } #endif } }