feat: integrate composable Godot runtime and EventBus debugger

This commit is contained in:
cneicy committed 2026-11-06 19:18:54 +08:00
1 parent 0d9e04c09d
commit 51f8a1d044
55 files changed
+1481 -114

No files matched your search

@@ -0,0 +1,31 @@
# ShrinkSDK.EntJoy.Godot
Godot 4.7 C# 的 EntJoy ECS/JobSystem 生命周期适配包。它复用 EntJoy 的 `World`、查询和 Job API,不创建第二套 ECS;`ShrinkEntJoyHost` 负责在 Godot 节点生命周期内初始化/关闭 JobSystem,并在帧边界交换事件缓冲和提交待处理 Job。
## 安装
```powershell
dotnet add package ShrinkSDK.Godot --version 0.2.1
dotnet add package ShrinkSDK.EntJoy.Godot --version 0.1.1
```
该包依赖 `EntJoy.ECS` 1.0.0。EntJoy 当前 NuGet 原生运行时以 Windows x64 为主要验证目标;没有可用原生 DLL 时由 EntJoy 自己回退到托管 JobSystem。
## 使用
```csharp
using EntJoy.ECS;
using ShrinkSDK.EntJoy.Godot;
public partial class GameRoot : ShrinkEntJoyHost
{
public override void _Ready()
{
base._Ready();
EntityManager entities = EntJoyWorld!.EntityManager;
// 创建实体、构造 QueryBuilder 并调度 EntJoy Job。
}
}
```
`EntJoyWorld` 在 `_Ready` 后可用,在 `_ExitTree` 前释放。多个 `ShrinkEntJoyHost` 会共享进程级 JobSystem,并在最后一个宿主退出时关闭。
@@ -0,0 +1,96 @@
#nullable enable
using System;
using System.Threading;
using EntJoy.ECS;
using EntJoy.JobSystem;
using Godot;
using ShrinkSDK.Godot;
namespace ShrinkSDK.EntJoy.Godot;
/// <summary>Godot node that owns an EntJoy World and advances its frame boundary.</summary>
public partial class ShrinkEntJoyHost : ShrinkGodotHost
{
private static readonly object SchedulerGate = new();
private static int SchedulerUsers;
private bool _schedulerAcquired;
private bool _worldDisposed;
private ulong _lastFrame = ulong.MaxValue;
private SceneTree? _tree;
[Export] public string EntJoyWorldName { get; set; } = "GodotWorld";
public World? EntJoyWorld { get; private set; }
public override void _Ready()
{
base._Ready();
AcquireScheduler();
EntJoyWorld = new World(EntJoyWorldName);
World.DefaultWorld = EntJoyWorld;
_tree = GetTree();
_tree.ProcessFrame += OnProcessFrame;
TreeExited += OnTreeExited;
}
public override void _Process(double delta)
{
base._Process(delta);
Advance();
}
private void OnProcessFrame() => Advance();
private void Advance()
{
if (EntJoyWorld == null || _worldDisposed) return;
var frame = Engine.GetProcessFrames();
if (frame == _lastFrame) return;
_lastFrame = frame;
EntJoyWorld.NextFrameEvents();
NativeJobScheduler.EndFrame();
EntJoyWorld.CurrentFrame++;
}
public override void _ExitTree()
{
DisposeWorld();
base._ExitTree();
}
private void OnTreeExited()
{
if (_tree != null)
_tree.ProcessFrame -= OnProcessFrame;
_tree = null;
TreeExited -= OnTreeExited;
DisposeWorld();
}
private void AcquireScheduler()
{
lock (SchedulerGate)
{
if (SchedulerUsers++ == 0)
NativeJobScheduler.Initialize();
_schedulerAcquired = true;
}
}
private void DisposeWorld()
{
if (_worldDisposed) return;
_worldDisposed = true;
EntJoyWorld?.Dispose();
EntJoyWorld = null;
if (!_schedulerAcquired) return;
lock (SchedulerGate)
{
_schedulerAcquired = false;
if (--SchedulerUsers == 0)
NativeJobScheduler.Shutdown();
}
}
}
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyName>ShrinkSDK.EntJoy.Godot</AssemblyName>
<RootNamespace>ShrinkSDK.EntJoy.Godot</RootNamespace>
<PackageId>ShrinkSDK.EntJoy.Godot</PackageId>
<Version>0.1.1</Version>
<Description>Godot lifecycle integration for EntJoy headless ECS and JobSystem.</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="*.cs" />
<None Include="README.md" Pack="true" PackagePath="\" />
<ProjectReference Include="..\ShrinkSDK.Godot\ShrinkSDK.Godot.csproj" />
<!-- The adapter exposes the runtime API; consumers that use EntJoy source-generated
jobs can add EntJoy.ECS directly and opt into its analyzer explicitly. -->
<PackageReference Include="EntJoy.ECS" Version="1.0.0" ExcludeAssets="analyzers" />
</ItemGroup>
</Project>