readme
This commit is contained in:
55
BITKit/Scripts/Camera/CameraService.cs
Normal file
55
BITKit/Scripts/Camera/CameraService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
105
BITKit/Scripts/Camera/FreeLookCamera.cs
Normal file
105
BITKit/Scripts/Camera/FreeLookCamera.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
58
BITKit/Scripts/Camera/VirtualCamera.cs
Normal file
58
BITKit/Scripts/Camera/VirtualCamera.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
80
BITKit/Scripts/Core/BITAppForGodot.cs
Normal file
80
BITKit/Scripts/Core/BITAppForGodot.cs
Normal 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;
|
||||
}
|
||||
}
|
11
BITKit/Scripts/Core/BITAppProxy.cs
Normal file
11
BITKit/Scripts/Core/BITAppProxy.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
namespace BITKit;
|
||||
public partial class BITAppProxy : Node
|
||||
{
|
||||
private void Exit()
|
||||
{
|
||||
GetTree().Quit();
|
||||
}
|
||||
}
|
23
BITKit/Scripts/Core/Exec.cs
Normal file
23
BITKit/Scripts/Core/Exec.cs
Normal 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}");
|
||||
}
|
||||
}
|
||||
}
|
56
BITKit/Scripts/Core/IntervalTimer.cs
Normal file
56
BITKit/Scripts/Core/IntervalTimer.cs
Normal 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;
|
||||
}
|
||||
}
|
210
BITKit/Scripts/Data/DataPlayer.cs
Normal file
210
BITKit/Scripts/Data/DataPlayer.cs
Normal 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);
|
||||
}
|
||||
}
|
78
BITKit/Scripts/ECS/Entity.cs
Normal file
78
BITKit/Scripts/ECS/Entity.cs
Normal 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);
|
||||
}
|
||||
}
|
13
BITKit/Scripts/ECS/EntityComponent.cs
Normal file
13
BITKit/Scripts/ECS/EntityComponent.cs
Normal 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(){}
|
||||
}
|
46
BITKit/Scripts/ECS/GodotEntitiesService.cs
Normal file
46
BITKit/Scripts/ECS/GodotEntitiesService.cs
Normal 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();
|
||||
}
|
||||
}
|
30
BITKit/Scripts/Extensions/Node.cs
Normal file
30
BITKit/Scripts/Extensions/Node.cs
Normal 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();
|
||||
}
|
||||
}
|
14
BITKit/Scripts/Node2D/DateTimeNode.cs
Normal file
14
BITKit/Scripts/Node2D/DateTimeNode.cs
Normal 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);
|
||||
}
|
||||
}
|
2
BITKit/Scripts/Resource/GDStringResource.gd
Normal file
2
BITKit/Scripts/Resource/GDStringResource.gd
Normal file
@@ -0,0 +1,2 @@
|
||||
extends Godot.Resource
|
||||
var Value;
|
10
BITKit/Scripts/Resource/StringResource.cs
Normal file
10
BITKit/Scripts/Resource/StringResource.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Godot;
|
||||
|
||||
namespace BITKit;
|
||||
|
||||
public partial class StringResource : Resource
|
||||
{
|
||||
[Export]
|
||||
private string value { get; set; }
|
||||
public string Value => value;
|
||||
}
|
53
BITKit/Scripts/UX/UXMetaElement.cs
Normal file
53
BITKit/Scripts/UX/UXMetaElement.cs
Normal 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);
|
||||
}
|
||||
}
|
61
BITKit/Scripts/UX/UXMetaService.cs
Normal file
61
BITKit/Scripts/UX/UXMetaService.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
50
BITKit/Scripts/UX/UXPanel.cs
Normal file
50
BITKit/Scripts/UX/UXPanel.cs
Normal 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);
|
||||
}
|
||||
}
|
144
BITKit/Scripts/UX/UXService.cs
Normal file
144
BITKit/Scripts/UX/UXService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user