Files
template/docs/intermediate/replay-commands.md
T
AncTe 687e248ac1 docs(10-11): 用户修订循环 batch3(基础/其他 + 高级教程预留)
- 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 篇:高级教程显式说明 + 非链接预留标记
2026-10-18 14:45:00 +08:00

53 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 录像指令
录像验证要能重放出和原来一模一样的结果,前提是:把该记下来的操作都记下来。录像系统(`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): 录像指令 -->