Skip to content

# 事件触发系统 ​

事件触发系统是一个可以监测游戏运行时的系统，通过这个系统，你可以在游戏运行的特定时刻执行一些函数，而因此可以大大减少复写的量，代码也会更加清晰。

事件触发系统依赖于两个类 `EventEmitter` 和 `IndexedEventEmitter`，这两个类在游戏进程和渲染进程都可以直接使用。本页面将会着重介绍这两个类的使用方法。

样板内置的所有包含事件触发功能的内容都基于 `EventEmitter` 类，而不基于 `IndexedEventEmitter`。

## 监听事件 ​

通过 `on` 函数监听事件：

ts

    function on(event: string, fn: (...params: any) => any, config?: any): void

其中 `event` 参数表示要监听的事件类型，`fn` 是这个事件被触发时执行的函数，接受任意数量的参数，这取决于触发事件时传入了哪些参数。也可以有返回值，返回值可以被触发函数获取到。

例如，我想监听 UI 控制器的 `end` 事件，就可以这么写：

js

    const { mainUi } = Mota.requireAll('var');

    // 监听的 end 事件无参数
    mainUi.on('end', () => {
        console.log('event emitted!');
    });

这时，如果 UI 控制器触发了 `end` 事件，那么传入的函数就会被执行，也就会在控制台打印 `event emitted!`。对于有参数的事件，可以这么写：

js

    // 监听 splice 事件，并在控制台打印所有被关闭的 UI 名称
    mainUi.on('splice', (items) => {
        console.log(items.map(v => v.id));
    });

对于 `IndexedEventEmitter`，还拥有 `onIndex` 函数，可以为这次监听的函数指定一个 id，从而在取消监听时可以通过这个 id 删除，不再需要传入原函数的引用。值得注意的是，样板内置的所有拥有事件触发功能的内容都基于 `EventEmitter`，也就是说它们不能使用 `onIndex` 函数。

ts

    function onIndex(
        event: string,
        symbol: string | number | symbol,
        fn: (...params: any) => any,
        config?: any
    ): void

这里的 `symbol` 便是指定的 `id`

js

    eventEmitter.onIndex('end', 'myListener', () => {
        console.log('emitted!');
    });

## 取消监听 ​

可以通过 `off` 函数来取消监听：

ts

    function off(event: string, fn: (...params: any) => any): void

示例

js

    const fn = () => console.log('event emitted!');

    mainUi.on('end', fn);

    // 取消监听，注意要求函数引用相同，内容一样是没用的！
    mainUi.off('end', fn);

对于 `IndexedEventEmitter`，还可以通过 `offIndex` 来取消监听：

js

    // 传入监听时指定的 id 即可，不需要传入函数
    eventEmitter.offIndex('end', 'myListener');

除此之外，对于两种事件触发器，都还可以通过 `removeAllListeners` 来取消监听所有事件，或者是一个事件的所有内容

ts

    function removeAllListeners(event?: string): void

当不传入 `event` 时，会将所有事件的所有监听器全部取消，这时所有的监听器都将失效。当传入 `event` 时，会将指定事件的所有监听器全部取消。

js

    // 取消监听 mainUi 上的所有内容
    mainUi.removeAllListeners();
    // 取消监听 mainUi 上的 end 事件
    mainUi.removeAllListeners('end');

WARNING

不要对样板内置变量执行 `removeAllListeners` 函数，因为样板内置内容很多都基于事件触发。

## 触发事件 ​

可以通过 `emit` 函数触发事件：

ts

    function emit(event: string, ...params: any): any[]

这个函数的 `event` 参数表示的是要触发哪个事件，后面的参数表示的是要传递给监听器的参数。其返回值是每个监听器的返回值组成的数组，顺序不确定。除此之外，如果设置了自定义触发器，那么返回值也会跟随自定义触发器而改变。

例如，如果我想触发 `mainUi` 的 `end` 事件：

js

    mainUi.emit('end'); // 触发事件，无参数
    mainUi.emit('end', 123, '123'); // 触发事件，有两个参数传入监听器，监听器可以通过参数获取

## 监听配置 ​

对于 `on` 函数以及 `onIndex` 函数的最后一个参数，表示的是这个监听器的配置选项。在目前，它包含两个选项：

- `once`: 表示这个监听器只会触发一次，触发一次后即自动删除
- `immediate`: 当这个监听器被添加的时候，会立刻被触发一次，不传入任何参数

对于 `once` 配置项，还可以通过 `once` 函数来设置：

ts

    function once(event: string, fn: (...params: any) => any): void
    function onceIndex(event: string, symbol: string | number | symbol, fn: (...params: any) => any): void

这样，我们就可以通过 `once` 函数来添加一个只会触发一次的监听器了：

js

    mainUi.once('end', () => {
        console.log('这个事件只会触发一次');
    });

## 自定义触发器 ​

你还可以通过 `setEmitter` 函数来自定义你的事件触发器：

ts

    function setEmitter(event: string, fn?: (event: any[], ...params: any) => any): void

其中 `event` 参数表示的是要设置的时间，`fn` 参数表示的是触发器函数，它接收事件信息作为第一个参数，之后的参数是事件触发时传入的参数。

示例

js

    eventEmitter.setEmitter('myEmitter', (event, ...params) => {
        const returns = [];
        for (const ev of event) {
            // 修改触发器的返回值，在后面加上'-myEmitter'字符串
            returns.push(ev.fn(...params) + '-myEmitter');
        }
        return returns;
    })

## 样板中基于事件触发器的接口 ​

类

- `AudioPlayer`
- `Disposable`
- `IndexedEventEmitter`
- `ShaderEffect`
- `ResourceController`
- `MotaSetting`
- `SettingDisplayer`
- `Hotkey`
- `Keyboard`
- `CustomToolbar`
- `Focus`
- `GameUi`

变量

- `mainUi`
- `fixedUi`
- `bgm`
- `sound`
- `gameKey`
- `mainSetting`
- `hook`
- `gameListener`
- `loading`