生命周期与事件¶
回调签名¶
void OnLoad(const BML::ModContext &in ctx)
void OnUnload(const BML::ModContext &in ctx)
void OnProcess(const BML::ModContext &in ctx)
void OnGameEvent(const BML::ModContext &in ctx, BML::GameEvent event)
void OnRender(const BML::ModContext &in ctx, const BML::RenderEvent &in event)
void OnCommandEvent(const BML::ModContext &in ctx, const BML::CommandEvent &in event)
void OnModifyConfig(const BML::ModContext &in ctx, const BML::ConfigEvent &in event)
void OnLoadObject(const BML::ModContext &in ctx, const BML::LoadObjectEvent &in event)
void OnLoadScript(const BML::ModContext &in ctx, const BML::LoadScriptEvent &in event)
void OnPhysicalize(const BML::ModContext &in ctx, const BML::PhysicalizeEvent &in event)
void OnUnphysicalize(const BML::ModContext &in ctx, const BML::ObjectEvent &in event)
void OnCheatEnabled(const BML::ModContext &in ctx, const BML::CheatEvent &in event)
签名必须完全匹配,否则 BML 不会调用。
热重载状态迁移使用三个可选方法:
void SaveState(BML::StateBag@ state)
void MigrateState(const string &in fromVersion, BML::StateBag@ state)
void RestoreState(BML::StateBag@ state)
这些方法不是普通回调。只有热重载需要迁移脚本字段时才实现它们。
回调时机¶
| Callback | 触发时机 | 典型用途 |
|---|---|---|
OnLoad |
Mod 加载完成 | 初始化配置、注册命令、启动计时器 |
OnUnload |
Mod 即将卸载 | 清理资源 |
OnProcess |
每帧调用 | ImGui 绘制、输入处理、状态刷新 |
OnGameEvent |
游戏事件发生 | 关卡开始时查找对象、退出时清理 |
OnRender |
渲染阶段 | 观察渲染状态 |
OnCommandEvent |
命令执行 | 观察命令执行流程 |
OnModifyConfig |
配置变更 | 同步成员变量 |
OnLoadObject |
对象加载 | 观察对象进入场景 |
OnLoadScript |
行为脚本加载 | 观察脚本加载 |
OnPhysicalize |
对象物理化 | 记录物理参数 |
OnUnphysicalize |
对象取消物理化 | 清理物理相关状态 |
OnCheatEnabled |
作弊开关切换 | 切换调试功能 |
GameEvent 枚举¶
| 枚举值 | 说明 |
|---|---|
GAME_EVENT_PRE_START_MENU |
进入主菜单前 |
GAME_EVENT_POST_START_MENU |
进入主菜单后 |
GAME_EVENT_EXIT_GAME |
游戏退出 |
GAME_EVENT_PRE_LOAD_LEVEL |
关卡加载前 |
GAME_EVENT_POST_LOAD_LEVEL |
关卡加载后 |
GAME_EVENT_START_LEVEL |
关卡开始 |
GAME_EVENT_PRE_RESET_LEVEL |
重置关卡前 |
GAME_EVENT_POST_RESET_LEVEL |
重置关卡后 |
GAME_EVENT_PAUSE_LEVEL |
关卡暂停 |
GAME_EVENT_UNPAUSE_LEVEL |
关卡恢复 |
GAME_EVENT_PRE_EXIT_LEVEL |
退出关卡前 |
GAME_EVENT_POST_EXIT_LEVEL |
退出关卡后 |
GAME_EVENT_PRE_NEXT_LEVEL |
进入下一关前 |
GAME_EVENT_POST_NEXT_LEVEL |
进入下一关后 |
GAME_EVENT_DEAD |
玩家死亡 |
GAME_EVENT_PRE_END_LEVEL |
关卡结束前 |
GAME_EVENT_POST_END_LEVEL |
关卡结束后 |
GAME_EVENT_COUNTER_ACTIVE |
倒计时/计数器激活 |
GAME_EVENT_COUNTER_INACTIVE |
倒计时/计数器关闭 |
GAME_EVENT_BALL_NAV_ACTIVE |
球导航激活 |
GAME_EVENT_BALL_NAV_INACTIVE |
球导航关闭 |
GAME_EVENT_CAM_NAV_ACTIVE |
摄像机导航激活 |
GAME_EVENT_CAM_NAV_INACTIVE |
摄像机导航关闭 |
GAME_EVENT_BALL_OFF |
球离开有效区域 |
GAME_EVENT_PRE_CHECKPOINT_REACHED |
到达存档点前 |
GAME_EVENT_POST_CHECKPOINT_REACHED |
到达存档点后 |
GAME_EVENT_LEVEL_FINISH |
关卡完成 |
GAME_EVENT_GAME_OVER |
游戏结束 |
GAME_EVENT_EXTRA_POINT |
获得额外分数 |
GAME_EVENT_PRE_SUB_LIFE |
扣除生命前 |
GAME_EVENT_POST_SUB_LIFE |
扣除生命后 |
GAME_EVENT_PRE_LIFE_UP |
增加生命前 |
GAME_EVENT_POST_LIFE_UP |
增加生命后 |
工具函数:string BML::GetGameEventName(BML::GameEvent event),返回枚举的可读名称。
执行顺序¶
关卡加载序列:
热重载¶
热重载只替换已经被 BML 发现的脚本 mod runtime,不会在运行中新增 mod,也不会重排依赖图。
常用命令:
script reload # 重载全部脚本 mod
script reload <id> # 重载一个脚本 mod
script reload <id> --dry-run
script reload <id> --dry-run --check-state
script diag <id>
script logs error
script panel
关键规则:
- 编译失败或 metadata 失败时,如果旧 runtime 存在,旧 runtime 会继续运行。
- 启动时首次编译失败时,修正文件后重启 Player,确保重新执行完整发现和依赖排序。
- 运行中放入新的
*.mod.as不会新增 mod。新增 mod 需要重启 Player。 - 修改 Mod ID 或依赖声明后需要重启 Player。
- Timer、Command、DataShare 和回调句柄在实例替换后失效,新实例必须在
OnLoad中重新注册所需资源。 - BML 只能恢复自己持有的脚本资源,不能自动撤销脚本改过的游戏世界状态。
状态迁移示例:
int counter = 0;
string mode = "idle";
void SaveState(BML::StateBag@ state) {
state.SetInt("counter", counter);
state.SetString("mode", mode);
}
void MigrateState(const string &in fromVersion, BML::StateBag@ state) {
if (fromVersion == "1.0.0" && state.Has("oldMode")) {
state.SetString("mode", state.GetString("oldMode", "idle"));
state.Remove("oldMode");
}
}
void RestoreState(BML::StateBag@ state) {
counter = state.GetInt("counter", counter);
mode = state.GetString("mode", mode);
}
StateBag 只保存 bool、int、float、string。不要把 CK 句柄、脚本对象、Timer、Command、DataShare 或 ModRef 放进去。状态迁移方法应只复制/转换纯数据,不注册资源、不执行命令、不修改游戏世界。资源在 RestoreState 后的 OnLoad 里按当前状态重新创建。
规则¶
- 不要在
GAME_EVENT_START_LEVEL之前查询关卡对象 - 在
PRE_EXIT_LEVEL或PRE_LOAD_LEVEL时清除关卡状态 - 禁止在
OnProcess中每帧打日志 - 成员变量在帧之间持久存在
- 回调签名必须精确匹配,否则 BML 不会调用
- 如果需要跨热重载保留成员变量,实现
SaveState/RestoreState,版本升级时再实现MigrateState