This commit is contained in:
CortexCore
2023-06-19 00:41:44 +08:00
parent 073996ce6c
commit bf122c66dc
48 changed files with 683 additions and 84 deletions

View File

@@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.Linq;
using Godot;
namespace BITKit;
/// <summary>
/// 摄像头服务该服务需要加载到Camera3D节点中
/// </summary>
public partial class CameraService:Camera3D
{
public static CameraService Singleton { get; private set; }
/// <summary>
/// 场景中所有的摄像头
/// </summary>
private static readonly List<IVirtualCamera> _cameras=new();
/// <summary>
/// 当前已激活的摄像头
/// </summary>
public static IVirtualCamera ActiveCamera { get; private set; }
/// <summary>
/// 注册摄像头
/// </summary>
/// <param name="camera">摄像头</param>
/// <returns></returns>
public static bool Register(IVirtualCamera camera) => _cameras.TryAdd(camera);
/// <summary>
/// 注销摄像头
/// </summary>
/// <param name="camera">摄像头</param>
/// <returns></returns>
public static bool UnRegister(IVirtualCamera camera) => _cameras.TryRemove(camera);
public override void _Ready()
{
Singleton = this;
}
/// <summary>
/// 处理摄像头的位置
/// </summary>
/// <param name="delta"></param>
public override void _Process(double delta)
{
//获取所有已启用的相机并加载一个虚拟相机的数据
foreach (var x in _cameras.Where(x => x.IsEnabled))
{
//应用相机坐标
Position = x.Position;
//应用相机角度
Rotation = x.Rotation;
//已加载相机位置,退出循环
break;
}
}
}

View File

@@ -0,0 +1,105 @@
using System;
using Godot;
namespace BITKit;
public partial class FreeLookCamera : Node3D,IVirtualCamera
{
[Export]
private int fov;
[Export]
private bool isEnabled;
[Export]
private Curve wheelCurve;
[Export(PropertyHint.Range,"0.32,8")]
private float distance;
[Export] private float maxDistance;
private Vector3 rotation;
private Vector3 position;
void IActivable.SetActive(bool active) => isEnabled = active;
int IVirtualCamera.FOV => fov;
bool IVirtualCamera.IsEnabled => isEnabled;
// ReSharper disable once ConvertToAutoProperty
Vector3 IVirtualCamera.Rotation =>rotation ;
// ReSharper disable once ConvertToAutoProperty
Vector3 IVirtualCamera.Position => position;
private Vector3 euler;
private bool isMoving;
public override void _Ready()
{
euler = Rotation;
position = Position;
CameraService.Register(this);
}
protected override void Dispose(bool disposing)
{
CameraService.UnRegister(this);
}
public override void _Process(double delta)
{
var _rot = Quaternion.FromEuler(euler);
var _dir = _rot * Vector3.Forward * distance;
var newPos = Position - _dir;
position = position.Lerp(newPos, 1);
rotation = rotation.Lerp(euler, 1);
BITAppForGodot.AllowCursor.SetDisableElements(this,Input.IsMouseButtonPressed(MouseButton.Middle));
isMoving = Input.IsKeyPressed(Key.Shift);
SetMeta("CurrentRot",_rot);
}
public override void _Input(InputEvent @event)
{
switch (@event)
{
case InputEventMouseMotion mouseMotion:
if(Input.IsMouseButtonPressed(MouseButton.Middle) is false)break;
if (isMoving)
{
var velocity = mouseMotion.Relative;
Position +=
Quaternion.FromEuler(euler) *
new Vector3()
{
X= -velocity.X,
Y = velocity.Y
} * (float)GetProcessDeltaTime();
}
else
{
var mouseVelocity = mouseMotion.Relative /* 0.022f*/ * 1.81f * (float)GetProcessDeltaTime();
euler.X -= mouseVelocity.Y;
euler.Y -= mouseVelocity.X;
euler.Y %= 360;
euler.X = Math.Clamp(euler.X, -80, 80);
}
break;
case InputEventMouseButton mouseButton:
var delta =(float) GetProcessDeltaTime() * (wheelCurve?.Sample(distance*0.1f) ?? 32);
switch (mouseButton.ButtonIndex)
{
case MouseButton.WheelUp:
distance -= delta;
break;
case MouseButton.WheelDown:
distance += delta;
break;
}
distance = Math.Clamp(distance,0, maxDistance);
break;
}
}
}

View File

@@ -0,0 +1,58 @@
using Godot;
namespace BITKit;
/// <summary>
/// 虚拟相机接口定义
/// </summary>
public interface IVirtualCamera:IActivable
{
/// <summary>
/// 相机的FOV
/// </summary>
int FOV { get; }
/// <summary>
/// 相机是否已启用
/// </summary>
bool IsEnabled { get; }
/// <summary>
/// 相机坐标
/// </summary>
Vector3 Position { get; }
/// <summary>
/// 相机旋转
/// </summary>
Vector3 Rotation { get; }
}
/// <summary>
/// 基于Node3D的包括基础功能的虚拟相机
/// </summary>
public partial class VirtualCamera : Node3D, IVirtualCamera
{
[Export] private int fov;
[Export] private bool isEnabled;
public int FOV => fov;
public bool IsEnabled => isEnabled;
Vector3 IVirtualCamera.Position => GlobalPosition;
Vector3 IVirtualCamera.Rotation => Rotation;
public void SetActive(bool active)
{
isEnabled = active;
}
public override void _Ready()
{
CameraService.Register(this);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
CameraService.UnRegister(this);
}
}
}

View File

@@ -0,0 +1,80 @@
using System.Threading;
using Godot;
using Microsoft.Extensions.DependencyInjection;
using HttpClient = System.Net.Http.HttpClient;
namespace BITKit;
/// <summary>
/// 为Godot提供的BITApp加载服务
/// </summary>
public partial class BITAppForGodot : Node
{
public static readonly ValidHandle AllowCursor = new();
/// <summary>
/// 在构造函数中注册Logger
/// </summary>
public BITAppForGodot()
{
BIT4Log.OnLog += GD.Print;
BIT4Log.OnWarning += GD.PushWarning;
BIT4Log.OnNextLine += () => GD.Print();
BIT4Log.OnException += x=>GD.PrintErr(x.ToString());
BIT4Log.UseLogTime();
//启动BITApp
BITApp.Start(ProjectSettings.GetSetting("application/config/name").AsString());
BIT4Log.Log<BITAppForGodot>("已创建BITApp");
}
public override void _Ready()
{
BIT4Log.Log<BITAppForGodot>("正在创建BITWebApp");
//添加测试用HttpClient
BITApp.ServiceCollection.AddSingleton<HttpClient>();
//构造依赖服务提供接口
BITApp.BuildService();
//设置光标状态
AllowCursor.AddListener(SetCursor);
AllowCursor.AddElement(this);
// AllowCursor.AddElement(this);
// Input.MouseMode = Input.MouseModeEnum.Hidden;
// DisplayServer.MouseSetMode(DisplayServer.MouseMode.Hidden);
}
protected override void Dispose(bool disposing)
{
#pragma warning disable CS4014
//停止BITApp
BITApp.Stop();
#pragma warning restore CS4014
BIT4Log.Log<BITAppForGodot>("已安全退出App");
}
private void Exit()
{
GetTree().Quit();
}
private void WindowSetMaxSize()
{
var max = DisplayServer.WindowGetMaxSize();
//DisplayServer.WindowSetMaxSize(max);
var nextMode = DisplayServer.WindowGetMode() switch
{
DisplayServer.WindowMode.Fullscreen=>DisplayServer.WindowMode.Windowed,
DisplayServer.WindowMode.Windowed=>DisplayServer.WindowMode.Fullscreen,
_ => DisplayServer.WindowMode.Fullscreen,
};
DisplayServer.WindowSetMode(nextMode);
}
private void WindowSetMinSize()
{
DisplayServer.WindowSetMode(DisplayServer.WindowMode.Minimized);
}
private static void SetCursor(bool allow)
{
Input.MouseMode = allow ? Input.MouseModeEnum.Visible : Input.MouseModeEnum.Captured;
}
}

View File

@@ -0,0 +1,11 @@
using Godot;
using System;
namespace BITKit;
public partial class BITAppProxy : Node
{
private void Exit()
{
GetTree().Quit();
}
}

View File

@@ -0,0 +1,23 @@
using Godot;
using System.IO;
using Environment = System.Environment;
namespace BITKit;
public partial class Exec : Node
{
[Export] private string path;
public override void _Ready()
{
var filePath = Path.Combine(Environment.CurrentDirectory, path);
if (PathHelper.TryGetText(filePath,out var json))
{
json = File.ReadAllText(filePath);
DataParser.Set(json);
BIT4Log.Log<Exec>($"已加载:{path}");
}
else
{
BIT4Log.Warnning<Exec>($"未加载:{path}");
}
}
}

View File

@@ -0,0 +1,56 @@
using Godot;
namespace BITKit;
/// <summary>
/// 固定间隔工具,用于控制执行速率
/// </summary>
[System.Serializable]
public class IntervalTimer
{
public IntervalTimer()
{
}
/// <summary>
/// 在构造函数中声明间隔时间
/// </summary>
/// <param name="interval">间隔(秒)</param>
public IntervalTimer(ulong interval)
{
this.interval = interval;
}
/// <summary>
/// 间隔时间
/// </summary>
private readonly ulong interval;
/// <summary>
/// 可以执行的事件
/// </summary>
private ulong allowTime;
/// <summary>
/// 是否可以执行(执行重置间隔)
/// </summary>
public bool Allow
{
get
{
if (allowTime >= Time.GetTicksMsec()) return false;
Release();
return true;
}
}
/// <summary>
/// 是否可以执行(不会执行重置间隔)
/// </summary>
public bool AllowWithoutRelease => allowTime >= Time.GetTicksMsec();
/// <summary>
/// 重置执行间隔
/// </summary>
/// <param name="immediately">是否可以立即执行</param>
public void Release(bool immediately=false)
{
var currentTime = Time.GetTicksMsec();
allowTime = immediately ? currentTime : currentTime + interval*1000;
}
}

View File

@@ -0,0 +1,210 @@
using Godot;
using System;
using System.Timers;
using Cysharp.Threading.Tasks;
namespace BITKit;
/// <summary>
/// 默认的String数据播放器,主要应用于播放json
/// </summary>
public partial class DataPlayer : Node
{
//参数
/// <summary>
/// 当前帧
/// </summary>
[Export]
public int CurrentFrame
{
get => _currentFrame;
set => _currentFrame = Math.Clamp(value, 0, Values?.Length ?? 0);
}
/// <summary>
/// 播放帧率
/// </summary>
[Export] private int frameRate = 60;
[Export] private string timeFormat="mm:ss:fff";
/// <summary>
/// 基于帧率的每帧播放数据
/// </summary>
[Signal]
public delegate void OnProcessPlayEventHandler(string value);
/// <summary>
/// 开始播放回调
/// </summary>
[Signal]
public delegate void OnPlayEventHandler();
/// <summary>
/// 暂停回调
/// </summary>
[Signal]
public delegate void OnPauseEventHandler();
/// <summary>
/// 播放和暂停回调
/// </summary>
[Signal]
public delegate void OnPlayOrPauseEventHandler(bool isPlaying);
/// <summary>
/// 设置总播放时间的回调
/// </summary>
[Signal]
public delegate void OnSetTotalTimeEventHandler(string dateTimeString);
/// <summary>
/// 设置当前播放时间的回调
/// </summary>
[Signal]
public delegate void OnSetPlaybackTimeEventHandler(string dateTimeString);
/// <summary>
/// 设置标准化播放时间的回调
/// </summary>
[Signal]
public delegate void OnSetNormalizeTimeEventHandler(double normalizeTime);
/// <summary>
/// 是否正在播放
/// </summary>
public bool IsPlaying { get; protected set; }
/// <summary>
/// 是否已暂停播放
/// </summary>
public bool IsPaused { get; protected set; }
/// <summary>
/// 内部计时器
/// </summary>
private readonly System.Timers.Timer timer = new();
/// <summary>
/// 内部数组
/// </summary>
private string[] Values;
//内部当前帧
private int _currentFrame;
public override void _Ready()
{
timer.Elapsed += Play;
}
/// <summary>
/// 播放
/// </summary>
/// <param name="values">播放数据的数组</param>
public bool Play(string[] values)
{
if (values?.Length is 0) return false;
if (values?.Length != Values?.Length)
{
CurrentFrame = 0;
}
Values = values;
timer.Interval = TimeSpan.FromSeconds(1f / frameRate).Milliseconds;
Play();
timer.Start();
var totalTime = new DateTime().AddSeconds((float)Values!.Length / frameRate);
EmitSignal(nameof(OnSetTotalTime), totalTime.ToString(timeFormat));
return true;
}
/// <summary>
/// 播放(在暂停时)
/// </summary>
/// <returns></returns>
public bool Play()
{
IsPaused = false;
IsPlaying = true;
return true;
}
/// <summary>
/// 停止播放
/// </summary>
/// <returns></returns>
public bool Stop()
{
if (!IsPlaying) return false;
timer.Stop();
IsPlaying = IsPaused = false;
return true;
}
/// <summary>
/// 暂停播放
/// </summary>
/// <returns></returns>
public bool Pause()
{
if (!IsPlaying) return false;
IsPaused = true;
return true;
}
/// <summary>
/// 继续播放或暂停播放
/// </summary>
/// <returns></returns>
public bool PlayOrPause()
{
if (!IsPlaying) return false;
IsPaused = !IsPaused;
return true;
}
//设置标准化播放时间(0-1进度)
public void SetNormalizeTime(float normalizeTime)
{
CurrentFrame = (int)(Values.Length * normalizeTime);
}
protected override void Dispose(bool disposing)
{
timer.Stop();
timer.Dispose();
}
/// <summary>
/// 内部调用方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual async void Play(object sender, ElapsedEventArgs e)
{
//等待返回主线程
await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext);
//if (IsPaused) return;
//如果超过了播放长度,停止播放
if (CurrentFrame >= Values?.Length)
{
Stop();
return;
}
//获取当前播放信息
var current = Values?[CurrentFrame++];
var currentNormalizeTime = (float)CurrentFrame / Values!.Length;
var currentTime = new DateTime().AddSeconds((float)CurrentFrame / frameRate);
//调用信号
EmitSignal(nameof(OnProcessPlay), current);
EmitSignal(nameof(OnSetPlaybackTime), currentTime.ToString(timeFormat));
EmitSignal(nameof(OnSetNormalizeTime), currentNormalizeTime);
//设置Meta
SetMeta(nameof(OnProcessPlay),current);
SetMeta(nameof(OnSetNormalizeTime),currentNormalizeTime);
}
}

View File

@@ -0,0 +1,78 @@
using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using BITKit.Core.Entites;
namespace BITKit;
/// <summary>
/// 用于Godot的ECS.Entity实现
/// </summary>
public partial class Entity : Node,IEntity
{
/// <summary>
/// 类型组件的缓存
/// </summary>
private readonly Dictionary<Type,IEntityComponent> TypeComponents=new ();
/// <summary>
/// IEntityService的缓存
/// </summary>
private IEntitiesService _entitiesService;
/// <summary>
/// 所有EntityComponent
/// </summary>
private IEntityComponent[] _components;
IEntityComponent[] IEntity.Components => _components;
/// <summary>
/// IEntity.Id实现
/// </summary>
public ulong Id { get; private set; }
/// <summary>
/// 加载所有EntityComponent的内部实现
/// </summary>
public override void _Ready()
{
List<IEntityComponent> entityComponents = new();
Id = GetInstanceId();
_entitiesService = DI.Get<IEntitiesService>();
foreach (var x in MathNode.GetAllNode(this))
{
GetInstanceId();
if (x is not IEntityComponent component) continue;
component.Entity = this;
TypeComponents.TryAdd(x.GetType(),component);
//BIT4Log.Log<Entity>($"已加载组件:{x.Name}");
component.OnAwake();
entityComponents.Add(component);
}
foreach (var component in TypeComponents.Values)
{
component.OnStart();
}
_entitiesService.Register(this);
this._components = entityComponents.ToArray();
SetMeta("Components",Variant.From(_components.Select(x=>x.GetType().Name).ToArray()));
}
public bool TryGetComponent<T>(out T component) where T : IEntityComponent
{
if (TypeComponents.TryGetValue(typeof(T), out var iComponent) && iComponent is T _component)
{
component = _component;
return true;
}
component = default;
return false;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_entitiesService.UnRegister(this);
}
}
public bool RegisterComponent<T>(T component) where T : IEntityComponent
{
return TypeComponents.TryAdd(typeof(T), component);
}
}

View File

@@ -0,0 +1,13 @@
using BITKit.Core.Entites;
using Godot;
namespace BITKit;
/// <summary>
/// 基于Godot.Node3D的IEntityComponent实现
/// </summary>
public partial class EntityComponent : Node3D,IEntityComponent
{
public IEntity Entity { get; set; }
public virtual void OnStart(){}
public virtual void OnAwake(){}
}

View File

@@ -0,0 +1,46 @@
using Godot;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using BITKit.Core.Entites;
// ReSharper disable All
namespace BITKit;
/// <summary>
/// 基于Godot.Node的IEntitiesService实现
/// </summary>
public partial class GodotEntitiesService : Node,IEntitiesService
{
public GodotEntitiesService()
{
DI.Register<IEntitiesService>(this);
}
private readonly Dictionary<ulong,IEntity> _entities=new ();
private CancellationTokenSource _cancellationTokenSource;
public IEntity[] Entities => _entities.Values.ToArray();
public bool Register(IEntity entity)
{
return _entities.TryAdd(entity.Id, entity);
}
public bool UnRegister(IEntity entity)
{
return _entities.TryRemove(entity.Id);
}
public override void _Ready()
{
_cancellationTokenSource = new();
}
protected override void Dispose(bool disposing)
{
if(disposing)_cancellationTokenSource.Cancel();
}
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
public IEntity[] Query<T>() where T : IEntityComponent
{
return _entities.Values.Where(x => x.TryGetComponent<T>(out _)).ToArray();
}
}

View File

@@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Linq;
using Godot;
namespace BITKit;
/// <summary>
/// 为Godot.Node提供数学工具
/// </summary>
public static partial class MathNode
{
/// <summary>
/// 获取Node下所有的子Node节点
/// </summary>
/// <param name="self">Root Node</param>
/// <returns></returns>
public static IEnumerable<Node> GetAllNode(Node self)
{
List<Node> nodes = new() { self };
For(self);
void For(Node node)
{
foreach (var x in node.GetChildren())
{
For(x);
nodes.Add(x);
}
}
return nodes.Distinct();
}
}

View File

@@ -0,0 +1,14 @@
using Godot;
using System;
using System.Globalization;
namespace BITKit;
public partial class DateTimeNode : Label
{
[Export]
private string timeFormat;
public override void _Process(double delta)
{
Text = string.IsNullOrEmpty(timeFormat) ? DateTime.Now.ToString(CultureInfo.InvariantCulture) : DateTime.Now.ToString(timeFormat);
}
}

View File

@@ -0,0 +1,2 @@
extends Godot.Resource
var Value;

View File

@@ -0,0 +1,10 @@
using Godot;
namespace BITKit;
public partial class StringResource : Resource
{
[Export]
private string value { get; set; }
public string Value => value;
}

View File

@@ -0,0 +1,53 @@
using System.Linq;
using Godot;
namespace BITKit;
public partial class UXMetaElement : Node3D, IMetaDisplayElement
{
[Export] private bool isEnabled=true;
[Export] private Node3D proxy;
[Export] protected string[] MetaEntries;
private bool _registered;
string IMetaDisplayElement.Text
{
get
{
var node = proxy ?? this;
return string.Join(MetaEntries?.Length>2 ? "\n" : ":", MetaEntries!
.Where(x => string.IsNullOrEmpty(x) is false)
.Where(x => node.HasMeta(x))
.Select(x => node.GetMeta(x).AsString())
.InsertOf<string>(0, node.Name)
);
}
}
Vector3 IMetaDisplayElement.Position => (proxy ?? this).GlobalPosition;
// public override void _Ready()
// {
// if (!isEnabled) return;
// UXMetaService.Register(this);
// _registered = true;
// }
public override void _Process(double delta)
{
if (_registered == isEnabled) return;
if (isEnabled)
{
UXMetaService.Register(this);
}
else
{
UXMetaService.UnRegister(this);
}
_registered = isEnabled;
}
protected override void Dispose(bool disposing)
{
if (isEnabled && _registered)
UXMetaService.UnRegister(this);
}
}

View File

@@ -0,0 +1,61 @@
using Godot;
using System.Collections.Generic;
namespace BITKit;
public interface IMetaDisplayElement
{
string Text { get; }
Vector3 Position { get; }
}
public partial class UXMetaService : Control
{
#region
private static readonly Queue<IMetaDisplayElement> AddQueue = new();
private static readonly Queue<IMetaDisplayElement> RemoveQueue = new();
private static readonly List<IMetaDisplayElement> Elements = new();
public static void Register(IMetaDisplayElement element) => AddQueue.Enqueue(element);
public static void UnRegister(IMetaDisplayElement element) => RemoveQueue.Enqueue(element);
#endregion
#region
private readonly Dictionary<IMetaDisplayElement, Label> _dictionary = new();
#endregion
/// <summary>
/// 标签预制体
/// </summary>
[Export]
private PackedScene labelTemplate;
/// <summary>
/// 主要处理过程
/// </summary>
/// <param name="delta"></param>
public override void _Process(double delta)
{
//相机服务未初始化时返回
if(CameraService.Singleton is null)return;
//处理添加队列
while (AddQueue.TryDequeue(out var newElement))
{
if (!Elements.TryAdd(newElement)) continue;
var instance = labelTemplate.Instantiate<Label>();
_dictionary.Add(newElement,instance);
AddChild(instance);
}
//处理每个Element的数据
foreach (var element in Elements)
{
if (_dictionary.TryGetValue(element, out var label) is false) continue;
var pos = CameraService.Singleton.UnprojectPosition(element.Position);
label.Position = pos;
label.Text = element.Text;
}
//处理移除队列
while (RemoveQueue.TryDequeue(out var removeElement))
{
if (!_dictionary.TryGetValue(removeElement, out var label)) continue;
if (!_dictionary.TryRemove(removeElement)) continue;
Elements.Remove(removeElement);
label.QueueFree();
}
}
}

View File

@@ -0,0 +1,50 @@
using Godot;
using System;
namespace BITKit;
public partial class UXPanel : Control, IUXPanel
{
[Export] private bool isAnimate;
[Export] private bool allowCursor;
[Export] private bool allowInput;
[Export] private bool isStartPanel;
public bool IsAnimate => isAnimate;
public string Index => GetType().FullName == typeof(UXPanel).FullName ? Name : GetType().FullName;
public bool AllowCursor => allowCursor;
public bool AllowInput => allowInput;
public virtual void OnEntry(){}
public virtual void Entry()
{
Show();
OnEntry();
}
public virtual void Exit()
{
Hide();
OnExit();
}
public virtual void OnExit(){}
public override void _Ready()
{
UXService.Register(this);
if (isStartPanel)
{
UXService.Open(this as IUXPanel);
}
}
private void Open()
{
UXService.Open(this as IUXPanel);
}
protected override void Dispose(bool disposing)
{
UXService.UnRegister(this);
}
}

View File

@@ -0,0 +1,144 @@
using Godot;
using System.Collections.Generic;
using System.Xml;
using Microsoft.Extensions.DependencyInjection;
namespace BITKit;
/// <summary>
/// 基本UX面板接口,定义了基本的UX面板功能
/// </summary>
/// <para>⭐同步打开与关闭</para>
/// <para>⭐异步打开与关闭</para>
/// <para>⭐当前可见状态</para>
/// <para>⭐基本UI导航回调</para>
public interface IUXPanel
{
/// <summary>
/// 该面板是否具有动画
/// </summary>
bool IsAnimate { get; }
/// <summary>
/// 该面板的索引(入口,Key)
/// </summary>
string Index { get; }
/// <summary>
/// 该面板是否启用指针
/// </summary>
bool AllowCursor { get; }
/// <summary>
/// 该面板是否启用玩家输入
/// </summary>
bool AllowInput { get; }
void Entry();
void Exit();
}
/// <summary>
/// 基本UX服务(GUI管理器),主要通过加载叠加面板实现
/// </summary>
/// <para>使用方式:</para>
///
///
public partial class UXService : Control
{
private static UXService Singleton;
/// <summary>
/// 在构造函数中注入依赖
/// </summary>
public UXService()
{
BITApp.ServiceCollection.AddSingleton(this);
Singleton = this;
}
/// <summary>
/// 注册面板,加入注册队列
/// </summary>
/// <param name="panel">UX面板</param>
public static void Register(IUXPanel panel)
{
RegistryQueue.Enqueue(panel);
}
/// <summary>
/// 注销面板
/// </summary>
/// <param name="panel">UX面板</param>
public static void UnRegister(IUXPanel panel)
{
}
public static void Open<T>() where T : IUXPanel
{
}
public static void Open(IUXPanel panel) => EnableQueue.Push(panel);
public static void Open(Control control)
{
}
public static void Open(string name)
{
}
/// <summary>
/// 内部注册面板队列
/// </summary>
private static readonly Queue<IUXPanel> RegistryQueue = new();
/// <summary>
/// 已注册面板字典
/// </summary>
private static readonly Dictionary<string, IUXPanel> Panels = new();
/// <summary>
/// 等待启用的面板队列
/// </summary>
private static readonly Stack<IUXPanel> EnableQueue = new();
/// <summary>
/// 已启用面板
/// </summary>
private static readonly Stack<IUXPanel> EnabledPanels = new();
/// <summary>
/// 等待隐藏的面板
/// </summary>
private static readonly Stack<IUXPanel> WActivatedPanels = new();
/// <summary>
/// 正在播放过渡动画的面板
/// </summary>
private static readonly Stack<IUXPanel> TransitionPanles = new();
private void _Entry(IUXPanel panel)
{
}
public override void _Process(double delta)
{
if (TransitionPanles.Count is not 0) return;
while (RegistryQueue.TryDequeue(out var result))
{
Panels.Add(result.Index, result);
result.Exit();
}
if (EnableQueue.TryPop(out var next))
{
while (EnabledPanels.TryPop(out var enabledPanel))
{
enabledPanel.Exit();
}
next.Entry();
EnabledPanels.Push(next);
return;
}
}
}