mirror of
https://github.com/motajs/template.git
synced 2026-11-03 07:24:30 +08:00
- U-01 src-tour:content 层主要在编辑器内改、对应初级教程 - U-02 data-state-overview:解释 state 静态类型按 L0-L3 逐层累加 - U-04 map-structure:按 alias/zIndex 取指定图层(非 addLayer) - U-05 map-tiles:putMapData/getMapData 完整示例 + 高级教程预留 - U-06 flags:区分字段对象与字段值,两组接口如何选择 - U-20 events:以内置事件集已完整(发布前约 100 个)视角改写 - U-21 replay-commands:录像指令细节留高级教程 - U-22 11 篇:高级教程显式说明 + 非链接预留标记
53 lines
4.3 KiB
Markdown
53 lines
4.3 KiB
Markdown
# 录像指令
|
||
|
||
录像验证要能重放出和原来一模一样的结果,前提是:把该记下来的操作都记下来。录像系统(`state.replaySystem`)做的正是这件事——把玩家在游戏中的操作记录成一条条**录像步**,之后可以重新播放。它的主要用途是**录像验证**:只运行数据端、重放录像,就能确认同样的操作是否得到同样的结果(原理见[数据端与渲染端](./data-vs-render.md))。
|
||
|
||
## 核心原则
|
||
|
||
:::info 核心原则
|
||
**玩家直接产生、会影响最终成绩的行为,必须计入录像。**
|
||
:::
|
||
|
||
这句话包含两个条件:
|
||
|
||
- **玩家直接产生**:是玩家亲手做出的操作,例如移动、使用道具、装备道具。由代码或时间自动发生的变化不算;
|
||
- **会影响最终成绩**:这个操作会改变游戏结果。只改渲染表现、不进数据端的操作不算。
|
||
|
||
满足两个条件的操作,都必须作为一条录像步记录下来。否则重放这条录像时,玩家的操作没有被重新执行,结果就会与原始游玩对不上。
|
||
|
||
## 主动录像与被动录像
|
||
|
||
录像步分两类,由 `ReplayCommandType` 表示:
|
||
|
||
- **主动录像(`Active`)**:指令自己就会执行对应的操作。引擎内置的移动、瞬移、使用道具(见[勇士道具](./hero-items.md))、装备(见[勇士装备](./hero-equipment.md))都是主动录像;
|
||
- **被动录像(`Passive`)**:指令不自己执行,而是让外部通过录像沙盒的 `getPassive()` 取到这一步的参数,再由外部决定怎么处理。
|
||
|
||
## 新增自定义录像指令
|
||
|
||
录像指令属于**引擎内容**,需要改动 `packages-user` 下的包(这个目录是什么、和 `src` 是什么关系,见 [src 导览](./src-tour.md) 的「包从哪来」),而不是只在 `src` 里写几行代码。新增一条自定义录像指令大致分三步:
|
||
|
||
1. 在录像指令码枚举 `ReplayCode` 中分配一个新码。该枚举属于录像格式的一部分,遵循「只增不改」——新语义必须用新码,不能复用旧码;
|
||
2. 实现 `IReplayCommand`:`type` 指明是主动还是被动录像,`execute(step)` 执行这一步对应的操作并返回 `ReplayCommandResult`;若需要「连续步骤的后处理」,再实现可选的 `finalize()`;
|
||
3. 用 `state.replaySystem.registerCommand(code, command)` 把指令注册进录像系统。引擎内置指令的注册代码在 `src/data/replay/initialize.ts` 中。
|
||
|
||
`execute` 的返回值 `ReplayCommandResult` 有三种:`Success`(成功)、`Failed`(失败)、`Ignored`(失败但可忽略,继续执行后续录像,并会在控制台输出信息)。
|
||
|
||
## 录像安全检测:@shouldReplay 与 @ignoreReplay
|
||
|
||
`@shouldReplay(message)` 与 `@ignoreReplay(message)` 写在一个方法的上方,形如 `@名字(...)`。这种以 `@` 开头的写法叫**装饰器(decorator)**:它不改变方法本身的代码,只给这个方法附加一条额外说明(在这里,就是给录像系统打一个标记)。装饰器的基础写法见[代码基础](./code-basics.md)。
|
||
|
||
录像系统会检查「一步录像执行后,游戏是否真的记录下了新的录像步」。为了让这个检查覆盖到正确的方法,可以用两个方法装饰器给方法打标:
|
||
|
||
- `@shouldReplay(message)`:把方法标记为**应该计入录像**。它会为这次检查登记一段区间,并把 `message` 作为信息记录;一旦检查发现问题,就用这些信息告诉你哪些调用路径没有被计入录像;
|
||
- `@ignoreReplay(message)`:把方法标记为**忽略本次录像检查**,一般用于异步内容等不会立刻修改状态的方法。
|
||
|
||
检测区间的开始与结束由录像系统在每步录像执行前后统一控制:执行前开始收集,执行后结束并核对区间内是否产生了新的录像步。若没有,就在控制台输出告警,并列出登记过却未被计入的 `@shouldReplay` 路径。
|
||
|
||
:::warning
|
||
不要用 `@ignoreReplay` 来掩盖控制台的录像告警。它只应用于「确实不该计入录像」的方法,滥用很可能导致录像出错。
|
||
:::
|
||
|
||
> 录像指令的更多细节(内置指令各自怎么实现、录像格式与沙盒机制等)会在**高级教程**中讲解(预留标记:`高级教程 · 录像指令`);待高级教程就位后,此处会替换为链接。
|
||
|
||
<!-- TODO(advanced): 录像指令 -->
|