BITKit/Src/Unity/Scripts/UX/Service/UXService.cs

257 lines
6.6 KiB
C#
Raw Normal View History

2023-08-23 01:59:26 +08:00
using System;
using System.Collections.Generic;
2024-08-13 18:42:51 +08:00
using System.Text;
2024-12-25 11:35:30 +08:00
using System.Threading;
2024-11-03 16:38:17 +08:00
using BITKit.Mod;
2025-03-24 14:42:40 +08:00
using BITKit.StateMachine;
2024-11-03 16:38:17 +08:00
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
2025-02-24 23:02:43 +08:00
using Unity.Mathematics;
2023-08-23 01:59:26 +08:00
using UnityEngine;
2025-02-24 23:02:43 +08:00
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
2024-11-03 16:38:17 +08:00
using UnityEngine.UIElements;
using Object = UnityEngine.Object;
2023-08-23 01:59:26 +08:00
namespace BITKit.UX
{
/// <summary>
/// 适用于Unity的UX Service
/// </summary>
2024-11-03 16:38:17 +08:00
public class UXService : IUXService
2023-08-23 01:59:26 +08:00
{
2024-11-03 16:38:17 +08:00
private readonly IAfterTicker _ticker;
private readonly IServiceProvider _serviceProvider;
2024-12-25 11:35:30 +08:00
private readonly CancellationTokenSource _cancellationTokenSource;
public UXService(IAfterTicker ticker, IServiceProvider serviceProvider, CancellationTokenSource cancellationTokenSource)
2023-08-23 01:59:26 +08:00
{
2024-11-03 16:38:17 +08:00
_ticker = ticker;
_serviceProvider = serviceProvider;
2024-12-25 11:35:30 +08:00
_cancellationTokenSource = cancellationTokenSource;
2025-03-24 14:42:40 +08:00
_entryGroup.OnStateChanged += OnEntry;
2025-02-24 23:02:43 +08:00
2025-03-24 14:42:40 +08:00
_windowEntryGroup.OnStateChanged += OnWindowEntry;
2024-11-03 16:38:17 +08:00
_ticker.Add(OnTick);
2023-08-23 01:59:26 +08:00
}
2025-03-24 14:42:40 +08:00
private void OnWindowEntry(IUXPanel prev, IUXPanel next)
2025-02-24 23:02:43 +08:00
{
2025-03-24 14:42:40 +08:00
BITAppForUnity.AllowCursor.SetElements(_windowEntryGroup, next is { AllowCursor: true });
BITInputSystem.AllowInput.SetDisableElements(_windowEntryGroup, next is { AllowInput: false });
2025-02-24 23:02:43 +08:00
}
2025-03-24 14:42:40 +08:00
private readonly AsyncStateMachine<IUXPanel> _entryGroup = new();
private readonly AsyncStateMachine<IUXPanel> _windowEntryGroup = new();
2023-08-23 01:59:26 +08:00
/// <summary>
/// 内部注册面板队列
/// </summary>
2024-11-03 16:38:17 +08:00
private readonly Queue<IUXPanel> _registryQueue = new();
2023-08-23 01:59:26 +08:00
/// <summary>
/// 内部注销面板队列
/// </summary>
2024-11-03 16:38:17 +08:00
private readonly Queue<IUXPanel> _unRegistryQueue = new();
2023-08-23 01:59:26 +08:00
/// <summary>
/// 已注册面板字典
/// </summary>
2024-11-03 16:38:17 +08:00
private readonly Dictionary<string, IUXPanel> _panels = new();
2023-08-23 01:59:26 +08:00
/// <summary>
/// 等待启用的面板队列
/// </summary>
2024-11-03 16:38:17 +08:00
private readonly Stack<IUXPanel> _entryQueue = new();
private readonly List<string> _entryQueueByName = new();
2023-08-23 01:59:26 +08:00
2024-06-14 16:16:13 +08:00
/// <summary>
/// 返回面板缓冲区
/// </summary>
2024-11-03 16:38:17 +08:00
private readonly DoubleBuffer<IUXPanel> _returnBuffer = new();
2024-06-14 16:16:13 +08:00
2023-08-23 01:59:26 +08:00
/// <summary>
/// 已启用面板
/// </summary>
2024-11-03 16:38:17 +08:00
private IUXPanel _currentPanel;
2023-08-23 01:59:26 +08:00
/// <summary>
/// 历史面板
/// </summary>
2024-08-13 18:42:51 +08:00
private static readonly Stack<IUXPanel> History = new();
2024-08-05 09:53:22 +08:00
/// <summary>
/// 清空历史面板,通常用于关闭返回上一步
/// </summary>
public static void ClearHistory() => History.Clear();
2023-08-23 01:59:26 +08:00
2025-01-12 11:13:19 +08:00
public string SettingsPath { get; set; } = "ux_panel_settings";
2024-11-03 16:38:17 +08:00
public object Root { get; private set; }
2025-02-24 23:02:43 +08:00
public static VisualElement RootVisualElement { get; private set; }
2024-11-03 16:38:17 +08:00
public async UniTask InitializeAsync()
{
var gameObject = new GameObject("UXService");
Object.DontDestroyOnLoad(gameObject);
2024-12-25 11:35:30 +08:00
_cancellationTokenSource.Token.Register(() =>
{
Object.Destroy(gameObject);
});
2024-11-03 16:38:17 +08:00
var document = gameObject.AddComponent<UIDocument>();
try
{
2025-02-24 23:02:43 +08:00
if (Touchscreen.current is not null && SettingsPath == "ux_panel_settings")
{
SettingsPath = "ux_panel_settings_mobile";
}
2025-01-12 11:13:19 +08:00
var panelSettings =await ModService.LoadAsset<PanelSettings>(SettingsPath);
2024-11-03 16:38:17 +08:00
document.panelSettings = panelSettings;
}
catch (Exception e)
{
BIT4Log.Warning<UXService>("未找到ux_panel_settings");
throw;
}
2025-02-24 23:02:43 +08:00
Root = RootVisualElement= document.rootVisualElement;
if (Touchscreen.current is not null)
{
RootVisualElement.AddToClassList("mobile");
}
2024-11-03 16:38:17 +08:00
}
public void Register(IUXPanel panel) => _registryQueue.Enqueue(panel);
2023-08-23 01:59:26 +08:00
2024-11-03 16:38:17 +08:00
public void UnRegister(IUXPanel panel) => _unRegistryQueue.Enqueue(panel);
2023-08-23 01:59:26 +08:00
2024-11-03 16:38:17 +08:00
public void Entry<T>() where T : IUXPanel
{
var panel = _serviceProvider.GetRequiredService<T>();
Entry(panel);
//Entry(typeof(T).Name);
}
2023-08-23 01:59:26 +08:00
2024-11-03 16:38:17 +08:00
public void Entry(IUXPanel panel) => _entryQueue.Push(panel);
public void Entry(string panelName) => _entryQueueByName.TryAdd(panelName);
2024-11-08 12:52:09 +08:00
public IUXPanel CurrentPanel => _currentPanel;
public event Action<IUXPanel, IUXPanel> OnPanelChanged;
2025-02-24 23:02:43 +08:00
public bool TryPick(float2 position, out object obj)
{
obj = null;
if (!EventSystem.current.IsPointerOverGameObject())
{
return false;
}
position.y = Screen.height - position.y;
var ve = RootVisualElement.panel.Pick(RuntimePanelUtils.ScreenToPanel(RootVisualElement.panel, position));
obj = ve;
return obj is not null;
}
2024-11-08 12:52:09 +08:00
2024-11-03 16:38:17 +08:00
public void Return()
2023-08-23 01:59:26 +08:00
{
2025-03-24 14:42:40 +08:00
if (_windowEntryGroup.CurrentState is not null)
2024-07-07 14:27:34 +08:00
{
2025-03-24 14:42:40 +08:00
_windowEntryGroup.DisposeState();
2024-07-07 14:27:34 +08:00
return;
}
2023-08-23 01:59:26 +08:00
if (History.TryPop(out var returnPanel))
{
2024-11-03 16:38:17 +08:00
_returnBuffer.Release(returnPanel);
2023-08-23 01:59:26 +08:00
}
}
2024-11-03 16:38:17 +08:00
2023-11-15 23:55:06 +08:00
2024-08-13 18:42:51 +08:00
private bool _initialized;
2024-11-03 16:38:17 +08:00
2025-03-24 14:42:40 +08:00
private void OnEntry(IUXPanel prev,IUXPanel next)
2023-11-15 23:55:06 +08:00
{
2025-03-24 14:42:40 +08:00
OnPanelChanged?.Invoke(_currentPanel,next);
_currentPanel = next;
2023-11-15 23:55:06 +08:00
}
2024-11-03 16:38:17 +08:00
private void OnTick(float delta)
2023-08-23 01:59:26 +08:00
{
2024-07-15 17:26:08 +08:00
try
2023-08-23 01:59:26 +08:00
{
2025-02-24 23:02:43 +08:00
2024-11-03 16:38:17 +08:00
while (_registryQueue.TryDequeue(out var result))
2024-07-15 17:26:08 +08:00
{
if (result is null) continue;
2025-03-24 14:42:40 +08:00
_entryGroup.Register(result);
2024-11-03 16:38:17 +08:00
_panels.Set(result.Index, result);
2024-07-15 17:26:08 +08:00
}
2023-11-30 00:25:43 +08:00
2024-11-03 16:38:17 +08:00
while (_unRegistryQueue.TryDequeue(out var result))
2024-07-15 17:26:08 +08:00
{
if (result is null) continue;
2025-03-24 14:42:40 +08:00
_entryGroup.UnRegister(result);
2024-11-03 16:38:17 +08:00
_panels.Remove(result.Index);
2024-07-15 17:26:08 +08:00
}
2023-08-23 01:59:26 +08:00
2024-11-03 16:38:17 +08:00
if (_returnBuffer.TryGetRelease(out var returnPanel))
2024-07-15 17:26:08 +08:00
{
2025-03-24 14:42:40 +08:00
_entryGroup.TransitionState(returnPanel);
2024-07-15 17:26:08 +08:00
BITAppForUnity.AllowCursor.SetElements(this, returnPanel.AllowCursor);
BITInputSystem.AllowInput.SetElements(this, returnPanel.AllowInput);
}
2024-11-03 16:38:17 +08:00
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))
2024-07-07 14:27:34 +08:00
{
2024-07-15 17:26:08 +08:00
if (nextPanel.IsWindow)
{
2025-03-24 14:42:40 +08:00
_windowEntryGroup.TransitionState(nextPanel);
2024-07-15 17:26:08 +08:00
return;
}
2025-03-24 14:42:40 +08:00
_windowEntryGroup.DisposeState();
2024-08-13 18:42:51 +08:00
History.Push(_currentPanel);
2025-03-24 14:42:40 +08:00
_entryGroup.TransitionState(nextPanel);
2024-07-15 17:26:08 +08:00
BITAppForUnity.AllowCursor.SetElements(this, nextPanel.AllowCursor);
BITInputSystem.AllowInput.SetElements(this, nextPanel.AllowInput);
}
2025-03-24 14:42:40 +08:00
if (_entryGroup.CurrentState is {Enabled:true})
2024-07-15 17:26:08 +08:00
{
2025-03-24 14:42:40 +08:00
_entryGroup.CurrentState.OnTick(delta);
2024-08-13 18:42:51 +08:00
}
2025-03-24 14:42:40 +08:00
if (_windowEntryGroup.CurrentState is {Enabled:true})
2024-07-15 17:26:08 +08:00
{
2025-03-24 14:42:40 +08:00
_windowEntryGroup.CurrentState.OnTick(delta);
2024-08-13 18:42:51 +08:00
}
2023-08-23 01:59:26 +08:00
}
2024-07-15 17:26:08 +08:00
catch (Exception e)
2024-07-07 14:27:34 +08:00
{
2024-07-15 17:26:08 +08:00
BIT4Log.LogException(e);
2024-07-07 14:27:34 +08:00
}
2023-08-23 01:59:26 +08:00
}
2024-11-03 16:38:17 +08:00
public async void Dispose()
{
2025-02-24 23:02:43 +08:00
foreach (var panelsValue in _panels.Values)
{
if (panelsValue is IDisposable disposable)
{
disposable.Dispose();
}
}
2024-11-03 16:38:17 +08:00
_ticker.Remove(OnTick);
await UniTask.SwitchToMainThread();
2025-03-24 14:42:40 +08:00
_entryGroup.Dispose();
_windowEntryGroup.Dispose();
2024-11-03 16:38:17 +08:00
}
2023-08-23 01:59:26 +08:00
}
}