◆ 持续开发中 · 当前为非稳定版本,如需抢先体验请通过 GitHub Issues 联系作者

引导与世界 Bootstrap

在启动时组装一个 Framework World:装基础系统(输入 / 相机 / UI / 本地化 / 设置)、加载配置表、选择模式(Live / Sim),并提供统一更新入口。

一句话:三选一地创建 World —— FrameworkWorld / FrameworkModeBootstrap / 游戏自己的 ICustomBootstrap。

跨端数据加载契约(先读这段)

✕
打包发布时必须用异步引导入口。WebGL 没有文件系统、Android 的 StreamingAssets 在 APK 压缩包内(jar:file://),这两者都不能用 System.IO.File 直接读,必须先把数据 HTTP 进内存缓存。而读取发生在建 World 时的 System.OnCreate,所以预取必须先于建 World 完成。

三种引导方式各自都有 Initialize / InitializeAsync。但无论哪种,全平台统一用 InitializeAsync 即可:

public sealed class GameBootstrap : MonoBehaviour
{
    private async void Start()
    {
        // WebGL/Android:HTTP 预取进内存缓存;PC/iOS:no-op
        await new FrameworkBootstrap().InitializeAsync("Game World");
        // 之后照常:装 UI、进主菜单……
    }
}

InitializeAsync 内部顺序:await StreamingAssets.PreloadAsync() → 建 World + 装系统(各 System.OnCreate 在这里读 Blob)→ FrameworkLifecycle.EnsureBootDataLoaded() 补加载入。宿主代码因此零平台分支。

更新阶梯(UpdateGroups)

固定顺序的 ComponentSystemGroup,全部挂在 SimulationSystemGroup 下的 UpdateGroup_Root 下:

SimulationSystemGroup
└── UpdateGroup_Root          [After BeginSimulationEntityCommandBufferSystem]
    ├── UpdateGroup_A0         引导期:装单例、起管线、读启动配置
    ├── UpdateGroup_A1         数据期:配置加载、账号/状态、统计
    ├── UpdateGroup_A2         房间/场景期:房间进入、场景烘焙、环境
    ├── UpdateGroup_A3         玩法期:模拟、技能、AI、战斗(主体)
    └── UpdateGroup_B9         表现期:UI 刷新、HUD、本地化刷新(最后跑)
⚠
UpdateGroup_B9 不能写 [UpdateBefore(typeof(LateSimulationSystemGroup))]——跨级排序会让 Entities 的 system sorter 抛 NRE。顺序关键:A0 装单例 → A1 加载配置 → A3 跑玩法 → B9 刷 UI,在 A3 之前查配置会查不到。

三种引导方式

1. FrameworkWorld(installer 组合)

var host = new FrameworkWorld("Framework World");
World.DefaultGameObjectInjectionWorld = host.World;
host.Install(installers, BootMode);   // installers: IEnumerable<IFrameworkInstaller>
host.Update();                         // 每帧调
host.Dispose();

手动驱动:host.Update() 调 _rootGroup.Update()(没有就 _world.Update()),宿主要在 MonoBehaviour 里每帧调。UIManager 等 [DisableAutoCreation] 系统需手动 GetOrCreateSystemManaged 加进更新列表。

2. FrameworkModeBootstrap(按模式扫系统)

var boot = new FrameworkModeBootstrap();
await boot.InitializeAsync("Framework World");   // 内部 new World + AppendWorldToCurrentPlayerLoop
◈
它会把 World 加进 player loop,所以不要再手动每帧 Update(),否则一帧跑两次。FrameworkWorld 才需要手动 Update()。

3. 游戏自己的 ICustomBootstrap

框架的两个 bootstrap 都刻意不实现 ICustomBootstrap,避免被 Entities 自动扫描发现、与游戏自己的 bootstrap 冲突。游戏要自动引导就:在自己 ICustomBootstrap.Initialize 里 new FrameworkWorld(...),或把真正的引导放到 async void Start() 的 MonoBehaviour 里 await ...InitializeAsync(...)。

⚠
ICustomBootstrap.Initialize 是同步签名,无法 await。Unity 在 WebGL 上也无法等待预取——这是它本身的限制,不是框架能绕过的。请把框架装配放在 async void Start() 里。

模式(Live / Sim)

static class SimulationBootstrap
{
    public const int Live = 1;
    public const int Sim  = 2;
    public static int Mode { get; set; } = 0;
    public static bool IsSimulated => Mode == Sim;
}

系统用 [FrameworkModeOnly(模式)](仅该模式)或 [FrameworkMode(模式)](该模式才装)过滤;两者都没标记的系统在所有模式下都装。运行时用 RequireForUpdate<FrameworkMode_Live>() 按模式开关。

UI 适配与导航参数

UIManager.Setting(static)是 UI 适配 / 导航参数的唯一真源,引导期定制:

protected override void Configure(World world)
{
    // UI 不是按 1920×1080 设计时才需要:
    UIManager.Setting.ReferenceResolution = new Vector2(2560, 1080);
}

命令行 / 启动参数

GameCommandArgs 解析 Environment.GetCommandLineArgs():--account / --token / --appid / --serverip / --serverport / --lang / --width / --height / --fullscreen。

仅场景运行(调试用)

主工具栏中间区的 ▶ Scene 按钮可只跑当前场景、跳过框架引导,方便单独调试某个 Demo。机制:点按钮置位 BootstrapGate.ShouldSkip → 框架三个 Initialize 入口顶部查闸门为真即 return false → Entities 自动建默认世界。宿主只需把框架 Initialize 的返回值透传给 Entities 即可。

常见坑

  • UIManager / LocalizationBlobSystem 等都是 [DisableAutoCreation],只有引导期手动创建 / 扫描才进 World。
  • 顺序关键:A0 装单例 → A1 加载配置 → A3 跑玩法 → B9 刷 UI;A3 之前查配置查不到。
  • FrameworkModeBootstrap.Initialize 已加进 player loop,勿再手动 Update()。
  • WebGL / Android 上不要用 System.IO 读 StreamingAssets,引导用 InitializeAsync。