mirror of
https://github.com/motajs/template.git
synced 2026-11-02 23:14:34 +08:00
docs(10-11): 按 D-49 改类示例与改内置定位(查询/钩子)
This commit is contained in:
1 parent
3aae27a939
commit
9c57673e0a
3 files changed
+265
-122
No files matched your search
+90
-31
@@ -4,18 +4,28 @@
|
||||
|
||||
## 钩子是什么
|
||||
|
||||
在数据端,勇士、地图、战斗流程等主对象在发生关键动作时,会依次调用自己身上每个钩子的对应方法。于是你只需要写一个钩子对象、把它注册到关心的主对象上,就能在这些动作发生时收到通知。
|
||||
在数据端,勇士、地图、战斗流程等主对象在发生关键动作时,会依次调用自己身上每个钩子的对应方法。于是你只需要写一个钩子类、把它注册到关心的主对象上,就能在这些动作发生时收到通知。
|
||||
|
||||
一个钩子对象就是一组方法的集合,**「什么时候」由方法名决定,「做什么」由方法体决定**。例如想在勇士属性变化时做点什么,就实现 `onUpdateAttribute`:
|
||||
一个钩子就是一个**类**:类里实现某个钩子族接口的方法,**「什么时候」由方法名决定,「做什么」由方法体决定**。例如想在勇士属性变化时做点什么,就实现 `IHeroAttributeHooks` 的 `onUpdateAttribute`:
|
||||
|
||||
```ts
|
||||
const hook = {
|
||||
onUpdateAttribute(name, value) {
|
||||
// [模板] 钩子类:implements 后面写钩子族接口
|
||||
export class MyHeroAttributeHooks implements IHeroAttributeHooks<IHeroAttr> {
|
||||
// [模板] 构造函数里保存 state,方法体里才能取到数据端主对象
|
||||
constructor(readonly state: ICoreState) {}
|
||||
|
||||
// [需改] 方法体换成你要做的事
|
||||
onUpdateAttribute<K extends keyof IHeroAttr>(
|
||||
name: K,
|
||||
value: IHeroAttr[K]
|
||||
): void {
|
||||
// 当勇士的某个属性变化时执行
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
引擎里的内置钩子也是这样的类,例如 `src/data/hook/hero.ts` 的 `MainHeroAttributeHooks`;它们的实例在 `src/data/hook/initialize.ts` 里统一注册。日常改内置钩子时,不需要自己写上面这个类,改内置类的方法体即可(见下文「改内置钩子」)。
|
||||
|
||||
## 常用钩子族一览
|
||||
|
||||
「钩子族」是同一类钩子方法的集合(一个 `interface`)。常用的几族如下,方法名以源码为准:
|
||||
@@ -62,45 +72,56 @@ const hook = {
|
||||
|
||||
若某类需求暂未在表中找到,可以在对应主对象的 `IHookable` 接口处查看它支持的全部钩子方法(例如 `hero-attribute`、`enemy-status-hooks` 等篇会进一步说明相关钩子的用法)。
|
||||
|
||||
## 注册与移除钩子
|
||||
## 改内置钩子(推荐)
|
||||
|
||||
写好了钩子对象,还要挂到目标主对象上。用 `addHook` 完成注册,它会返回一个控制器。
|
||||
日常需求优先改内置钩子文件(`src/data/hook/*`):内置钩子已经在 `initializeHooks`(`src/data/hook/initialize.ts`)里注册好,你只要改对应类的方法体,不需要自己写类,也不需要再调用 `addHook`。
|
||||
|
||||
内置钩子类与所在文件:
|
||||
|
||||
| 钩子族 | 内置类 | 文件 |
|
||||
| ------------------------- | ---------------------------- | ------------------------- |
|
||||
| `ICombatFlowHooks` | `MainCombatFlowHooks` | `src/data/hook/combat.ts` |
|
||||
| `IGameEventExecutorHooks` | `MainGameEventExecutorHooks` | `src/data/hook/event.ts` |
|
||||
| `IHeroStateHooks` | `MainHeroStateHooks` | `src/data/hook/hero.ts` |
|
||||
| `IHeroAttributeHooks` | `MainHeroAttributeHooks` | `src/data/hook/hero.ts` |
|
||||
| `IHeroLocationHooks` | `MainHeroLocationHooks` | `src/data/hook/hero.ts` |
|
||||
| `IObjectMoverHooks` | `MainHeroMoverHooks` | `src/data/hook/hero.ts` |
|
||||
| `IMotaDataLoaderHooks` | `MainMotaDataLoaderHooks` | `src/data/hook/loader.ts` |
|
||||
| `IPathfindingSystemHooks` | `MainPathfindingSystemHooks` | `src/data/hook/path.ts` |
|
||||
|
||||
**目标**:在勇士某个属性变化时,立刻执行你写好的处理。
|
||||
|
||||
**改哪里**:希望效果生效的地方——事件函数、道具效果,或 `src/data/hook/` 下你新增的钩子文件都可以。
|
||||
**改哪里**:`src/data/hook/hero.ts` 的 `MainHeroAttributeHooks.onUpdateAttribute` 方法体。
|
||||
|
||||
**复制什么**:
|
||||
**复制什么**:下面的类本来就在文件里,复制时保留类与构造函数,只把方法体里的占位行换成你的处理:
|
||||
|
||||
```ts
|
||||
const hook = {
|
||||
onUpdateAttribute(name, value) {
|
||||
// 属性变化时要做的事
|
||||
export class MainHeroAttributeHooks implements IHeroAttributeHooks<IHeroAttr> {
|
||||
constructor(readonly state: ICoreState) {}
|
||||
|
||||
onUpdateAttribute<K extends keyof IHeroAttr>(
|
||||
name: K,
|
||||
value: IHeroAttr[K]
|
||||
): void {
|
||||
// [需改] 删掉下面两行占位,换成你的处理
|
||||
void name;
|
||||
void value;
|
||||
|
||||
// 示例:攻击变化时给出提示
|
||||
if (name === 'atk') {
|
||||
this.state.emitTip(`攻击变为 ${value}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const controller = state.hero.attribute.addHook(hook);
|
||||
}
|
||||
```
|
||||
|
||||
> 复制说明:片段已省略 `import`;文件顶部通常已有,不要再粘。若编辑器提示缺少某个名字,再在顶部补对应的 `import`(详见 [代码基础](./code-basics.md) 的「照抄教程里的代码时,怎么处理 import」)。
|
||||
> 复制说明:这段就是内置类的现有代码,不需要新增文件;若编辑器提示缺少某个名字,再在文件顶部补对应的 `import`(详见 [代码基础](./code-basics.md) 的「照抄教程里的代码时,怎么处理 import」)。
|
||||
|
||||
**改什么**:把 `onUpdateAttribute` 换成你要用的钩子方法名(对照上两张表),把方法体换成你的处理;把 `state.hero.attribute` 换成要挂接的目标主对象。
|
||||
**改什么**:把 `void name;` / `void value;` 换成你的处理;示例只是提示,也可以改成加属性、记 flag、触发事件等。
|
||||
|
||||
**怎么验证**:手动触发一次对应动作(例如改一次攻击力),看方法体里的效果有没有执行。
|
||||
**怎么验证**:手动改一次勇士攻击力(例如使用道具或调试命令),看提示有没有出现。
|
||||
|
||||
**注意**:一个钩子方法会对该主对象的**所有**同类动作触发——例如 `onUpdateAttribute` 对每个属性变化都触发,需要时要在方法体里判断 `name`,别误以为只对某一个属性生效。
|
||||
|
||||
需要移除时,有两种方式:把**原来的钩子对象**传给 `removeHook`,或调用 `addHook` 返回的控制器的 `unload` 方法。
|
||||
|
||||
```ts
|
||||
// 方式一:传回原来的钩子对象
|
||||
state.hero.attribute.removeHook(hook);
|
||||
|
||||
// 方式二:用注册时返回的控制器卸载
|
||||
controller.unload();
|
||||
```
|
||||
|
||||
数据端的内置钩子在 `src/data/hook/initialize.ts` 中统一注册(涵盖战斗、事件、勇士、加载、寻路等),它是了解内置钩子如何挂载的入口。
|
||||
**注意**:一个钩子方法会对该主对象的**所有**同类动作触发——例如 `onUpdateAttribute` 对每个属性变化都触发,需要时要在方法体里判断 `name`,别误以为只对某一个属性生效。内置类已注册,改完方法体就会生效,不要重复 `addHook`。
|
||||
|
||||
## 异步钩子
|
||||
|
||||
@@ -119,3 +140,41 @@ controller.unload();
|
||||
钩子由所属主对象触发,但同一个主对象可能对应多个游戏对象(例如场上有多只怪物)。因此钩子被触发时,不一定是针对你关心的那一只;当场上存在多个同类主对象时,需要在回调里根据传入的参数判断是否为你的目标对象。一般情况下不会串。
|
||||
|
||||
钩子建立在数据端的主对象之上,两端分工见[数据端与渲染端](./data-vs-render.md);编写钩子所需的语法见[代码基础](./code-basics.md)。
|
||||
|
||||
## 自己写钩子类
|
||||
|
||||
如果不想改内置文件,或需要给同一个主对象挂多个互不影响的钩子,可以自己写一个钩子类,再用 `addHook(new ...(state))` 注册:
|
||||
|
||||
```ts
|
||||
// [需改] 类名与处理逻辑
|
||||
export class MyHeroAttributeHooks implements IHeroAttributeHooks<IHeroAttr> {
|
||||
constructor(readonly state: ICoreState) {}
|
||||
|
||||
onUpdateAttribute<K extends keyof IHeroAttr>(
|
||||
name: K,
|
||||
value: IHeroAttr[K]
|
||||
): void {
|
||||
// 属性变化时要做的事
|
||||
}
|
||||
}
|
||||
|
||||
// [模板] 注册:实例先存进变量,之后移除时要用
|
||||
const hook = new MyHeroAttributeHooks(state);
|
||||
const controller = state.hero.attribute.addHook(hook);
|
||||
```
|
||||
|
||||
> 复制说明:片段已省略 `import`;文件顶部通常已有,不要再粘。若编辑器提示缺少某个名字,再在顶部补对应的 `import`(详见 [代码基础](./code-basics.md) 的「照抄教程里的代码时,怎么处理 import」)。
|
||||
|
||||
**改什么**:类名、`implements` 后面的钩子族接口、方法名(对照上两张表),以及 `addHook` 前面的目标主对象。
|
||||
|
||||
**注意**:一个主对象可以挂多个钩子,它们会依次被调用。需要移除时,保留注册时的实例或控制器:把**原来的实例**传给 `removeHook`,或调用控制器的 `unload` 方法。
|
||||
|
||||
```ts
|
||||
// 方式一:传回原来的钩子实例
|
||||
state.hero.attribute.removeHook(hook);
|
||||
|
||||
// 方式二:用注册时返回的控制器卸载
|
||||
controller.unload();
|
||||
```
|
||||
|
||||
**如果你不怎么懂代码,建议直接改内置钩子(`src/data/hook/*` 里已有的类),而不是自己写新的类。** 改内置只需要替换方法体,注册也已经完成;自己写类还要处理接口签名、构造函数与注册/移除时机。
|
||||
Reference in new issue
Block a user