web-input-system 0.0.0-stage → 0.4.0
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/LICENSE +21 -0
- package/README.md +109 -2
- package/README.zh-CN.md +108 -0
- package/dist/action.d.ts +43 -0
- package/dist/action.d.ts.map +1 -0
- package/dist/actionMap.d.ts +21 -0
- package/dist/actionMap.d.ts.map +1 -0
- package/dist/asset.d.ts +72 -0
- package/dist/asset.d.ts.map +1 -0
- package/dist/composites.d.ts +35 -0
- package/dist/composites.d.ts.map +1 -0
- package/dist/index.cjs +936 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +885 -0
- package/dist/index.js.map +1 -0
- package/dist/interactions.d.ts +49 -0
- package/dist/interactions.d.ts.map +1 -0
- package/dist/manager.d.ts +134 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/processors.d.ts +29 -0
- package/dist/processors.d.ts.map +1 -0
- package/dist/types.d.ts +44 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +46 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 林贤庆 (Xianqing Lin)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,110 @@
|
|
|
1
|
-
#
|
|
1
|
+
# web-input-system
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[中文文档](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
A lightweight, Unity-style input system for browser games: describe all input in one
|
|
6
|
+
**declarative asset** (action maps + actions + bindings), then consume typed action
|
|
7
|
+
objects via callbacks or polling. Zero dependencies, engine-agnostic (works with
|
|
8
|
+
Three.js, Phaser, Babylon, Canvas2D, or plain DOM).
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Declarative input asset** — one `defineInputAsset` table holds every map/action/binding; action names and value types are fully inferred.
|
|
13
|
+
- **Three-phase callbacks** — `started` / `performed` / `canceled`, Unity-style; subscriptions return an unsubscribe function.
|
|
14
|
+
- **Polling channel** — `readValue()` (`boolean` / `number` / `{x,y}`) and `isPressed()`; inactive maps report default values.
|
|
15
|
+
- **Composite bindings** — `axis1D` (A/D → axis), `vector2D` (WASD → vector, diagonals normalized).
|
|
16
|
+
- **Interactions** — `hold` / `tap` / `doubleTap` decide *when* a button action fires.
|
|
17
|
+
- **Processors** — `scale` / `invert` / `invertX` / `invertY` / `deadzone` / `clamp` / `normalize` transform values after evaluation (polling and callbacks see the same result).
|
|
18
|
+
- **Action maps** — explicit `enable()/disable()` plus declarative `active(ctx)` predicates; deactivating a map fires `canceled` on its actuated actions (no stuck state).
|
|
19
|
+
- **Mouse controls** — built-in paths `<Mouse>/delta`, `<Mouse>/wheelY`, `<Mouse>/leftButton`, etc., with optional pointer-lock gating.
|
|
20
|
+
- **Browser robustness built in** — blur/tab-switch clears held keys and fires `canceled`, OS auto-repeat (`e.repeat`) filtered, form-field typing ignored, `preventDefault` only for bound keys.
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { createInput, defineInputAsset, vector2D, scale, MOUSE_DELTA } from 'web-input-system';
|
|
26
|
+
|
|
27
|
+
interface Ctx { isPaused: boolean }
|
|
28
|
+
|
|
29
|
+
// 1. The input asset: single source of truth for maps/actions/bindings.
|
|
30
|
+
// (Annotate predicate parameters — the context type is inferred from them.)
|
|
31
|
+
const asset = defineInputAsset({
|
|
32
|
+
maps: {
|
|
33
|
+
gameplay: {
|
|
34
|
+
active: (ctx: Ctx) => !ctx.isPaused,
|
|
35
|
+
actions: {
|
|
36
|
+
move: { type: 'value', value: 'vector2', bindings: [
|
|
37
|
+
vector2D({ up: 'KeyW', down: 'KeyS', left: 'KeyA', right: 'KeyD' }),
|
|
38
|
+
]},
|
|
39
|
+
jump: { type: 'button', bindings: ['Space'] },
|
|
40
|
+
look: { type: 'value', value: 'vector2', bindings: [MOUSE_DELTA], processors: [scale(0.002)] },
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
ui: {
|
|
44
|
+
actions: {
|
|
45
|
+
pause: { type: 'button', bindings: ['Escape'] },
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// 2. Create the manager (context sampled live; setContext also available).
|
|
52
|
+
const input = createInput(asset, { context: () => ({ isPaused: store.paused }) });
|
|
53
|
+
|
|
54
|
+
// 3. Subscribe (returns an unsubscribe function).
|
|
55
|
+
input.action('ui', 'pause').performed(() => togglePause());
|
|
56
|
+
input.action('gameplay', 'jump').canceled(() => console.log('jump released'));
|
|
57
|
+
|
|
58
|
+
// 4. Frame loop: poll continuous values, reset deltas at end of frame.
|
|
59
|
+
function frame(dt: number) {
|
|
60
|
+
const dir = input.action('gameplay', 'move').readValue(); // { x, y }, normalized
|
|
61
|
+
player.move(dir.x, dir.y);
|
|
62
|
+
input.update(dt);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Semantics worth knowing
|
|
67
|
+
|
|
68
|
+
- **Delta controls** (`<Mouse>/delta`, `<Mouse>/wheelY`): callback `ctx.value` is the
|
|
69
|
+
*per-event* delta; `readValue()` is the *per-frame accumulation*, reset by
|
|
70
|
+
`input.update(dt)`. They dispatch `performed` only — no `started`/`canceled`.
|
|
71
|
+
- **Discrete controls** (keyboard / mouse buttons): `started`+`performed` on press,
|
|
72
|
+
`canceled` on release, blur, or map deactivation.
|
|
73
|
+
- **Interactions** (button actions): `hold` performs only after being held long enough
|
|
74
|
+
(early release only cancels); `tap` performs on a quick press+release; `doubleTap`
|
|
75
|
+
needs two quick taps within the interval. Timers advance in `input.update(dt)`.
|
|
76
|
+
One interaction per action.
|
|
77
|
+
- **Processors** (value actions): applied in order after evaluation; each processor
|
|
78
|
+
declares the value types it handles and passes others through untouched.
|
|
79
|
+
- **Context**: prefer the live `context: () => Ctx` sampler (never stale);
|
|
80
|
+
`input.setContext(ctx)` is the push-style alternative.
|
|
81
|
+
- **SSR-safe**: no `window` access at module scope; without a DOM the manager is
|
|
82
|
+
constructed detached.
|
|
83
|
+
|
|
84
|
+
## Runtime rebinding
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// Capture flow: the next key/mouse-button press becomes the new binding
|
|
88
|
+
// (Escape cancels by default; blur cancels; the returned function cancels manually).
|
|
89
|
+
input.startRebind('gameplay', 'jump', {
|
|
90
|
+
onComplete: (path) => console.log('jump is now on', path),
|
|
91
|
+
onCancel: () => console.log('rebind cancelled'),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Persistence is the host's job — overrides are plain JSON.
|
|
95
|
+
localStorage.setItem('bindings', JSON.stringify(input.exportOverrides()));
|
|
96
|
+
input.applyOverrides(JSON.parse(localStorage.getItem('bindings') ?? '{}'));
|
|
97
|
+
|
|
98
|
+
// Conflict detection before accepting a binding.
|
|
99
|
+
input.findActionsByControl('Space'); // → [{ map: 'gameplay', action: 'jump' }]
|
|
100
|
+
|
|
101
|
+
// Composite parts (e.g. "move up"): read, edit, write back.
|
|
102
|
+
const [wasd] = input.getBindings('movement', 'move');
|
|
103
|
+
input.setBindings('movement', 'move', [vector2D({ ...wasd, up: 'ArrowUp' })]);
|
|
104
|
+
|
|
105
|
+
input.resetBindings(); // restore asset defaults
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# web-input-system
|
|
2
|
+
|
|
3
|
+
[English](./README.md)
|
|
4
|
+
|
|
5
|
+
Unity Input System 的浏览器轻量版:用一份**声明式输入资产**(动作地图 + 动作 + 绑定)
|
|
6
|
+
描述全部输入,运行时得到类型安全的动作对象——回调订阅与轮询双通道,零依赖、
|
|
7
|
+
引擎无关(不绑定 Three.js / Phaser / React)。
|
|
8
|
+
|
|
9
|
+
## 特性
|
|
10
|
+
|
|
11
|
+
- **声明式输入资产**:`defineInputAsset` 一张表声明地图/动作/绑定,动作名与值类型全推导;
|
|
12
|
+
- **三阶段回调**:`started` / `performed` / `canceled`(Unity 同款生命周期),订阅返回退订函数;
|
|
13
|
+
- **轮询双通道**:`readValue()`(boolean / number / Vector2)与 `isPressed()`,地图未激活时返回默认值;
|
|
14
|
+
- **复合绑定**:`axis1D`(A/D 合成轴)、`vector2D`(WASD 合成向量,对角线自动归一化);
|
|
15
|
+
- **Interactions**:`hold`(长按)/ `tap`(点按)/ `doubleTap`(双击)决定 button 动作何时触发;
|
|
16
|
+
- **Processors**:`scale` / `invert` / `invertX` / `invertY` / `deadzone` / `clamp` / `normalize`
|
|
17
|
+
链式加工 value 动作的值(轮询与回调拿到同一加工结果);
|
|
18
|
+
- **动作地图**:`enable()/disable()` 显式开关 + `active` 谓词声明式门控,二者并存;
|
|
19
|
+
地图失活自动给激活中的动作补发 `canceled`(状态不卡死);
|
|
20
|
+
- **鼠标控件**:`<Mouse>/delta`、`<Mouse>/wheelY`、`<Mouse>/leftButton` 等内置路径,
|
|
21
|
+
可选指针锁定门控(`gateMouseByPointerLock`);
|
|
22
|
+
- **健壮性**:窗口失焦/切后台清空按键并补发 `canceled`、过滤 OS 按住连发(`e.repeat`)、
|
|
23
|
+
输入框/文本域打字豁免、`preventDefault` 仅针对绑定键。
|
|
24
|
+
|
|
25
|
+
## 快速上手
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { createInput, defineInputAsset, vector2D, MOUSE_DELTA } from 'web-input-system';
|
|
29
|
+
|
|
30
|
+
interface Ctx { isPaused: boolean }
|
|
31
|
+
|
|
32
|
+
// 1. 输入资产:地图/动作/绑定的唯一出处(谓词参数标注类型,Ctx 由此推导)
|
|
33
|
+
const asset = defineInputAsset({
|
|
34
|
+
maps: {
|
|
35
|
+
gameplay: {
|
|
36
|
+
active: (ctx: Ctx) => !ctx.isPaused,
|
|
37
|
+
actions: {
|
|
38
|
+
move: { type: 'value', value: 'vector2', bindings: [
|
|
39
|
+
vector2D({ up: 'KeyW', down: 'KeyS', left: 'KeyA', right: 'KeyD' }),
|
|
40
|
+
]},
|
|
41
|
+
jump: { type: 'button', bindings: ['Space'] },
|
|
42
|
+
look: { type: 'value', value: 'vector2', bindings: [MOUSE_DELTA] },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
ui: {
|
|
46
|
+
actions: {
|
|
47
|
+
pause: { type: 'button', bindings: ['Escape'] },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// 2. 创建管理器(上下文实时采样;也可 setContext 推送)
|
|
54
|
+
const input = createInput(asset, { context: () => ({ isPaused: store.paused }) });
|
|
55
|
+
|
|
56
|
+
// 3. 回调订阅(返回退订函数)
|
|
57
|
+
input.action('ui', 'pause').performed(() => togglePause());
|
|
58
|
+
input.action('gameplay', 'jump').canceled(() => console.log('jump released'));
|
|
59
|
+
|
|
60
|
+
// 4. 帧循环:轮询持续值,帧末清零增量
|
|
61
|
+
function frame(dt: number) {
|
|
62
|
+
const dir = input.action('gameplay', 'move').readValue(); // { x, y },已归一化
|
|
63
|
+
player.move(dir.x, dir.y);
|
|
64
|
+
input.update(dt);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 设计约定
|
|
69
|
+
|
|
70
|
+
- **Interactions(button 动作)**:`interactions: [hold(0.5)]` 决定何时触发。
|
|
71
|
+
默认(即 `press()`)按下即 `started`+`performed`、松开 `canceled`;`hold` 按住满时长才
|
|
72
|
+
`performed`、提前松开只 `canceled`;`tap` 快速点按才 `performed`;`doubleTap` 间隔内两次
|
|
73
|
+
点按才 `performed`。计时依赖每帧调用 `input.update(dt)`;每个动作最多一个 interaction。
|
|
74
|
+
- **Processors(value 动作)**:`processors: [scale(0.1), invertY()]` 在求值后按序加工,
|
|
75
|
+
轮询与回调拿到同一结果。每个 processor 声明自己处理的值类型(axis 走 `number`,
|
|
76
|
+
vector2 走 `vector2`),不匹配的类型原样透传。
|
|
77
|
+
|
|
78
|
+
- **增量控件**(`<Mouse>/delta`、`MOUSE_WHEEL_Y`):回调里 `ctx.value` 是**本次事件**的增量;
|
|
79
|
+
`readValue()` 轮询是**本帧累计**,`input.update(dt)` 清零。只派发 `performed`,
|
|
80
|
+
不维护 `started`/`canceled`。
|
|
81
|
+
- **离散控件**(键盘/鼠标按键):`started`+`performed` 在按下时派发,`canceled` 在松开、
|
|
82
|
+
失焦或所属地图失活时派发。
|
|
83
|
+
- **上下文**:构造选项 `context: () => Ctx` 实时采样(推荐,无陈旧状态),
|
|
84
|
+
或 `input.setContext(ctx)` 推送;二者采样器优先。
|
|
85
|
+
- **SSR 安全**:模块顶层不触碰 `window`;无 DOM 环境可构造但处于未接线状态。
|
|
86
|
+
|
|
87
|
+
## 运行时重绑定
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
// 捕获流程:下一次按键/鼠标点击成为新绑定(默认 Escape 取消;失焦取消;返回函数手动取消)
|
|
91
|
+
input.startRebind('gameplay', 'jump', {
|
|
92
|
+
onComplete: (path) => console.log('jump 现在绑定到', path),
|
|
93
|
+
onCancel: () => console.log('已取消'),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// 覆盖表是纯 JSON,持久化由宿主负责
|
|
97
|
+
localStorage.setItem('bindings', JSON.stringify(input.exportOverrides()));
|
|
98
|
+
input.applyOverrides(JSON.parse(localStorage.getItem('bindings') ?? '{}'));
|
|
99
|
+
|
|
100
|
+
// 接受绑定前的冲突检测
|
|
101
|
+
input.findActionsByControl('Space'); // → [{ map: 'gameplay', action: 'jump' }]
|
|
102
|
+
|
|
103
|
+
// 复合绑定部件(如"向前移动"):读出、改一个部件、写回
|
|
104
|
+
const [wasd] = input.getBindings('movement', 'move');
|
|
105
|
+
input.setBindings('movement', 'move', [vector2D({ ...wasd, up: 'ArrowUp' })]);
|
|
106
|
+
|
|
107
|
+
input.resetBindings(); // 恢复资产默认
|
|
108
|
+
```
|
package/dist/action.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InputAction: a single action, created by InputManager from the asset.
|
|
3
|
+
*
|
|
4
|
+
* Two ways to consume:
|
|
5
|
+
* - subscribe: performed / started / canceled (each returns an unsubscribe fn)
|
|
6
|
+
* - poll: readValue() (current value), isPressed() (currently actuated)
|
|
7
|
+
*
|
|
8
|
+
* Lifecycle for discrete controls:
|
|
9
|
+
* press → started → performed (re-fires on value change) → release → canceled
|
|
10
|
+
* Delta controls (mouse motion, wheel) only dispatch performed.
|
|
11
|
+
*/
|
|
12
|
+
import type { ActionCallback, ActionPhase } from './types';
|
|
13
|
+
/** Runtime hooks injected by InputManager (avoids a reverse type dependency). */
|
|
14
|
+
export interface ActionRuntime<T> {
|
|
15
|
+
readValue: () => T;
|
|
16
|
+
isPressed: () => boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare class InputAction<T = boolean> {
|
|
19
|
+
/** Action name in 'map.action' form, for logging and debugging. */
|
|
20
|
+
readonly name: string;
|
|
21
|
+
/** Current phase ('waiting' = never triggered or fully reset). */
|
|
22
|
+
get phase(): ActionPhase;
|
|
23
|
+
private _phase;
|
|
24
|
+
private readonly runtime;
|
|
25
|
+
private readonly listeners;
|
|
26
|
+
constructor(name: string, runtime: ActionRuntime<T>);
|
|
27
|
+
/** Subscribe to performed (value produced/changed). Returns an unsubscribe fn. */
|
|
28
|
+
performed(cb: ActionCallback<T>): () => void;
|
|
29
|
+
/** Subscribe to started (first frame leaving the default value). Returns an unsubscribe fn. */
|
|
30
|
+
started(cb: ActionCallback<T>): () => void;
|
|
31
|
+
/** Subscribe to canceled (release / back to default / map deactivated). Returns an unsubscribe fn. */
|
|
32
|
+
canceled(cb: ActionCallback<T>): () => void;
|
|
33
|
+
private subscribe;
|
|
34
|
+
/** Poll the current value (type default — false / 0 / {0,0} — while the map is inactive). */
|
|
35
|
+
readValue(): T;
|
|
36
|
+
/** Whether the action is currently actuated (button: held; value: non-default). */
|
|
37
|
+
isPressed(): boolean;
|
|
38
|
+
/** [internal] Called by the manager during dispatch. */
|
|
39
|
+
_emit(phase: 'started' | 'performed' | 'canceled', value: T): void;
|
|
40
|
+
/** [internal] Reset phase without dispatching (used by dispose). */
|
|
41
|
+
_reset(): void;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=action.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyB,WAAW,EAAE,MAAM,SAAS,CAAC;AAElF,iFAAiF;AACjF,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC,CAAC;IACnB,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AAED,qBAAa,WAAW,CAAC,CAAC,GAAG,OAAO;IAClC,mEAAmE;IACnE,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,IAAW,KAAK,IAAI,WAAW,CAE9B;IAED,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAIxB;gBAEU,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IAKnD,kFAAkF;IAC3E,SAAS,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAInD,+FAA+F;IACxF,OAAO,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIjD,sGAAsG;IAC/F,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIlD,OAAO,CAAC,SAAS;IAMjB,6FAA6F;IACtF,SAAS,IAAI,CAAC;IAIrB,mFAAmF;IAC5E,SAAS,IAAI,OAAO;IAI3B,wDAAwD;IACjD,KAAK,CAAC,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,UAAU,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAWzE,oEAAoE;IAC7D,MAAM,IAAI,IAAI;CAGtB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InputActionMap: a named group of actions with a shared activation condition.
|
|
3
|
+
*
|
|
4
|
+
* A map is effectively active when: enabled && (active?.(ctx) ?? true).
|
|
5
|
+
* - enable()/disable(): explicit runtime switch
|
|
6
|
+
* - the asset's `active` predicate: declarative context gating
|
|
7
|
+
*
|
|
8
|
+
* When a map deactivates, its actuated actions receive `canceled` (no stuck
|
|
9
|
+
* state); when it reactivates, still-held controls re-fire started+performed.
|
|
10
|
+
*/
|
|
11
|
+
export declare class InputActionMap {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
/** [internal] Notifies the manager to re-evaluate dispatch on toggle. */
|
|
14
|
+
private readonly onToggle;
|
|
15
|
+
private _enabled;
|
|
16
|
+
constructor(name: string, onToggle: () => void);
|
|
17
|
+
get enabled(): boolean;
|
|
18
|
+
enable(): void;
|
|
19
|
+
disable(): void;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=actionMap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actionMap.d.ts","sourceRoot":"","sources":["../src/actionMap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,qBAAa,cAAc;IACzB,SAAgB,IAAI,EAAE,MAAM,CAAC;IAE7B,yEAAyE;IACzE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAa;IACtC,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI;IAK9C,IAAW,OAAO,IAAI,OAAO,CAE5B;IAEM,MAAM,IAAI,IAAI;IAMd,OAAO,IAAI,IAAI;CAKvB"}
|
package/dist/asset.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input asset definition: the single source of truth for maps, actions, bindings.
|
|
3
|
+
*
|
|
4
|
+
* An asset is plain data (processors hold functions, everything else is
|
|
5
|
+
* JSON-serializable); defineInputAsset only carries types. Map names, action
|
|
6
|
+
* names and per-action value types are all inferred from the object literal,
|
|
7
|
+
* so input.action('movement', 'move') returns a precise type.
|
|
8
|
+
*/
|
|
9
|
+
import type { CompositeBinding } from './composites';
|
|
10
|
+
import type { InteractionDef } from './interactions';
|
|
11
|
+
import type { Processor } from './processors';
|
|
12
|
+
import type { ActionValueType, ControlPath, ValueOfType } from './types';
|
|
13
|
+
/** A single control path, or a composite combining several controls into one value. */
|
|
14
|
+
export type Binding = ControlPath | CompositeBinding;
|
|
15
|
+
export interface ButtonActionDef {
|
|
16
|
+
/** button: press/release semantics; started+performed on press, canceled on release. */
|
|
17
|
+
readonly type: 'button';
|
|
18
|
+
readonly bindings?: readonly Binding[];
|
|
19
|
+
/**
|
|
20
|
+
* Trigger conditions (hold / tap / doubleTap). Default (= press()) fires on
|
|
21
|
+
* press. At most one interaction per action.
|
|
22
|
+
*/
|
|
23
|
+
readonly interactions?: readonly InteractionDef[];
|
|
24
|
+
}
|
|
25
|
+
export interface ValueActionDef {
|
|
26
|
+
/** value: continuous semantics; poll via readValue(), performed fires on value change. */
|
|
27
|
+
readonly type: 'value';
|
|
28
|
+
readonly value: ActionValueType;
|
|
29
|
+
readonly bindings?: readonly Binding[];
|
|
30
|
+
/** Value transforms, applied in order after evaluation (both polling and callbacks). */
|
|
31
|
+
readonly processors?: readonly Processor[];
|
|
32
|
+
}
|
|
33
|
+
export type ActionDef = ButtonActionDef | ValueActionDef;
|
|
34
|
+
export interface ActionMapDef<Ctx = unknown> {
|
|
35
|
+
/**
|
|
36
|
+
* Activation predicate: false takes the whole map out of dispatch (default: always
|
|
37
|
+
* active). Coexists with the enabled flag: effective = enabled && active(ctx).
|
|
38
|
+
*/
|
|
39
|
+
readonly active?: (ctx: Ctx) => boolean;
|
|
40
|
+
readonly actions: Record<string, ActionDef>;
|
|
41
|
+
}
|
|
42
|
+
export interface InputAssetDef<Ctx = unknown> {
|
|
43
|
+
readonly maps: Record<string, ActionMapDef<Ctx>>;
|
|
44
|
+
}
|
|
45
|
+
/** Opaque handle returned by defineInputAsset, carrying the def and context types. */
|
|
46
|
+
export interface InputAsset<Ctx, Def extends InputAssetDef<Ctx>> {
|
|
47
|
+
readonly def: Def;
|
|
48
|
+
}
|
|
49
|
+
/** Context type inferred from the `active` predicate parameter annotations. */
|
|
50
|
+
type CtxOf<Def> = Def extends InputAssetDef<infer C> ? C : unknown;
|
|
51
|
+
/**
|
|
52
|
+
* Define an input asset (returns the def as-is; only carries types).
|
|
53
|
+
*
|
|
54
|
+
* Annotate predicate parameters with your context type so Ctx can be inferred:
|
|
55
|
+
* defineInputAsset({ maps: { move: { active: (ctx: MyCtx) => ctx.playing, actions: {...} } } })
|
|
56
|
+
*/
|
|
57
|
+
export declare function defineInputAsset<const Def extends InputAssetDef<any>>(def: Def): InputAsset<CtxOf<Def>, Def>;
|
|
58
|
+
/** JS value type of an action definition. */
|
|
59
|
+
export type ActionValueOf<D> = D extends {
|
|
60
|
+
readonly type: 'button';
|
|
61
|
+
} ? boolean : D extends {
|
|
62
|
+
readonly type: 'value';
|
|
63
|
+
readonly value: infer V extends ActionValueType;
|
|
64
|
+
} ? ValueOfType<V> : never;
|
|
65
|
+
/** Asset definition → per-action value types (drives InputManager.action's return type). */
|
|
66
|
+
export type AssetActionValues<Def extends InputAssetDef<any>> = {
|
|
67
|
+
[M in keyof Def['maps']]: {
|
|
68
|
+
[A in keyof Def['maps'][M]['actions']]: ActionValueOf<Def['maps'][M]['actions'][A]>;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=asset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset.d.ts","sourceRoot":"","sources":["../src/asset.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEzE,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,wFAAwF;IACxF,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CACnD;AAED,MAAM,WAAW,cAAc;IAC7B,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IACvC,wFAAwF;IACxF,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;CAC5C;AAED,MAAM,MAAM,SAAS,GAAG,eAAe,GAAG,cAAc,CAAC;AAEzD,MAAM,WAAW,YAAY,CAAC,GAAG,GAAG,OAAO;IACzC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,aAAa,CAAC,GAAG,GAAG,OAAO;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;CAClD;AAED,sFAAsF;AACtF,MAAM,WAAW,UAAU,CAAC,GAAG,EAAE,GAAG,SAAS,aAAa,CAAC,GAAG,CAAC;IAC7D,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;CACnB;AAED,+EAA+E;AAC/E,KAAK,KAAK,CAAC,GAAG,IAAI,GAAG,SAAS,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;AAEnE;;;;;GAKG;AAEH,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,GAAG,SAAS,aAAa,CAAC,GAAG,CAAC,EACnE,GAAG,EAAE,GAAG,GACP,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAE7B;AAED,6CAA6C;AAC7C,MAAM,MAAM,aAAa,CAAC,CAAC,IACzB,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAAG,OAAO,GAC3C,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,eAAe,CAAA;CAAE,GAAG,WAAW,CAAC,CAAC,CAAC,GACpG,KAAK,CAAC;AAEd,4FAA4F;AAE5F,MAAM,MAAM,iBAAiB,CAAC,GAAG,SAAS,aAAa,CAAC,GAAG,CAAC,IAAI;KAC7D,CAAC,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC,GAAG;SACvB,CAAC,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;KACpF;CACF,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composite bindings: combine several controls into a single value.
|
|
3
|
+
*
|
|
4
|
+
* - axis1D: negative/positive pair → [-1, 1] (e.g. A/D)
|
|
5
|
+
* - vector2D: four directions → 2D vector (e.g. WASD), normalized by default
|
|
6
|
+
*/
|
|
7
|
+
import type { ControlPath } from './types';
|
|
8
|
+
export interface Axis1DBinding {
|
|
9
|
+
readonly kind: 'axis1d';
|
|
10
|
+
readonly negative: ControlPath;
|
|
11
|
+
readonly positive: ControlPath;
|
|
12
|
+
}
|
|
13
|
+
export interface Vector2DBinding {
|
|
14
|
+
readonly kind: 'vector2d';
|
|
15
|
+
readonly up: ControlPath;
|
|
16
|
+
readonly down: ControlPath;
|
|
17
|
+
readonly left: ControlPath;
|
|
18
|
+
readonly right: ControlPath;
|
|
19
|
+
/** Normalize diagonals (default true; false allows length up to √2). */
|
|
20
|
+
readonly normalize: boolean;
|
|
21
|
+
}
|
|
22
|
+
export type CompositeBinding = Axis1DBinding | Vector2DBinding;
|
|
23
|
+
export declare function axis1D(opts: {
|
|
24
|
+
negative: ControlPath;
|
|
25
|
+
positive: ControlPath;
|
|
26
|
+
}): Axis1DBinding;
|
|
27
|
+
export declare function vector2D(opts: {
|
|
28
|
+
up: ControlPath;
|
|
29
|
+
down: ControlPath;
|
|
30
|
+
left: ControlPath;
|
|
31
|
+
right: ControlPath;
|
|
32
|
+
normalize?: boolean;
|
|
33
|
+
}): Vector2DBinding;
|
|
34
|
+
export declare function isComposite(binding: unknown): binding is CompositeBinding;
|
|
35
|
+
//# sourceMappingURL=composites.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composites.d.ts","sourceRoot":"","sources":["../src/composites.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,WAAW,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,eAAe,CAAC;AAE/D,wBAAgB,MAAM,CAAC,IAAI,EAAE;IAAE,QAAQ,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,WAAW,CAAA;CAAE,GAAG,aAAa,CAE5F;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAC7B,EAAE,EAAE,WAAW,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,eAAe,CAElB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,gBAAgB,CAMzE"}
|