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 篇:高级教程显式说明 + 非链接预留标记
This commit is contained in:
unanmed 2026-10-18 14:45:00 +08:00
parent 5c77676bca
commit 687e248ac1
15 changed files with 174 additions and 24 deletions

View File

@ -322,3 +322,30 @@
- 格式门禁:`pnpm exec prettier --write docs/intermediate/*.md`(D-45)已跑,被改 6 篇格式化通过。
- 构建门禁:`cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` → **green**,输出 `build complete`,dead link = 0。
---
## 用户循环 · batch 3 记录(基础接口 / 其他 + 高级教程预留)
**范围:** 用户修订循环第 1 轮 batch 3(U-01、U-02、U-04、U-05、U-06、U-20、U-21、U-22)。**U-03 不在本批**(需新增一篇基础接口文档并先经命名审批,另行处理)。
### 逐条处置
| # | 处置 | 改动文件 |
|---|------|----------|
| U-01 | `src-tour.md` 的 `src/content` 一节标题补注「(初级教程范畴)」,并加一段:内容层**主要在编辑器内修改**、对应**初级教程**,本中级教程不手工编辑这些 `.jsonc`,只将其视为「配置数据存放处」(知道 `tile.jsonc` 条目的 `num` 即[图块数字](./map-tiles.md)才能在代码里定位);「该不该改」表该行改为「配置数据,主要在编辑器内改」。 | `src-tour.md` |
| U-02 | `data-state-overview.md` 的 L0–L3 节新增「为什么有些地方的 state 看不到全部层级」:说明 `state` 运行时始终是同一个主对象,分层只是其**静态类型**逐层累加——L0 `IDataCommon`(公共层)→ L1 `IStateBase`(数据层)→ L2 `IStateSystem`(系统层)→ L3 `ICoreState`(顶层),每层只声明自己拥有的成员;故从底层对象取到的 `state` 类型更窄、看不到更高层成员,要完整成员用 L3 的 `ICoreState`。 | `data-state-overview.md` |
| U-04 | `map-structure.md` 新增「如何取到指定图层」:`layerList` 是 `Set<IMapLayer>`,本身无按名取法,从其中按 `alias`(最常用)或 `zIndex` 筛出目标图层;事件层直接取 `map.eventLayer`;并提醒 `addLayer(alias)` 是**新建**图层而非取用。 | `map-structure.md` |
| U-05 | `map-tiles.md` 的「批量读写」补一个**完整示例**(3 列 × 2 行 `Uint32Array` → `putMapData(data, 5, 5, 3)` → `getMapData(5, 5, 3, 2)` 读回),并说明扁平数组第 `row * width + col` 项对应偏移 `(col, row)`;注明更深入的用法在高级教程。 | `map-tiles.md` |
| U-06 | `flags.md` 原「字段」节改写为「字段与字段值」:区分**字段对象**(`IFlagCommonField`,有 `get` / `set` / `add`)与**字段值**(普通值);接口分为两组——返回字段的 `setField` / `getField` / `getOrInsert` / `getOrInsertComputed`,操作值的 `setFieldValue` / `addFieldValue` / `getFieldValue` / `getFieldValueDefaults`;给出选择依据(一次读写用值、反复操作用字段对象)与互相等价关系。 | `flags.md` |
| U-20 | `events.md` 以「内置事件集**已完整**」视角改写:开篇说明发布前约 **100** 个内置事件;注册节把代码块标为**节选**、删去「注册了 7 个」的定性句;内置导读改为「按用途分散在若干文件」;并明确**新增一个事件**只需「写函数类 + `new` 进 `createEventRegistrations()`」两步。全文不再出现「功能没做完」式表述。 | `events.md` |
| U-21 | `replay-commands.md` 末尾加注:录像指令的更多细节会在**高级教程**中讲解。 | `replay-commands.md` |
| U-22 | 在「留高级」主题所在篇目统一加一句显式说明 + **预留标记**(可见代码 span `高级教程 · <topic>` + 非渲染 HTML 注释 `<!-- TODO(advanced): <topic> -->`),并说明高级教程就位后替换为链接。**不引入真实链接**(`docs/advanced/` 尚未创建,真链接会死链):渲染端结构(`data-vs-render`)、地图通行与踩踏(`map-structure`)、静态与动态图块 / 地图批量接口 / `getLocationData`(`map-tiles`)、光环设计原理(`enemy-aura`)、查询设计原理(`enemy-query`)、插件系统工作原理(`data-plugins`)、装备排序器(`hero-equipment`)、修饰器原理(`hero-attribute`)、数据端分层(`data-state-overview`)、位置移动与切换楼层(`hero-location`)、录像指令(`replay-commands`)。 | `data-vs-render.md`、`map-structure.md`、`map-tiles.md`、`enemy-aura.md`、`enemy-query.md`、`data-plugins.md`、`hero-equipment.md`、`hero-attribute.md`、`data-state-overview.md`、`hero-location.md`、`replay-commands.md` |
### 校验
- 源码核对:`packages-user/data-common/src/types.ts`(`IDataCommon`:`tileStore` / `itemStore` / `mapStore` / `eventStore` / `roleFace` / `faceManager` / `replaySystem` / `emitTip`)、`packages-user/data-base/src/types.ts`(`IStateBase`:`maps` / `hero` / `enemyManager` / `flags`)、`packages-user/data-system/src/types.ts`(`IStateSystem`:`enemyContext` / `combat` / `eventSystem` / `pathfinding`)、`packages-user/data-state/src/types.ts`(`ICoreState`:`loader` / `loadManager` / `getDataPlugin` / `load`)、`packages-user/data-base/src/map/types.ts`(`IGameMap.layerList` / `eventLayer`;`IMapLayer.alias` / `zIndex`;`ILayerStatic.putMapData` 与 `getMapData` 两处重载 `:332, :337, :345`)、`packages-user/data-base/src/map/gameMap.ts`(`addLayer` 每次 `new MapLayer`、`aliasLayerMap` 未暴露于 `IGameMap`)、`packages-user/data-base/src/flag/types.ts`(`IFlagCommonField.get/set/add`;`IFlagSystem` 的字段接口与值接口分组)、`src/data/event/registrations.ts`(`createEventRegistrations()` 返回 `BuiltInFunction[]`)。
- 结构门禁:24 篇全部 CRLF、单 H1(`STRUCT-OK`)。
- 格式门禁:`pnpm exec prettier --write`(D-45)已跑,改动篇目格式化通过;`map-tiles.md` 示例数组用 `// prettier-ignore` 保留**按行分组**(否则会被折成逐元素一行,失去「按行排列」的演示效果)。
- 构建门禁:`cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` → **green**,输出 `build complete`,dead link = 0(高级教程预留使用代码 span 与 HTML 注释,不产生链接)。
- 边界:仅改 `docs/intermediate/*.md`;未改 `config.ts` 与任何生产代码;未新增文件;U-03 不在本批。

View File

@ -1,6 +1,8 @@
# 数据插件
当你有一组相关功能想打包在一起、挂到数据端主对象 `state` 上,并让它们按固定的时机自动初始化,就可以把它做成一个**数据插件**。插件是数据端的**强拓展系统**:它把一组相关的对象与逻辑收拢到一处,随数据端启动一起加装。本篇只讲基础用法;插件系统更底层的工作原理见高级教程。
当你有一组相关功能想打包在一起、挂到数据端主对象 `state` 上,并让它们按固定的时机自动初始化,就可以把它做成一个**数据插件**。插件是数据端的**强拓展系统**:它把一组相关的对象与逻辑收拢到一处,随数据端启动一起加装。本篇只讲基础用法,插件系统更底层的工作原理(依赖如何排序、三个时机在加载流程中的位置等)会在**高级教程**中系统讲解(预留标记:`高级教程 · 插件系统工作原理`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 插件系统工作原理 -->
## 插件定义 IDataPluginDefine

View File

@ -93,6 +93,21 @@ const map = state.maps.getActiveMap('floor-1'); // L1
state.emitTip('获得黄钥匙'); // L0
```
### 为什么有些地方的 state 看不到全部层级
`state` 在运行时始终是同一个主对象,分层只是它的**静态类型**在逐层累加。类型定义一层叠一层,每层只声明自己这一层拥有的成员:
- L0 `IDataCommon`:公共层,声明 `tileStore` / `itemStore` / `mapStore` / `eventStore` / `faceManager` / `roleFace` / `replaySystem` / `emitTip` 等共享设施;
- L1 `IStateBase`:在 L0 之上加数据层成员,即 `hero` / `maps` / `flags` / `enemyManager`;
- L2 `IStateSystem`:在 L1 之上加系统层成员,即 `enemyContext` / `combat` / `eventSystem` / `pathfinding`;
- L3 `ICoreState`:在 L2 之上加顶层成员,即 `loader` / `loadManager` / `getDataPlugin` 等。
所以,当你从某个**底层对象**上取到 `state` 时,它的类型可能只标到 L0 或 L1——编辑器里就看不到 `hero`、`flags` 这些更高层的成员。这并不是它们不存在,而是**当前这层接口没有声明它们**:运行时是同一个对象,只是编译期类型更窄。这正是「分层」的实际体现:越往上,成员越全。
要使用完整成员,就拿 L3 的 `ICoreState`,也就是框架作为参数传入、或从 `handler.state` 取到的那个 `state`(获取方式见上文「state 从哪来」)。这套分层的设计原理(为什么这样拆、每层负责什么)会在**高级教程**中展开(预留标记:`高级教程 · 数据端分层`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 数据端分层 -->
## 从这里继续
- 勇士的数值与加成:见[勇士属性](./hero-attribute.md);

View File

@ -50,4 +50,8 @@ graph LR
- 数据端可脱离浏览器单独运行,**录像验证只跑数据端**。
- 改变规则与数值看数据端,改变画面与交互看渲染端。
> 渲染端的结构性内容(渲染端内部如何组织、它与数据端的边界细节等)会在**高级教程**中系统讲解(预留标记:`高级教程 · 渲染端结构`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 渲染端结构 -->
想了解两端内部各有哪些内容、哪些需要改,见 [src 导览](./src-tour.md);想在数据端的关键动作上挂接自己的处理,见 [钩子](./hooks.md)。

View File

@ -386,3 +386,7 @@ export function initializeEnemy(
```
把 `registerAuraConverter(new WeakAuraConverter())` 放在这几行之后,你的光环就会随 `initializeEnemy` 一起生效。
> 光环机制的设计原理(优先级为何这样设计、转换器与视图为何要分开、范围是如何定义的)会在**高级教程**中讲解(预留标记:`高级教程 · 光环设计原理`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 光环设计原理 -->

View File

@ -259,3 +259,7 @@ export function initializeEnemy(
```
把注册调用放在这里,查询就会参与构建计算后怪物。
> 查询机制的设计原理(为什么常规查询要绑定 code、查询上下文为何是只读的、它与光环如何共用优先级)会在**高级教程**中讲解(预留标记:`高级教程 · 查询设计原理`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 查询设计原理 -->

View File

@ -1,6 +1,6 @@
# 自定义事件
在地图事件里,你能调用一批内建动作——设置图块、移动勇士、往事件里再插一段……这些都是引擎内置的**事件函数**。当内置的还不够用时,你也能照着同样的方式加一个自己的动作,让事件语句调用到它。
在地图事件里,你能调用一批内建动作——设置图块、移动勇士、往事件里再插一段……这些都是引擎内置的**事件函数**。内置事件集已经相当完整(发布前会提供约 100 个,覆盖地图、玩家、事件、流程控制等常用动作);当你需要其中没有的额外动作时,也能照着同样的方式加一个自己的事件函数,让事件语句调用到它。
本篇只讲「如何自定义新的事件函数」,不展开事件系统的原理。事件环境里会用到数据端主对象 `state`,它的获取方式与含义见[数据端总览](./data-state-overview.md)。
@ -58,7 +58,7 @@ interface ISetBlockEventParam {
## 注册事件
光写内建函数还不够,还要把它注册进事件系统。注册入口是 `src/data/event/registrations.ts` 的 `createEventRegistrations()`,它返回一个 `BuiltInFunction[]`:
光写内建函数还不够,还要把它注册进事件系统。注册入口是 `src/data/event/registrations.ts` 的 `createEventRegistrations()`,它返回一个 `BuiltInFunction[]`。下面是它的**节选**(内置事件总数约 100 个,这里只列几个作示例):
```ts
export function createEventRegistrations(): BuiltInFunction[] {
@ -75,21 +75,23 @@ export function createEventRegistrations(): BuiltInFunction[] {
// 事件控制
new EventInsertEvents(),
new EventInsertEvent()
// ...其余内置事件(发布前合计约 100 个)
];
}
```
目前它注册了 7 个内置事件:地图控制 `setBlock` / `moveBlock` / `removeBlock`、玩家控制 `moveHero` / `stepHero`、事件控制 `insertEvents` / `insertEvent`。把你自己写的事件函数 `new` 出来加进这个数组,就完成注册。
这些内置事件覆盖了地图控制(`setBlock` / `moveBlock` / `removeBlock` 等)、玩家控制(`moveHero` / `stepHero` 等)、事件控制(`insertEvents` / `insertEvent` 等)以及更多常用动作。要**新增一个事件**,只需两步:按上一节写好事件函数类,再把它 `new` 出来加进这个数组。
## 内置事件导读
内置事件的实现分别放在 `src/data/event/` 下:
内置事件的实现按用途分散在 `src/data/event/` 下的若干文件里,例如:
- `map.ts`:图块相关。`setBlock` 取到事件图层后把指定坐标设为某个图块,`moveBlock` 把图块按步骤移动,`removeBlock` 删除图块;
- `hero.ts`:勇士相关。`moveHero` 让勇士按给定步骤移动,`stepHero` 让勇士前进一步;
- `event.ts`:事件相关。`insertEvents` 按 id 临时插入多个已有事件,`insertEvent` 临时执行一段事件语句。
想照着写,挑一个内置事件读它的 `func` 即可。
想照着写新事件,挑一个功能相近的内置事件读它的 `func` 即可。
## 示例:设置 Flag

View File

@ -2,9 +2,32 @@
有些状态要跨事件、跨楼层一直保留,还要跟着存档走——比如「Boss 是否已被击败」「这道门开过没有」「某处计数到了几」。这些状态放哪?Flag 系统(`state.flags`)就是数据端提供的**全局可存档变量**:任何需要跨事件、跨楼层保留的简单状态,都可以存进 Flag,它会随存档一起保存与恢复。
## 字段
## 字段与字段值
Flag 中的每一项是一个**字段**(`IFlagCommonField`),字段名与值一一对应。字段对象提供 `set(value)`(设置值)、`add(value)`(对数字值增减)与 `get()`(取值)。一般不需要直接操作字段对象,用下面的系统接口即可。
Flag 里的每一项都对应一个**字段**(`IFlagCommonField`)。要读懂下面的接口清单,先分清两个概念:
- **字段**是一个**对象**,代表「这个变量本身」,提供 `get()`(取值)、`set(value)`(设置值)、`add(value)`(对数字值增减)等方法;
- **字段值**就是字段里存的**那个普通值**(数字、字符串、布尔等)。你平时读写的,大多是字段值。
系统接口因此分成两组,看名字就能分辨:
- **返回或操作「字段」(对象)**的:`setField` / `getField` / `getOrInsert` / `getOrInsertComputed`——它们返回字段对象本身;
- **直接操作「字段值」**的:`setFieldValue` / `addFieldValue` / `getFieldValue` / `getFieldValueDefaults`——只关心值,不暴露字段对象。
怎么选?**只读写一次值**时用「值」那组更简单,不必多一步 `get()`;**要多次操作同一个字段、或要复用字段对象**(例如先拿到字段、再反复增减)时,用「字段对象」那组更省事。两者可以互相转换:
```ts
// 用「字段值」的写法:一次设置、一次增减
state.flags.setFieldValue('open-count', 0);
state.flags.addFieldValue('open-count', 1);
// 用「字段对象」的写法:拿到字段后反复操作
const field = state.flags.getOrInsert('open-count', 0);
field.add(1);
const value = field.get();
```
其实「值」那组就是对「字段对象」那组的简写:`setFieldValue(name, v)` 等价于 `getField(name)?.set(v)`,`getFieldValue(name)` 等价于 `getField(name)?.get()`,`addFieldValue(name, v)` 等价于 `getField(name)?.add(v)`,`getFieldValueDefaults(name, d)` 等价于 `getOrInsert(name, d).get()`。
## 全部接口

View File

@ -95,3 +95,7 @@ state.hero.getModifiableAttribute().addModifier('def', weaken);
```
想在属性变化时收到通知,可以用属性钩子族的 `onUpdateAttribute`,见[钩子](./hooks.md)。
> 修饰器的底层原理(修饰器如何参与属性求值、加减乘除类修饰器的组合规则等)会在**高级教程**中讲解(预留标记:`高级教程 · 修饰器原理`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 修饰器原理 -->

View File

@ -62,4 +62,8 @@ const diff = state.hero.equip.compareEquip(uid, -1, 0);
// diff.atk 即装备这件装备相对空槽的攻击提升
```
> 装备排序器(决定同一槽位上多件装备如何排序、如何挑选「更好」的一件)会在**高级教程**中讲解(预留标记:`高级教程 · 装备排序器`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 装备排序器 -->
装备道具本身来自道具定义,见[勇士道具](./hero-items.md);想从 `state` 出发找到这些对象,见[数据端总览](./data-state-overview.md)。

View File

@ -1,6 +1,8 @@
# 勇士位置
很多逻辑都要先知道一件事:勇士此刻站在哪一层、哪一格、朝哪个方向。比如「进门前朝向对不对」「这只怪离勇士多远」「当前在哪一层」。勇士位置(`state.hero.location`)记录的就是这些信息。本篇只讲如何**获取**它们;如何移动、如何切换楼层属于更底层的内容,留到高级教程。
很多逻辑都要先知道一件事:勇士此刻站在哪一层、哪一格、朝哪个方向。比如「进门前朝向对不对」「这只怪离勇士多远」「当前在哪一层」。勇士位置(`state.hero.location`)记录的就是这些信息。本篇只讲如何**获取**它们;如何移动、如何切换楼层属于更底层的内容,会在**高级教程**中讲解(预留标记:`高级教程 · 位置移动与切换楼层`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 位置移动与切换楼层 -->
## 当前坐标

View File

@ -48,9 +48,38 @@ const active = state.maps.getActiveMap('floor-1');
`layerList` 中的每个图层(`IMapLayer`)都有:
- `alias`:图层别名;
- `alias`:图层别名,用于标识这个图层;
- `zIndex`:图层纵深,决定渲染时的遮挡顺序;
- `width`、`height`:图层宽高。
**事件层本质就是一个图层**:`eventLayer` 的类型同样是 `IMapLayer`,因此对图层的操作同样适用于事件层。图块的读写见[地图图块](./map-tiles.md)。
**事件层本质就是一个图层**:`eventLayer` 的类型同样是 `IMapLayer`,因此对图层的操作同样适用于事件层。
### 如何取到指定图层
`layerList` 是一个集合(`Set<IMapLayer>`),本身没有「按名字直接取」的方法,所以从 `layerList` 里筛出想要的那一个即可:按 **`alias`** 找(最常用),或按 **`zIndex`** 找。
```ts
const map = state.maps.getMap('floor-1')!;
// 按别名取:例如名为 'ground' 的图层
const ground = [...map.layerList].find(layer => layer.alias === 'ground');
// 按纵深取:例如 zIndex 为 2 的图层
const second = [...map.layerList].find(layer => layer.zIndex === 2);
```
事件层是个例外:它可以直接从楼层的 `eventLayer` 取得,不必去筛 `layerList`。
```ts
const eventLayer = map.eventLayer; // IMapLayer | null
```
注意别把 `addLayer(alias)` 当成「取图层」——它是**新建**一个图层并返回,用同一个别名调用会再建一个。取已有图层请按上面的方式从 `layerList` 里找。
图块的读写见[地图图块](./map-tiles.md)。
> 地图的通行性判定、图块移动与踩踏触发等更底层的内容,会在**高级教程**中讲解(预留标记:`高级教程 · 地图通行与踩踏`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 地图通行与踩踏 -->
想从 `state` 出发找到地图对象,见[数据端总览](./data-state-overview.md);勇士当前所在楼层见[勇士位置](./hero-location.md)。

View File

@ -4,6 +4,10 @@
图层的接口分成两半:一半管**静态图块**(`ILayerStatic`,负责单点与批量读写、迭代),一半管**动态图块**(`ILayerDynamic`,负责创建、删除、移动)。本篇只讲静态图块那半,也就是 `ILayerStatic` 上的方法。
> 静态图块与动态图块各自的设计取向(为什么要把图块分成这两种、它们之间如何转换)会在**高级教程**中讲解(预留标记:`高级教程 · 静态与动态图块`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 静态与动态图块 -->
先明确一个词:**图块数字**(tile number)是每个图块的整数编号,以内容层 `src/content/tile.jsonc` 中每个条目的 `num` 字段为准(同一条目还有字符串名 `id`)。图层上存取的都是这个编号;想用 `id` 换编号可以用 `state.tileStore.num(id)`,反过来用 `state.tileStore.id(num)`。
## 取到图层
@ -43,10 +47,28 @@ layer.removeBlock(3, 5);
返回的数组是对内部存储的拷贝,修改它不会直接改动地图。
**完整示例:一次写入一片 3 列 × 2 行的区域,再读回来。**
```ts
const region = layer.getMapData(0, 0, 3, 3);
const width = 3;
// 按行排列:先第 0 行(y = 5),再第 1 行(y = 6)
// prettier-ignore
const data = new Uint32Array([
21, 21, 21, // 第 0 行
22, 22, 22 // 第 1 行
]);
// 从 (5, 5) 开始写入;高度由 6 ÷ 3 = 2 自动推出
layer.putMapData(data, 5, 5, width);
// 读出刚写入的区域(区域版必须显式传宽高)
const back = layer.getMapData(5, 5, width, 2);
// back 与 data 内容一致:长度 6、按行排列
```
`putMapData` 与 `getMapData` 用的都是「扁平」的一维数组:第 `row * width + col` 项,对应左上角偏移 `(col, row)` 的那一格。日常读写用这两个接口就够了;它们更深入的用法与性能取向会在**高级教程**中讲解(预留标记:`高级教程 · 地图批量接口`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 地图批量接口 -->
## 迭代非空图块
`iterateBlocks()` 返回所有非空图块,每个元素包含该点的位置 `locator`、图块数字 `tile`、静态图块 `static` 与动态图块 `dynamics`。非空指图块数字不为 0,因此只挂事件、没有图块的格子不会被迭代到。
@ -57,6 +79,8 @@ for (const block of layer.iterateBlocks()) {
}
```
若需要读取某一点的全部图块信息(静态、动态、事件等),图层还提供 `getLocationData(x, y)`;它面向更底层的需求,留到高级教程。
若需要读取某一点的全部图块信息(静态、动态、事件等),图层还提供 `getLocationData(x, y)`;它面向更底层的需求,会在**高级教程**中讲解(预留标记:`高级教程 · getLocationData`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): getLocationData -->
图层与事件层的结构见[地图结构](./map-structure.md);想从 `state` 出发找到地图对象,见[数据端总览](./data-state-overview.md)。

View File

@ -46,3 +46,7 @@
:::warning
不要用 `@ignoreReplay` 来掩盖控制台的录像告警。它只应用于「确实不该计入录像」的方法,滥用很可能导致录像出错。
:::
> 录像指令的更多细节(内置指令各自怎么实现、录像格式与沙盒机制等)会在**高级教程**中讲解(预留标记:`高级教程 · 录像指令`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 录像指令 -->

View File

@ -37,7 +37,7 @@
两侧如何分工、各自负责什么,见[数据端与渲染端](./data-vs-render.md)。
## src/content:内容层
## src/content:内容层(初级教程范畴)
游戏内容与代码逻辑分开存放,大多以 `.jsonc` 形式存在。想调数值、换贴图、改地图,就来这里找:
@ -46,6 +46,8 @@
- `maps/`:游戏地图;
- `item.ts`:道具效果的代码实现——内容层可以写少量代码,用来描述「使用某个道具会发生什么」。
这一层的内容**主要在编辑器内修改**,对应的是**初级教程**的内容,本中级教程不会让你手工编辑这些 `.jsonc` 文件。这里只把它们当作「配置数据的存放处」来了解:知道某个图块、怪物或道具的编号与字段长什么样,才能在代码里正确定位它(例如 `tile.jsonc` 里条目的 `num` 就是[图块数字](./map-tiles.md),配合 `state.tileStore` 换算)。要在代码里读写这些内容,请继续看后续各篇。
## src/constant.ts:常量
游戏的底层核心常量都集中在这个文件,例如数据端核心配置 `CORE_URL`、勇士默认朝向组 `HERO_FACE`、渲染画布 `RENDER_CANVAS`、渲染宽高 `RENDER_WIDTH` 与 `RENDER_HEIGHT` 等。文件里已注明「不懂的话千万不要动」,一般不需要修改。
@ -63,15 +65,15 @@
把上面几节收成一张表,动手前对照一下:
| 路径 | 用途 | 是否通常需要改动 |
| ------------------ | ---------------- | ---------------------- |
| `src/data/` | 数据端逻辑与结果 | 是,本教程重点 |
| `src/client/` | 渲染端画面与交互 | 视需求,交互类需求会改 |
| `src/content/` | 游戏内容数据 | 是,配置与内容 |
| `src/constant.ts` | 底层核心常量 | 否,一般不动 |
| `src/data.ts` | 数据端入口 | 否,一般不动 |
| `src/bootstrap.ts` | 启动入口 | 否,一般不动 |
| `src/package.json` | 入口包依赖声明 | 否,一般不动 |
| `src/styles.less` | 全局样式 | 否,一般不动 |
| 路径 | 用途 | 是否通常需要改动 |
| ------------------ | ---------------- | -------------------------- |
| `src/data/` | 数据端逻辑与结果 | 是,本教程重点 |
| `src/client/` | 渲染端画面与交互 | 视需求,交互类需求会改 |
| `src/content/` | 游戏内容数据 | 配置数据,主要在编辑器内改 |
| `src/constant.ts` | 底层核心常量 | 否,一般不动 |
| `src/data.ts` | 数据端入口 | 否,一般不动 |
| `src/bootstrap.ts` | 启动入口 | 否,一般不动 |
| `src/package.json` | 入口包依赖声明 | 否,一般不动 |
| `src/styles.less` | 全局样式 | 否,一般不动 |
想先补齐读写这些文件所需的语法与类型知识,见[代码基础](./code-basics.md)。