using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace HotUpdate.UI { /// /// 竖屏 UI 页面栈:加载 Prefab、打开/关闭、按名查找。 /// public sealed class UiManager : MonoBehaviour { public static UiManager Instance { get; private set; } readonly Dictionary _opened = new Dictionary(StringComparer.Ordinal); Transform _root; UiPrefabLoader _loader; readonly HashSet _loading = new HashSet(StringComparer.Ordinal); public string Theme { get; private set; } = UiThemeCatalog.Candy; public void Init(Transform root, UiPrefabLoader loader, string theme = UiThemeCatalog.Candy) { Instance = this; _root = root; _loader = loader ?? new UiPrefabLoader(); Theme = UiThemeCatalog.Normalize(theme); } public IEnumerator OpenAsync(string prefabName, Action onOpened, int siblingIndex = -1) { if (string.IsNullOrEmpty(prefabName)) { onOpened?.Invoke(null); yield break; } while (_loading.Contains(prefabName)) yield return null; if (_opened.TryGetValue(prefabName, out var existing) && existing != null) { existing.SetActive(true); onOpened?.Invoke(existing); yield break; } _loading.Add(prefabName); GameObject prefab = null; try { var assetName = UiThemeCatalog.Resolve(prefabName, Theme); yield return _loader.LoadAsync(assetName, loaded => prefab = loaded); if (prefab == null && assetName != prefabName) { Debug.LogWarning($"[UI] Missing {assetName}; falling back to {prefabName}. Include both themes in the resource package."); yield return _loader.LoadAsync(prefabName, loaded => prefab = loaded); } } finally { _loading.Remove(prefabName); } if (prefab == null) { Debug.LogError($"[UiManager] prefab not found: {prefabName}"); onOpened?.Invoke(null); yield break; } var go = Instantiate(prefab, _root, false); go.name = prefabName; if (siblingIndex >= 0) go.transform.SetSiblingIndex(siblingIndex); _opened[prefabName] = go; onOpened?.Invoke(go); } public void Close(string prefabName, bool destroy = false) { if (!_opened.TryGetValue(prefabName, out var go) || go == null) return; if (destroy) { Destroy(go); _opened.Remove(prefabName); } else go.SetActive(false); } public GameObject Get(string prefabName) { _opened.TryGetValue(prefabName, out var go); return go; } void OnDestroy() { StopAllCoroutines(); _loader?.Dispose(); if (Instance == this) Instance = null; } } }