canto/Assets/Scripts/UI/Orchestration/UiSceneManager.cs
2025-07-18 20:38:44 -04:00

157 lines
No EOL
4.8 KiB
C#

using UnityEngine;
using R3;
using System.Collections.Generic;
using System;
using UnityEngine.UIElements;
using UnityEngine.Pool;
using KitsuneCafe.System;
using KitsuneCafe.Extension;
namespace KitsuneCafe.UI
{
public class UISceneManager : MonoBehaviour
{
[SerializeField] private UiOrchestrator orchestrator;
[SerializeField] private Transform root;
[SerializeField] private PanelSettings panelSettings;
[SerializeField] private GameObject prefab;
private readonly Dictionary<ElementId, GameObject> activeElements = new();
private readonly Dictionary<VisualTreeAsset, IObjectPool<GameObject>> pools = new();
private void Awake()
{
var d = Disposable.CreateBuilder();
Observable.FromEvent<EventHandler<SpawnElementRequest>, SpawnElementRequest>(
h => (sender, e) => h(e),
e => orchestrator.SpawnRequested += e,
e => orchestrator.SpawnRequested -= e
).Subscribe(request =>
{
SpawnElement(request.Id, request.Element, request.Position);
})
.AddTo(ref d);
Observable.FromEvent<EventHandler<DespawnElementRequest>, DespawnElementRequest>(
h => (sender, e) => h(e),
e => orchestrator.DespawnRequested += e,
e => orchestrator.DespawnRequested -= e
)
.Select(request => request.Id)
.Subscribe(DespawnElement)
.AddTo(ref d);
d.RegisterTo(destroyCancellationToken);
}
private IObjectPool<GameObject> GetOrCreatePool(IUiElement element)
{
if (!pools.TryGetValue(element.VisualTreeAsset, out var pool))
{
pool = new ObjectPool<GameObject>(
() => CreatePoolObject(pool),
GetPoolObject,
ReleasePoolObject,
DestroyPoolObject,
collectionCheck: false,
defaultCapacity: 10,
maxSize: 100
);
pools.Add(element.VisualTreeAsset, pool);
}
return pool;
}
private GameObject CreatePoolObject(IObjectPool<GameObject> pool)
{
GameObject obj;
if (prefab != null)
{
obj = Instantiate(prefab);
}
else
{
obj = new GameObject("Pooled Object", typeof(UIDocument));
}
obj.AddComponent<UiElementInstance>();
var doc = obj.GetComponent<UIDocument>();
doc.panelSettings = panelSettings;
var poolObj = obj.AddComponent<PooledObject>();
poolObj.objectPool = pool;
obj.transform.SetParent(root);
return obj;
}
private void GetPoolObject(GameObject obj)
{
obj.SetActive(true);
}
private void ReleasePoolObject(GameObject obj)
{
obj.SetActive(false);
}
private void DestroyPoolObject(GameObject obj)
{
Destroy(obj);
}
private GameObject GetUiDocument(IUiElement element)
{
return GetOrCreatePool(element).Get();
}
private Observable<T> ObserveEvent<T>(VisualElement target) where T : EventBase<T>, new()
{
return Observable.FromEvent<EventCallback<T>, T>(
h => e => h(e),
e => target.RegisterCallback(e),
e => target.UnregisterCallback(e)
);
}
private void SpawnElement(ElementId id, IUiElement element, Vector3 worldPosition)
{
var obj = GetUiDocument(element);
var trans = obj.transform;
trans.SetParent(root);
trans.SetPositionAndRotation(worldPosition, Quaternion.identity);
trans.localScale = Vector3.one;
if (obj.TryGetComponent(out UIDocument doc) && obj.TryGetComponent(out UiElementInstance instance))
{
doc.visualTreeAsset = element.VisualTreeAsset;
doc.panelSettings = panelSettings;
Observable.EveryValueChanged(doc, d => d.rootVisualElement)
.WhereNotNull()
.Select(_ => doc)
.Subscribe(doc =>
{
instance.Configure(element, doc.rootVisualElement);
})
.AddTo(doc.gameObject);
}
activeElements[id] = obj;
}
private void DespawnElement(ElementId id)
{
if (activeElements.TryGetValue(id, out GameObject obj))
{
obj.GetComponent<PooledObject>().Release();
activeElements.Remove(id);
}
}
}
}