99 lines
3.5 KiB
C#
99 lines
3.5 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace HotUpdate.UI
|
|
{
|
|
/// <summary>
|
|
/// 竖屏 UI 页面栈:加载 Prefab、打开/关闭、按名查找。
|
|
/// </summary>
|
|
public sealed class UiManager : MonoBehaviour
|
|
{
|
|
public static UiManager Instance { get; private set; }
|
|
|
|
readonly Dictionary<string, GameObject> _opened = new Dictionary<string, GameObject>(StringComparer.Ordinal);
|
|
Transform _root;
|
|
UiPrefabLoader _loader;
|
|
public UiAtlasLoader Atlases { get; private set; }
|
|
readonly HashSet<string> _loading = new HashSet<string>(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();
|
|
Atlases = GetComponent<UiAtlasLoader>();
|
|
if (Atlases == null) Atlases = gameObject.AddComponent<UiAtlasLoader>();
|
|
Atlases.Init(_loader.PreferredPackage);
|
|
Theme = UiThemeCatalog.Normalize(theme);
|
|
}
|
|
|
|
public IEnumerator OpenAsync(string prefabName, Action<GameObject> 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;
|
|
yield return Atlases.WaitForPending();
|
|
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;
|
|
}
|
|
}
|
|
}
|