using UnityEngine; using R3; using System.Collections.Generic; using System; using UnityEngine.UIElements; using UnityEngine.Pool; using KitsuneCafe.System; using System.Threading; 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.transform.SetParent(root); var doc = obj.GetComponent(); doc.panelSettings = panelSettings; var poolObj = obj.AddComponent(); poolObj.objectPool = pool; 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 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 var doc)) { doc.visualTreeAsset = element.VisualTreeAsset; doc.panelSettings = panelSettings; ConfigureWhenReady(doc, element); } activeElements[id] = obj; //Observable.Timer(TimeSpan.FromSeconds(3)) // .Subscribe(_ => orchestrator.DespawnElement(id)) // .AddTo(obj); } private void ConfigureWhenReady(UIDocument document, IUIElement element) { var cts = new CancellationTokenSource(); Observable.EveryValueChanged(document, d => d.didStart) .Where(started => started) .Take(1) .DoCancelOnCompleted(cts) .Subscribe(f => { element.Configure(document.rootVisualElement); }) .AddTo(document.gameObject); } private void DespawnElement(ElementId id) { if (activeElements.TryGetValue(id, out GameObject obj)) { obj.GetComponent().Release(); activeElements.Remove(id); } } } }