template/docs/intermediate/data-state-overview.md

118 lines
7.1 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` 的对象开始。本篇先讲清 `state` 是什么、怎样拿到它,再列出作者最常需要的对象与接口,作为中级教程其余篇章的查表入口。
## state:一切入口
`state` 是数据端的主对象(类型为 `ICoreState`),可以把它想成整个游戏的**总目录**:勇士、地图、怪物、战斗、Flag(全局可存档变量)等每一个子系统都挂在它下面。拿到 `state`,就等于拿到了通往整个数据端的入口。
```ts
// 从 state 出发访问勇士、地图与 Flag
const hp = state.hero.attribute.getFinalAttribute('hp');
const floorId = state.hero.location.floorId;
const cleared = state.flags.getFieldValue('cleared');
```
## state 从哪来
写代码的位置不同,`state` 的来路也不同,常见的有三类。
**一是框架作为参数传入。** 你写的函数由框架调用时,`state` 常常就是它的一个参数:
- 道具效果:`useEffect(item, state)`,主对象是第二个参数;
- 数据插件:`awake(state, ...)`、`initialize(state, ...)`、`afterLoad(state, ...)`,主对象是第一个参数;
- 事件内建函数:从环境对象上取 `env.state`。
```ts
// 道具效果:state 由框架作为第二个参数传入
function useEffect(item, state) {
state.hero.getModifiableAttribute().add('atk', 5);
}
```
**二是从处理对象上取。** 战斗、怪物等上下文里的处理对象自带一个 `state`,直接读 `handler.state` 即可,不必再向外找。
```ts
// 处理对象上带着主对象
handler.state.flags.setFieldValue('battle-start', true);
```
**三是存到自己的对象上。** 自己写的类可以在构造时接收 `state` 并保存下来,之后用 `this.state` 访问它。
```ts
class MySystem {
constructor(private readonly state) {}
mark() {
this.state.flags.setFieldValue('my-system', true);
}
}
```
本教程后续的代码示例都假设 `state` 已经在当前作用域中(由上面任一方式获得),因此示例不再重复它的来源;示例也一律**省略 `import`**,照抄时怎么处理引入见 [代码基础](./code-basics.md#照抄教程里的代码时-怎么处理-import) 的「照抄教程里的代码时,怎么处理 import」。
## 常用对象与获取方式
下表把最常用的对象与接口列在一起——只要拿到了 `state`,就能按表中的方式访问它们:
| 对象 | 获取方式 | 用途 | 分层 |
| ------------------- | ---------------------------- | ---------------------------------- | ---- |
| `hero` | `state.hero` | 勇士状态(属性、道具、装备、位置) | L1 |
| `maps` | `state.maps` | 楼层集合与楼层激活 | L1 |
| `flags` | `state.flags` | 全局可存档变量 | L1 |
| `enemyManager` | `state.enemyManager` | 怪物创建与查询 | L1 |
| `enemyContext` | `state.enemyContext` | 怪物属性构建与光环 | L2 |
| `combat` | `state.combat` | 战斗流程 | L2 |
| `eventSystem` | `state.eventSystem` | 地图事件执行 | L2 |
| `pathfinding` | `state.pathfinding` | 勇士寻路 | L2 |
| `tileStore` | `state.tileStore` | 图块定义存储 | L0 |
| `itemStore` | `state.itemStore` | 道具定义存储 | L0 |
| `mapStore` | `state.mapStore` | 地图定义存储 | L0 |
| `eventStore` | `state.eventStore` | 游戏事件存储 | L0 |
| `faceManager` | `state.faceManager` | 朝向管理 | L0 |
| `roleFace` | `state.roleFace` | 朝向绑定 | L0 |
| `replaySystem` | `state.replaySystem` | 录像系统 | L0 |
| `loader` | `state.loader` | 数据端加载对象 | L3 |
| `loadManager` | `state.loadManager` | 加载管理器 | L3 |
| `getDataPlugin(id)` | `state.getDataPlugin('xxx')` | 取得已加装的插件 | L3 |
| `emitTip(...)` | `state.emitTip('提示')` | 通知渲染端提示 | L0 |
```ts
const plugin = state.getDataPlugin('main');
state.emitTip('获得黄钥匙');
```
## 分层标注(L0–L3)
上表的「分层」列标注了对象所属的模块层级:L0 公共层、L1 数据层、L2 系统层、L3 顶层。分层的意义在于区分「哪些内容会进存档」「哪些内容负责产生动作」,但**不影响你如何使用它们**——所有对象都能从 `state` 直接访问,调用方式完全一致。
```ts
// 无论哪个分层,访问方式都相同
state.flags.setFieldValue('door-open', true); // L1
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);
- 勇士的道具与装备:见[勇士道具](./hero-items.md);
- 地图的层次结构:见[地图结构](./map-structure.md);
- 全局可存档变量:见[Flag 变量](./flags.md);
- 在关键动作上挂接处理:见[钩子](./hooks.md)。