template/docs/intermediate/hooks.md
unanmed c47aa5269b docs(10-02): write code-basics and hooks concept docs
- code-basics: concise syntax, modules/imports, types intro (generics as
  'functions of types'), interface/implements convention, approved external link
- hooks: what hooks are, common hook families, need-to-hook table, addHook/
  removeHook usage, async hooks
2026-10-17 17:49:49 +08:00

5.7 KiB
Raw Blame History

钩子

钩子(hook)是本引擎提供的「事件订阅」机制。当游戏里发生某个关键动作时,主对象会通知所有挂在它上面的钩子;你在钩子里写下想做的事,就能在正确的时机介入游戏流程。

钩子是什么

数据端的主对象(如勇士、地图、战斗流程等)在发生关键动作时,会依次调用自己身上每个钩子的对应方法。你只需要写一个钩子对象,把它注册到关心的主对象上,就能在这些动作发生时收到通知。

一个钩子对象就是若干个方法的集合,方法名对应「什么时候触发」。例如想在勇士属性变化时做点什么,就实现 onUpdateAttribute:

const hook = {
    onUpdateAttribute(name, value) {
        // 当勇士的某个属性变化时执行
    }
};

常用钩子族一览

「钩子族」是同一类钩子方法的集合(一个 interface)。常用的几族如下,方法名以源码为准:

钩子族 所属主对象 主要方法
IDataCommonHooks 数据端公共对象 onTip
IHeroAttributeHooks 勇士属性 onUpdateAttribute
IHeroLocationHooks 勇士位置 onSetPos、onSetFloor
IHeroStateHooks 勇士状态 onBeforeChangeFloor、onAfterChangeFloor
IEquipmentStateHooks 装备 onChangeModifier
IHeroFollowersControllerHooks 勇士跟随者 onAddFollower、onRemoveFollower、onGatherFollowers
IObjectMoverHooks 移动器 onMoveStart、onMoveEnd、onStepStart、onStepEnd、onSetPos、onSetFaceDir、onSetMoveDir
ICombatFlowHooks 战斗流程 onBeforeCombat、onAfterCombat
IGameEventExecutorHooks 事件执行器 onBeforeExecuteEvent、onAfterExecuteEvent
IPathfindingSystemHooks 寻路系统 onBeforeTeleportTo、onAfterTeleportTo
IMapLayerHooks 地图图层 onResize、onUpdateArea、onUpdateBlock、onOpenDoor、onCloseDoor、onCreateDynamic、onDeleteDynamic、onUpdateDynamicPosition
IGameMapHooks 地图 onChangeBackground、onUpdateLayer、onResizeLayer
IReplaySystemHooks 录像系统 onCreateSandbox、onRecordCommand
IReplaySandboxHooks 录像沙盒 onStep、onSpeedSet、onStartReplay、onStopReplay、onPauseReplay、onResumeReplay

需求 → 钩子对照表

下表把常见需求和对应的钩子方法列在一起,照着找即可:

需求 对应钩子
勇士属性变化时做点事 IHeroAttributeHooks.onUpdateAttribute
勇士被设置坐标或切换楼层 IHeroLocationHooks.onSetPos / onSetFloor
勇士移动开始、结束或每走一步 IObjectMoverHooks.onMoveStart / onMoveEnd / onStepStart / onStepEnd
切换楼层前后做过渡处理 IHeroStateHooks.onBeforeChangeFloor / onAfterChangeFloor
战斗前或战斗后介入 ICombatFlowHooks.onBeforeCombat / onAfterCombat
事件执行前后介入 IGameEventExecutorHooks.onBeforeExecuteEvent / onAfterExecuteEvent
地图图块被修改、区域被更新 IMapLayerHooks.onUpdateBlock / onUpdateArea
开门或关门时做点什么 IMapLayerHooks.onOpenDoor / onCloseDoor
动态图块被创建或删除 IMapLayerHooks.onCreateDynamic / onDeleteDynamic
装备的修饰器发生变化 IEquipmentStateHooks.onChangeModifier
瞬移寻路前后介入 IPathfindingSystemHooks.onBeforeTeleportTo / onAfterTeleportTo
录像播放到某一步 IReplaySandboxHooks.onStep
录像记录新指令 IReplaySystemHooks.onRecordCommand
通知渲染端进行一次提示 IDataCommonHooks.onTip

若某类需求暂未在表中找到,可以在对应主对象的 IHookable 接口处查看它支持的全部钩子方法(例如 hero-attribute、enemy-status-hooks 等篇会进一步说明相关钩子的用法)。

注册与移除钩子

用 addHook 把钩子对象注册到主对象上,它会返回一个控制器:

const controller = state.hero.attribute.addHook({
    onUpdateAttribute(name, value) {
        // 属性变化时的处理
    }
});

需要移除时,有两种方式:把原来的钩子对象传给 removeHook,或调用 addHook 返回的控制器的 unload 方法。

// 方式一:传回原来的钩子对象
state.hero.attribute.removeHook(hook);

// 方式二:用注册时返回的控制器卸载
controller.unload();

数据端的内置钩子在 src/data/hook/initialize.ts 中统一注册(涵盖战斗、事件、勇士、加载、寻路等),它是了解内置钩子如何挂载的入口。

异步钩子

部分钩子返回 Promise,是异步钩子,会在对应的动画或流程真正结束后才兑现;等待它们完成可以保证时序正确。例如:

  • IObjectMoverHooks 的 onMoveStart、onMoveEnd、onStepStart、onStepEnd;
  • ICombatFlowHooks 的 onBeforeCombat、onAfterCombat;
  • IHeroStateHooks 的 onBeforeChangeFloor、onAfterChangeFloor;
  • IMapLayerHooks 的 onOpenDoor、onCloseDoor、onDeleteDynamic;
  • IReplaySandboxHooks 的 onStep。

其余钩子均为同步钩子,触发时立即执行。

一个提醒:钩子可能被非本对象触发

钩子由所属主对象触发,但同一个主对象可能对应多个游戏对象(例如场上有多只怪物)。因此钩子被触发时,不一定是针对你关心的那一只;当场上存在多个同类主对象时,需要在回调里根据传入的参数判断是否为你的目标对象。一般情况下不会串。

钩子建立在数据端的主对象之上,两端分工见 数据端与渲染端;编写钩子所需的语法见 代码基础。