canto/Assets/Scripts/UI/Orchestration/UISceneManager.cs

158 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 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<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.transform.SetParent(root);
var doc = obj.GetComponent<UIDocument>();
doc.panelSettings = panelSettings;
var poolObj = obj.AddComponent<PooledObject>();
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<UIDocument>(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<PooledObject>().Release();
activeElements.Remove(id);
}
}
}
}