mirror of
https://github.com/motajs/template.git
synced 2026-10-20 13:50:18 +08:00
- replay-commands 以「重放要先记全」引导核心原则,保留主动/被动与装饰器,不给示例 - data-plugins 以「一组功能打包按时初始化」引导,保留时机次序与占位示例(N-4)
3.6 KiB
3.6 KiB
录像指令
录像验证要能重放出和原来一模一样的结果,前提是:把该记下来的操作都记下来。录像系统(state.replaySystem)做的正是这件事——把玩家在游戏中的操作记录成一条条录像步,之后可以重新播放。它的主要用途是录像验证:只运行数据端、重放录像,就能确认同样的操作是否得到同样的结果(原理见数据端与渲染端)。
核心原则
:::info 核心原则 玩家直接产生、会影响最终成绩的行为,必须计入录像。 :::
这句话包含两个条件:
- 玩家直接产生:是玩家亲手做出的操作,例如移动、使用道具、装备道具。由代码或时间自动发生的变化不算;
- 会影响最终成绩:这个操作会改变游戏结果。只改渲染表现、不进数据端的操作不算。
满足两个条件的操作,都必须作为一条录像步记录下来。否则重放这条录像时,玩家的操作没有被重新执行,结果就会与原始游玩对不上。
主动录像与被动录像
录像步分两类,由 ReplayCommandType 表示:
- 主动录像(
Active):指令自己就会执行对应的操作。引擎内置的移动、瞬移、使用道具(见勇士道具)、装备(见勇士装备)都是主动录像; - 被动录像(
Passive):指令不自己执行,而是让外部通过录像沙盒的getPassive()取到这一步的参数,再由外部决定怎么处理。
新增自定义录像指令
录像指令属于引擎内容,需要改动 packages-user 下的包,而不是只在 src 里写几行代码。新增一条自定义录像指令大致分三步:
- 在录像指令码枚举
ReplayCode中分配一个新码。该枚举属于录像格式的一部分,遵循「只增不改」——新语义必须用新码,不能复用旧码; - 实现
IReplayCommand:type指明是主动还是被动录像,execute(step)执行这一步对应的操作并返回ReplayCommandResult;若需要「连续步骤的后处理」,再实现可选的finalize(); - 用
state.replaySystem.registerCommand(code, command)把指令注册进录像系统。引擎内置指令的注册代码在src/data/replay/initialize.ts中。
execute 的返回值 ReplayCommandResult 有三种:Success(成功)、Failed(失败)、Ignored(失败但可忽略,继续执行后续录像,并会在控制台输出信息)。
录像安全检测:@shouldReplay 与 @ignoreReplay
录像系统会检查「一步录像执行后,游戏是否真的记录下了新的录像步」。为了让这个检查覆盖到正确的方法,可以用两个方法装饰器给方法打标:
@shouldReplay(message):把方法标记为应该计入录像。它会为这次检查登记一段区间,并把message作为信息记录;一旦检查发现问题,就用这些信息告诉你哪些调用路径没有被计入录像;@ignoreReplay(message):把方法标记为忽略本次录像检查,一般用于异步内容等不会立刻修改状态的方法。
检测区间的开始与结束由录像系统在每步录像执行前后统一控制:执行前开始收集,执行后结束并核对区间内是否产生了新的录像步。若没有,就在控制台输出告警,并列出登记过却未被计入的 @shouldReplay 路径。
:::warning
不要用 @ignoreReplay 来掩盖控制台的录像告警。它只应用于「确实不该计入录像」的方法,滥用很可能导致录像出错。
:::