using System; using System.Collections.Generic; using System.Text; using System.Threading; using BITKit.Mod; using Cysharp.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using UnityEngine; using UnityEngine.UIElements; using Object = UnityEngine.Object; namespace BITKit.UX { /// /// 适用于Unity的UX Service /// public class UXService : IUXService { private readonly IAfterTicker _ticker; private readonly IServiceProvider _serviceProvider; private readonly CancellationTokenSource _cancellationTokenSource; public UXService(IAfterTicker ticker, IServiceProvider serviceProvider, CancellationTokenSource cancellationTokenSource) { _ticker = ticker; _serviceProvider = serviceProvider; _cancellationTokenSource = cancellationTokenSource; _entryGroup.OnEntry += OnEntry; _ticker.Add(OnTick); } private readonly EntryGroup _entryGroup = new(); private readonly EntryGroup _windowEntryGroup = new(); /// /// 内部注册面板队列 /// private readonly Queue _registryQueue = new(); /// /// 内部注销面板队列 /// private readonly Queue _unRegistryQueue = new(); /// /// 已注册面板字典 /// private readonly Dictionary _panels = new(); /// /// 等待启用的面板队列 /// private readonly Stack _entryQueue = new(); private readonly List _entryQueueByName = new(); /// /// 返回面板缓冲区 /// private readonly DoubleBuffer _returnBuffer = new(); /// /// 已启用面板 /// private IUXPanel _currentPanel; /// /// 历史面板 /// private static readonly Stack History = new(); /// /// 清空历史面板,通常用于关闭返回上一步 /// public static void ClearHistory() => History.Clear(); public object Root { get; private set; } public async UniTask InitializeAsync() { var gameObject = new GameObject("UXService"); Object.DontDestroyOnLoad(gameObject); _cancellationTokenSource.Token.Register(() => { Object.Destroy(gameObject); }); var document = gameObject.AddComponent(); try { var panelSettings =await ModService.LoadAsset("ux_panel_settings"); document.panelSettings = panelSettings; } catch (Exception e) { BIT4Log.Warning("未找到ux_panel_settings"); throw; } Root = document.rootVisualElement; } public void Register(IUXPanel panel) => _registryQueue.Enqueue(panel); public void UnRegister(IUXPanel panel) => _unRegistryQueue.Enqueue(panel); public void Entry() where T : IUXPanel { var panel = _serviceProvider.GetRequiredService(); Entry(panel); //Entry(typeof(T).Name); } public void Entry(IUXPanel panel) => _entryQueue.Push(panel); public void Entry(string panelName) => _entryQueueByName.TryAdd(panelName); public IUXPanel CurrentPanel => _currentPanel; public event Action OnPanelChanged; public void Return() { if(_windowEntryGroup.TryGetEntried(out _)) { _windowEntryGroup.Entry(-1); return; } if (History.TryPop(out var returnPanel)) { _returnBuffer.Release(returnPanel); } } private bool _initialized; private void OnEntry(IUXPanel obj) { OnPanelChanged?.Invoke(_currentPanel,obj); _currentPanel = obj; } private void OnTick(float delta) { try { while (_registryQueue.TryDequeue(out var result)) { if (result is null) continue; _entryGroup.list.Add(result); _panels.Set(result.Index, result); } while (_unRegistryQueue.TryDequeue(out var result)) { if (result is null) continue; _entryGroup.list.Remove(result); _panels.Remove(result.Index); } if (_returnBuffer.TryGetRelease(out var returnPanel)) { _entryGroup.Entry(x=>x.Index==returnPanel.Index); BITAppForUnity.AllowCursor.SetElements(this, returnPanel.AllowCursor); BITInputSystem.AllowInput.SetElements(this, returnPanel.AllowInput); } foreach (var panelName in _entryQueueByName) { if (!_panels.TryGetValue(panelName, out var panel))continue; _entryQueue.Push(panel); _entryQueueByName.TryRemove(panelName); break; } if (_entryQueue.TryPop(out var nextPanel)) { if (nextPanel.IsWindow) { _windowEntryGroup.Entry(nextPanel); return; } _windowEntryGroup.Entry(-1); History.Push(_currentPanel); _entryGroup.Entry(x=>x.Index==nextPanel.Index); BITAppForUnity.AllowCursor.SetElements(this, nextPanel.AllowCursor); BITInputSystem.AllowInput.SetElements(this, nextPanel.AllowInput); } if (_entryGroup.TryGetEntried(out var currentPanel)) { currentPanel.OnTick(Time.deltaTime); } if(_windowEntryGroup.TryGetEntried(out var windowPanel)) { windowPanel.OnTick(Time.deltaTime); } } catch (Exception e) { BIT4Log.LogException(e); } } public async void Dispose() { _ticker.Remove(OnTick); await UniTask.SwitchToMainThread(); if (_currentPanel is not null) { // ReSharper disable once MethodHasAsyncOverload _currentPanel.Exit(); await _currentPanel.ExitAsync(); _currentPanel.Exited(); } } } }