@eva/plugin-trigger 2.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Trigger.ts +127 -0
- package/lib/TriggerSystem.ts +6 -0
- package/lib/index.ts +3 -0
- package/lib/types.ts +22 -0
- package/package.json +22 -0
package/lib/Trigger.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Component, decorators } from '@eva/eva.js';
|
|
2
|
+
import { getSignalBus, SignalHandle } from '@eva/plugin-signal-bus';
|
|
3
|
+
import type { TriggerParams, TriggerRule, TriggerAction } from './types';
|
|
4
|
+
|
|
5
|
+
declare const mx: any;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Trigger 组件 — 把信号映射成 DSL 动作。
|
|
9
|
+
*
|
|
10
|
+
* DSL 用法:
|
|
11
|
+
* ```json
|
|
12
|
+
* {
|
|
13
|
+
* "type": "Trigger",
|
|
14
|
+
* "props": {
|
|
15
|
+
* "rules": [
|
|
16
|
+
* { "on": "input:fire:press",
|
|
17
|
+
* "do": [
|
|
18
|
+
* { "type": "emit", "signal": "rocket:spawn" },
|
|
19
|
+
* { "type": "incStore", "key": "shotsFired" }
|
|
20
|
+
* ] },
|
|
21
|
+
* { "on": "rocket:hit",
|
|
22
|
+
* "guard": "ctx.allowedScene === true",
|
|
23
|
+
* "do": [
|
|
24
|
+
* { "type": "incStore", "key": "score" },
|
|
25
|
+
* { "type": "emit", "signal": "monster:hurt" }
|
|
26
|
+
* ] }
|
|
27
|
+
* ]
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* 这是 plugin-state-machine 的"无状态弟弟":只配 input → output,不维护 state。
|
|
33
|
+
* 适合写 80% 的"按钮按下→记分"业务,大幅减少自定义 Component 数量。
|
|
34
|
+
*/
|
|
35
|
+
@decorators.componentObserver({})
|
|
36
|
+
export class Trigger extends Component<TriggerParams> {
|
|
37
|
+
static componentName = 'Trigger';
|
|
38
|
+
|
|
39
|
+
private rules: TriggerRule[] = [];
|
|
40
|
+
private subs: SignalHandle[] = [];
|
|
41
|
+
ctx: Record<string, any> = {};
|
|
42
|
+
|
|
43
|
+
init(params?: TriggerParams) {
|
|
44
|
+
if (!params) return;
|
|
45
|
+
this.rules = params.rules ?? [];
|
|
46
|
+
this.ctx = params.context ?? {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
awake() {
|
|
50
|
+
const bus = getSignalBus();
|
|
51
|
+
for (const rule of this.rules) {
|
|
52
|
+
const h = bus.on(rule.on, (payload: any) => {
|
|
53
|
+
if (rule.guard && !this.evalGuard(rule.guard, payload)) return;
|
|
54
|
+
for (const a of rule.do) this.exec(a, payload);
|
|
55
|
+
});
|
|
56
|
+
this.subs.push(h);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private exec(action: TriggerAction, payload: any) {
|
|
61
|
+
try {
|
|
62
|
+
switch (action.type) {
|
|
63
|
+
case 'emit':
|
|
64
|
+
getSignalBus().emit(action.signal, action.payload ?? payload);
|
|
65
|
+
break;
|
|
66
|
+
case 'setStore':
|
|
67
|
+
if (typeof mx !== 'undefined' && mx?.store?.update) {
|
|
68
|
+
mx.store.update(action.key, () => action.value);
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
case 'incStore':
|
|
72
|
+
if (typeof mx !== 'undefined' && mx?.store?.update) {
|
|
73
|
+
mx.store.update(action.key, (v: number) => (v ?? 0) + (action.delta ?? 1));
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
case 'log':
|
|
77
|
+
// eslint-disable-next-line no-console
|
|
78
|
+
console.log('[trigger]', action.message, payload);
|
|
79
|
+
break;
|
|
80
|
+
case 'callMethod':
|
|
81
|
+
this.callMethod(action.entity, action.component, action.method, action.args ?? []);
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
} catch (err) {
|
|
85
|
+
// eslint-disable-next-line no-console
|
|
86
|
+
console.warn('[plugin-trigger] action failed', action, err);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private callMethod(entity: string, compName: string, method: string, args: any[]) {
|
|
91
|
+
const game: any = (this as any).gameObject?.scene?.game;
|
|
92
|
+
if (!game) return;
|
|
93
|
+
const stack: any[] = [...(game.scene?.gameObjects ?? [])];
|
|
94
|
+
while (stack.length) {
|
|
95
|
+
const go = stack.pop();
|
|
96
|
+
if (!go) continue;
|
|
97
|
+
if (go.name === entity) {
|
|
98
|
+
const comps: any[] = go.components ?? [];
|
|
99
|
+
const c = comps.find((c) => c?.constructor?.componentName === compName);
|
|
100
|
+
if (c && typeof c[method] === 'function') {
|
|
101
|
+
c[method](...args);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (go.transform?.children?.length) {
|
|
106
|
+
for (const ch of go.transform.children) stack.push(ch.gameObject);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private evalGuard(guard: string, payload: any): boolean {
|
|
112
|
+
try {
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
|
|
114
|
+
const fn = new Function('payload', 'ctx', `return (${guard});`);
|
|
115
|
+
return Boolean(fn(payload, this.ctx));
|
|
116
|
+
} catch (err) {
|
|
117
|
+
// eslint-disable-next-line no-console
|
|
118
|
+
console.warn(`[plugin-trigger] bad guard "${guard}":`, err);
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
onDestroy() {
|
|
124
|
+
for (const h of this.subs) h.dispose();
|
|
125
|
+
this.subs = [];
|
|
126
|
+
}
|
|
127
|
+
}
|
package/lib/index.ts
ADDED
package/lib/types.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** 单条 action,DSL 描述时只填 type + 字段 */
|
|
2
|
+
export type TriggerAction =
|
|
3
|
+
| { type: 'emit'; signal: string; payload?: any }
|
|
4
|
+
| { type: 'setStore'; key: string; value: any }
|
|
5
|
+
| { type: 'incStore'; key: string; delta?: number }
|
|
6
|
+
| { type: 'log'; message: string }
|
|
7
|
+
| { type: 'callMethod'; entity: string; component: string; method: string; args?: any[] };
|
|
8
|
+
|
|
9
|
+
export interface TriggerRule {
|
|
10
|
+
/** 监听的信号名 */
|
|
11
|
+
on: string;
|
|
12
|
+
/** 命中后执行的动作列表 */
|
|
13
|
+
do: TriggerAction[];
|
|
14
|
+
/** 可选:JS 表达式;以 (payload, ctx) 为变量,假则跳过 */
|
|
15
|
+
guard?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface TriggerParams {
|
|
19
|
+
rules: TriggerRule[];
|
|
20
|
+
/** ctx 变量,可在 guard 中读 */
|
|
21
|
+
context?: Record<string, any>;
|
|
22
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eva/plugin-trigger",
|
|
3
|
+
"version": "2.1.0-beta.1",
|
|
4
|
+
"description": "Trigger — 信号 → DSL 动作的执行器,DSL 配置 \"on signal\" + \"do actions\",emit/setStore/transitionState/playSound 等。",
|
|
5
|
+
"main": "lib/index.ts",
|
|
6
|
+
"module": "lib/index.ts",
|
|
7
|
+
"types": "lib/index.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"lib"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"eva.js",
|
|
13
|
+
"plugin",
|
|
14
|
+
"trigger",
|
|
15
|
+
"rules"
|
|
16
|
+
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@eva/eva.js": "2.1.0-beta.1",
|
|
20
|
+
"@eva/plugin-signal-bus": "2.1.0-beta.1"
|
|
21
|
+
}
|
|
22
|
+
}
|