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 activeElements = new(); private readonly Dictionary> pools = new(); private void Awake() { var d = Disposable.CreateBuilder(); Observable.FromEvent, 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, 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 GetOrCreatePool(IUiElement element) { if (!pools.TryGetValue(element.VisualTreeAsset, out var pool)) { pool = new ObjectPool( () => CreatePoolObject(pool), GetPoolObject, ReleasePoolObject, DestroyPoolObject, collectionCheck: false, defaultCapacity: 10, maxSize: 100 ); pools.Add(element.VisualTreeAsset, pool); } return pool; } private GameObject CreatePoolObject(IObjectPool pool) { GameObject obj; if (prefab != null) { obj = Instantiate(prefab); } else { obj = new GameObject("Pooled Object", typeof(UIDocument)); } obj.AddComponent(); var doc = obj.GetComponent(); doc.panelSettings = panelSettings; var poolObj = obj.AddComponent(); 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 ObserveEvent(VisualElement target) where T : EventBase, new() { return Observable.FromEvent, 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().Release(); activeElements.Remove(id); } } } }