template/docs/intermediate/events.md
unanmed 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

7.3 KiB
Raw Blame History

自定义事件

在地图事件里,你能调用一批内建动作——设置图块、移动勇士、往事件里再插一段……这些都是引擎内置的事件函数。内置事件集已经相当完整(发布前会提供约 100 个,覆盖地图、玩家、事件、流程控制等常用动作);当你需要其中没有的额外动作时,也能照着同样的方式加一个自己的事件函数,让事件语句调用到它。

本篇只讲「如何自定义新的事件函数」,不展开事件系统的原理。事件环境里会用到数据端主对象 state,它的获取方式与含义见数据端总览。

内建函数(BuiltInFunction)

事件里能调用的每一个动作都叫内建函数,接口为 BuiltInFunction(来自 @motajs/anon-tokyo——引擎里负责把事件脚本解释执行的包,其归属层级见 src 导览 的「包从哪来」):

interface BuiltInFunction<P, E> {
    name: string;
    save?: boolean;
    func: (parameters: P, env: E) => any;
}
  • name:事件语句里调用它时使用的名字,需全局唯一;
  • func(param, env):执行逻辑。param 是调用时传入的参数对象,env 是当前事件的环境。

一个内建函数通常写成一个类,name 是它的属性,func 是它的方法。内置的 setBlock、moveHero 等就是这样实现的。

事件环境 env

func 的第二个参数是事件环境 IBlockEventEnv,里面放的是「这次事件在什么情况下被触发」。常用的字段:

字段 说明
state 数据端主对象,一切接口的入口
type 事件类型(普通事件 / 点事件 / 图块事件)
trigger 本次事件的触发器类型
heroLocator 触发事件时勇士的位置
heroFloor 触发事件时勇士所在楼层
triggerLocator 触发者的位置,可能不存在
tile 触发事件的图块,可能不存在
layer 触发事件的图层,可能不存在
map 触发事件的地图,可能不存在
system 事件系统对象

其余字段见 API 参考。多数情况下你只需要 state:通过它能访问勇士、地图、Flag 等(见数据端总览)。

从 env 取图层时要小心它可能不存在,内置的 getPossibleLayer(env) 就是用来兜底的:它依次尝试 env.layer、env.map、env.heroFloor 对应的地图,帮你拿到一个可用的事件图层。

参数接口

每个内建函数都有自己的参数接口,定义在 src/data/event/types.ts。例如:

interface ISetBlockEventParam {
    readonly x: number;
    readonly y: number;
    readonly tile: number | string;
}

自定义事件的参数接口也建议放在这个文件里,与内置参数放在一起;事件函数类本身则放在 src/data/event/ 目录下的文件里——可以新建文件,也可以放进与该功能相关的现有文件(例如图块相关的放进 map.ts)。registrations.ts 通过 import 把这些类引入后再注册。

注册事件

光写内建函数还不够,还要把它注册进事件系统。注册入口是 src/data/event/registrations.ts 的 createEventRegistrations(),它返回一个 BuiltInFunction[]。下面是它的节选(内置事件总数约 100 个,这里只列几个作示例):

export function createEventRegistrations(): BuiltInFunction[] {
    return [
        // 地图控制
        new EventSetBlock(),
        new EventMoveBlock(),
        new EventRemoveBlock(),

        // 玩家控制
        new EventMoveHero(),
        new EventStepHero(),

        // 事件控制
        new EventInsertEvents(),
        new EventInsertEvent()

        // ...其余内置事件(发布前合计约 100 个)
    ];
}

这些内置事件覆盖了地图控制(setBlock / moveBlock / removeBlock 等)、玩家控制(moveHero / stepHero 等)、事件控制(insertEvents / insertEvent 等)以及更多常用动作。要新增一个事件,只需两步:按上一节写好事件函数类,再把它 new 出来加进这个数组。

内置事件导读

内置事件的实现按用途分散在 src/data/event/ 下的若干文件里,例如:

  • map.ts:图块相关。setBlock 取到事件图层后把指定坐标设为某个图块,moveBlock 把图块按步骤移动,removeBlock 删除图块;
  • hero.ts:勇士相关。moveHero 让勇士按给定步骤移动,stepHero 让勇士前进一步;
  • event.ts:事件相关。insertEvents 按 id 临时插入多个已有事件,insertEvent 临时执行一段事件语句。

想照着写新事件,挑一个功能相近的内置事件读它的 func 即可。

示例:设置 Flag

下面这个事件函数把参数里的 Flag 名与值写进 Flag 系统。示例为了紧凑,把参数接口 ISetFlagEventParam 直接写在同一个代码块里;实际编写时按上文「参数接口」一节,把它放进 src/data/event/types.ts,只把事件函数类放到 src/data/event/ 下(示例里的 @motajs/*、@user/* 是包名,含义见 src 导览 的「包从哪来」):

import { BuiltInFunction } from '@motajs/anon-tokyo';
import { IBlockEventEnv } from '@user/data-system';

interface ISetFlagEventParam {
    /** Flag 名 */
    readonly field: string;
    /** 要设置的值 */
    readonly value: number | string | boolean;
}

export class EventSetFlag implements BuiltInFunction<
    ISetFlagEventParam,
    IBlockEventEnv
> {
    name: string = 'setFlag';

    func(param: ISetFlagEventParam, env: IBlockEventEnv) {
        env.state.flags.setFieldValue(param.field, param.value);
    }
}

写好后加进 createEventRegistrations():

export function createEventRegistrations(): BuiltInFunction[] {
    return [
        // ... 原有事件
        new EventSetFlag() // [!code ++]
    ];
}

Flag 的读写接口见 Flag 变量。

示例:勇士回血

再写一个让勇士回复生命值的事件函数。它读取参数里的回复量,通过可修改属性接口加到勇士的 hp 上(参数接口同样应放入 src/data/event/types.ts,这里为紧凑仍内联书写):

import { BuiltInFunction } from '@motajs/anon-tokyo';
import { IBlockEventEnv } from '@user/data-system';

interface IHealHeroEventParam {
    /** 回复的生命值 */
    readonly hp: number;
}

export class EventHealHero implements BuiltInFunction<
    IHealHeroEventParam,
    IBlockEventEnv
> {
    name: string = 'healHero';

    func(param: IHealHeroEventParam, env: IBlockEventEnv) {
        env.state.hero.getModifiableAttribute().add('hp', param.hp);
    }
}

勇士属性的读写方式见勇士属性。

编辑器识别

编辑器若要为自定义事件生成图形化配置,需要先能识别出这个事件的参数结构。目前这一识别方式尚未最终确定。在它确定之前,按本篇的方式编写普通内建函数即可。