pixijs-input-devices 0.3.0 â 0.5.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/README.md +191 -183
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +196 -120
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,47 +1,98 @@
|
|
|
1
|
-
# đšī¸ pixijs-input-devices [](https://github.com/reececomo/pixijs-input-devices/blob/main/LICENSE) [](https://github.com/reececomo/pixijs-input-devices/actions/workflows/tests.yml) [](https://www.npmjs.com/package/pixijs-input-devices) [](https://www.npmjs.com/package/pixijs-input-devices)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
⥠Powerful, high-performance input device management for PixiJS
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
|
|
5
|
+
| | |
|
|
6
|
+
| ------ | ------ |
|
|
7
|
+
| đŽ Handles [keyboards](#keyboarddevice), [gamepads](#gamepaddevice), and [more](#custom-devices)! | đ Flexible [update](#real-time) and [event-driven](#keyboarddevice-events) APIs |
|
|
8
|
+
| ⥠Optimized for [INP performance](https://web.dev/articles/inp) | đĒ Built-in [named binds](#named-binds) |
|
|
9
|
+
| đŽ Highly configurable | đ Built-in [international keyboard](#keyboard-layout---detection) support |
|
|
10
|
+
| â
Cross-platform & mobile-friendly <sup>[[1]](https://caniuse.com/mdn-api_keyboardlayoutmap) [[2]](https://caniuse.com/mdn-api_gamepad_vibrationactuator) [[3]](https://chromestatus.com/feature/5989275208253440)</sup> | đ§ Built-in [UI navigation](#navigation-api) _(optional)_ |
|
|
11
|
+
| đ Zero dependencies & tree-shakeable | ⨠Supports PixiJS v8, v7, v6.3+ |
|
|
9
12
|
|
|
10
|
-
<hr/>
|
|
11
13
|
|
|
12
|
-
##
|
|
14
|
+
## Sample Usage
|
|
15
|
+
|
|
16
|
+
*Handle device inputs with ease.*
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { InputDevice } from "pixijs-input-devices";
|
|
20
|
+
|
|
21
|
+
// Iterative
|
|
22
|
+
let jump = false
|
|
23
|
+
|
|
24
|
+
for (const device of InputDevice.devices) {
|
|
25
|
+
if (device.type === "keyboard" && device.key.Space) jump = true
|
|
26
|
+
if (device.type === "gamepad" && device.button.A) jump = true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Event-driven
|
|
30
|
+
const gamepad = InputDevice.gamepads[0]
|
|
31
|
+
|
|
32
|
+
gamepad?.on("LeftShoulder", (e) => {
|
|
33
|
+
e.device.playVibration({ duration: 100 })
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Getting Started with PixiJS Input Devices
|
|
38
|
+
|
|
39
|
+
*Everything you need to quickly integrate powerful device management.*
|
|
40
|
+
|
|
41
|
+
**PixiJS Input Devices** adds first-class support for input device management and input handling. It also provides an optional navigation manager
|
|
42
|
+
that can enable input devices to traverse pointer-based UIs.
|
|
43
|
+
|
|
44
|
+
The core concepts are:
|
|
45
|
+
|
|
46
|
+
1. **Devices:** _Any human interface device_
|
|
47
|
+
2. **Binds:** _Custom, named input actions that can be triggered by assigned keys or buttons_
|
|
48
|
+
3. **UINavigation:** _A global controller that allows non-pointer devices to navigate UIs_
|
|
49
|
+
|
|
50
|
+
> [!NOTE]
|
|
51
|
+
> _See [UINavigation API](#uinavigation-api) for more information._
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
*Quick start guide.*
|
|
57
|
+
|
|
58
|
+
**1.** Install the latest `pixijs-input-devices` package:
|
|
13
59
|
|
|
14
60
|
```sh
|
|
15
|
-
npm
|
|
61
|
+
# npm
|
|
62
|
+
npm install pixijs-input-devices -D
|
|
63
|
+
|
|
64
|
+
# yarn
|
|
65
|
+
yarn add pixijs-input-devices --dev
|
|
16
66
|
```
|
|
17
67
|
|
|
18
|
-
|
|
68
|
+
**2.** Register the update loop:
|
|
19
69
|
|
|
20
70
|
```ts
|
|
21
|
-
import {
|
|
71
|
+
import { Ticker } from 'pixi.js';
|
|
72
|
+
import { InputDevice } from 'pixijs-input-devices';
|
|
22
73
|
|
|
23
|
-
Ticker.shared.add(
|
|
74
|
+
Ticker.shared.add(ticker => InputDevice.update());
|
|
24
75
|
```
|
|
25
76
|
|
|
26
|
-
|
|
77
|
+
> [!TIP]
|
|
78
|
+
> **Input polling:** In the context of a video game, you may want to put the input update at the start of your game event loop instead.
|
|
79
|
+
|
|
80
|
+
**3.** (Optional) enable the UINavigation API
|
|
27
81
|
|
|
28
82
|
```ts
|
|
29
|
-
import
|
|
83
|
+
import * as PIXI from 'pixi.js';
|
|
84
|
+
import { UINavigation, registerPixiJSNavigationMixin } from 'pixijs-input-devices';
|
|
30
85
|
|
|
31
|
-
|
|
32
|
-
Navigation.stage = app.stage
|
|
86
|
+
const app = new PIXI.Application(/*âĻ*/)
|
|
33
87
|
|
|
34
|
-
//
|
|
35
|
-
|
|
88
|
+
// enable the navigation API
|
|
89
|
+
UINavigation.configureWithRoot( app.stage )
|
|
90
|
+
registerPixiJSNavigationMixin( PIXI.Container )
|
|
36
91
|
```
|
|
37
92
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
There are a few very simple themes:
|
|
93
|
+
⨠You are now ready to use inputs!
|
|
41
94
|
|
|
42
|
-
|
|
43
|
-
- There are three supported device types: â¨ī¸ `"keyboard"`, đŽ `"gamepad"` and đģ `"custom"`
|
|
44
|
-
- Inputs can be accessed directly, or configured by [Named Groups](#named-input-groups)
|
|
95
|
+
## Features
|
|
45
96
|
|
|
46
97
|
### InputDevice Manager
|
|
47
98
|
|
|
@@ -56,7 +107,7 @@ InputDevice.custom // Array<CustomDevice>
|
|
|
56
107
|
You can access all **active/connected** devices using `.devices`:
|
|
57
108
|
|
|
58
109
|
```ts
|
|
59
|
-
for ( const device of InputDevice.devices ) { //
|
|
110
|
+
for ( const device of InputDevice.devices ) { // âĻ
|
|
60
111
|
```
|
|
61
112
|
|
|
62
113
|
#### InputDevice - properties
|
|
@@ -76,18 +127,18 @@ for ( const device of InputDevice.devices ) { // ...
|
|
|
76
127
|
Access global events directly through the manager:
|
|
77
128
|
|
|
78
129
|
```ts
|
|
79
|
-
InputDevice.on( "
|
|
130
|
+
InputDevice.on( "deviceadded", ({ device }) => {
|
|
80
131
|
// a device was connected
|
|
81
132
|
// do additional setup here, show a dialog, etc.
|
|
82
133
|
})
|
|
83
134
|
|
|
84
|
-
InputDevice.off( "
|
|
135
|
+
InputDevice.off( "deviceadded" ) // stop listening
|
|
85
136
|
```
|
|
86
137
|
|
|
87
138
|
| Event | Description | Payload |
|
|
88
139
|
|---|---|---|
|
|
89
|
-
| `"
|
|
90
|
-
| `"
|
|
140
|
+
| `"deviceadded"` | `{device}` | A device has been added. |
|
|
141
|
+
| `"deviceremoved"` | `{device}` | A device has been removed. |
|
|
91
142
|
|
|
92
143
|
|
|
93
144
|
### KeyboardDevice
|
|
@@ -97,7 +148,7 @@ Unlike gamepads & custom devices, there is a single global keyboard device.
|
|
|
97
148
|
```ts
|
|
98
149
|
let keyboard = InputDevice.keyboard
|
|
99
150
|
|
|
100
|
-
if ( keyboard.key.ControlLeft ) { //
|
|
151
|
+
if ( keyboard.key.ControlLeft ) { // âĻ
|
|
101
152
|
```
|
|
102
153
|
|
|
103
154
|
> [!NOTE]
|
|
@@ -141,12 +192,12 @@ InputDevice.keyboard.layoutSource // "manual"
|
|
|
141
192
|
| Event | Description | Payload |
|
|
142
193
|
|---|---|---|
|
|
143
194
|
| `"layoutdetected"` | `{layout,layoutSource,device}` | The keyboard layout (`"QWERTY"`, `"QWERTZ"`, `"AZERTY"`, or `"JCUKEN"`) has been detected, either from the native API or from keypresses. |
|
|
144
|
-
| `"
|
|
195
|
+
| `"bind"` | `{name,event,keyCode,keyLabel,device}` | A **named bind** key was pressed. |
|
|
145
196
|
| **Key presses:** | | |
|
|
146
197
|
| `"KeyA"` | `{event,keyCode,keyLabel,device}` | The `"KeyA"` was pressed. |
|
|
147
198
|
| `"KeyB"` | `{event,keyCode,keyLabel,device}` | The `"KeyB"` was pressed. |
|
|
148
199
|
| `"KeyC"` | `{event,keyCode,keyLabel,device}` | The `"KeyC"` was pressed. |
|
|
149
|
-
|
|
|
200
|
+
| âĻ | âĻ | âĻ |
|
|
150
201
|
|
|
151
202
|
|
|
152
203
|
### GamepadDevice
|
|
@@ -158,9 +209,9 @@ Gamepad accessors are modelled around the "Standard Controller Layout":
|
|
|
158
209
|
```ts
|
|
159
210
|
let gamepad = InputDevice.gamepads[0]
|
|
160
211
|
|
|
161
|
-
if ( gamepad.button.Start ) { //
|
|
162
|
-
if ( gamepad.leftTrigger > 0.25 ) { //
|
|
163
|
-
if ( gamepad.leftJoystick.x > 0.5 ) { //
|
|
212
|
+
if ( gamepad.button.Start ) { // âĻ
|
|
213
|
+
if ( gamepad.leftTrigger > 0.25 ) { // âĻ
|
|
214
|
+
if ( gamepad.leftJoystick.x > 0.5 ) { // âĻ
|
|
164
215
|
```
|
|
165
216
|
|
|
166
217
|
> [!TIP]
|
|
@@ -172,12 +223,11 @@ if ( gamepad.leftJoystick.x > 0.5 ) { // ...
|
|
|
172
223
|
Use the `playVibration()` method to play a haptic vibration, in supported browsers.
|
|
173
224
|
|
|
174
225
|
```ts
|
|
175
|
-
gamepad.playVibration()
|
|
176
|
-
|
|
177
226
|
gamepad.playVibration({
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
227
|
+
duration: 150,
|
|
228
|
+
weakMagnitude: 0.75,
|
|
229
|
+
strongMagnitude: 0.25,
|
|
230
|
+
// âĻ
|
|
181
231
|
})
|
|
182
232
|
```
|
|
183
233
|
|
|
@@ -185,31 +235,40 @@ gamepad.playVibration({
|
|
|
185
235
|
|
|
186
236
|
The gamepad buttons reference **Standard Controller Layout**:
|
|
187
237
|
|
|
188
|
-
| Button
|
|
238
|
+
| Button | BindableCode | Standard | Nintendo <sup>[[1]](#gamepad---nintendo-layout-remapping)</sup> | Playstation | Xbox |
|
|
189
239
|
|:---:|:---:|:---:|:---:|:---:|:---:|
|
|
190
|
-
| `0` | `"A"` | **A** | A | Cross | A |
|
|
191
|
-
| `1` | `"B"` | **B** | X | Circle | B |
|
|
192
|
-
| `2` | `"X"` | **X** | B | Square | X |
|
|
193
|
-
| `3` | `"Y"` | **Y** | Y | Triangle | Y |
|
|
240
|
+
| `0` | `"A"` | **A / FaceButton1** | A | Cross | A |
|
|
241
|
+
| `1` | `"B"` | **B / FaceButton2** | X | Circle | B |
|
|
242
|
+
| `2` | `"X"` | **X / FaceButton3** | B | Square | X |
|
|
243
|
+
| `3` | `"Y"` | **Y / FaceButton4** | Y | Triangle | Y |
|
|
194
244
|
| `4` | `"LeftShoulder"` | **Left Shoulder** | L | L1 | LB |
|
|
195
245
|
| `5` | `"RightShoulder"` | **Right Shoulder** | R | R1 | RB |
|
|
196
246
|
| `6` | `"LeftTrigger"` | **Left Trigger** | L2 | ZL | LT |
|
|
197
247
|
| `7` | `"RightTrigger"` | **Right Trigger** | R2 | ZR | RT |
|
|
198
248
|
| `8` | `"Back"` | **Back** | Minus | Options | Back |
|
|
199
249
|
| `9` | `"Start"` | **Start** | Plus | Select | Start |
|
|
200
|
-
| `10` | `"
|
|
201
|
-
| `11` | `"
|
|
250
|
+
| `10` | `"LeftStickClick"` | **Left Stick Click** | L3 | L3 | LSB |
|
|
251
|
+
| `11` | `"RightStickClick"` | **Right Stick Click** | R3 | R3 | RSB |
|
|
202
252
|
| `12` | `"DPadUp"` | **D-Pad Up** | âŦī¸ | âŦī¸ | âŦī¸ |
|
|
203
253
|
| `13` | `"DPadDown"` | **D-Pad Down** | âŦī¸ | âŦī¸ | âŦī¸ |
|
|
204
254
|
| `14` | `"DPadLeft"` | **D-Pad Left** | âŦ
ī¸ | âŦ
ī¸ | âŦ
ī¸ |
|
|
205
255
|
| `15` | `"DPadRight"` | **D-Pad Right** | âĄī¸ | âĄī¸ | âĄī¸ |
|
|
206
256
|
|
|
207
|
-
|
|
257
|
+
#### Gamepad Axis Codes
|
|
258
|
+
|
|
259
|
+
Bindable helpers are available for the joysticks.
|
|
260
|
+
|
|
261
|
+
| Axis # | AxisCode | Standard | Layout
|
|
262
|
+
|:---:|:---:|:---:|:---:|
|
|
263
|
+
| `0` | `"LeftStickLeft"`<br/>`"LeftStickRight"` | **Left Stick (Left/Right)** | âŦ
ī¸âĄī¸ |
|
|
264
|
+
| `1` | `"LeftStickUp"`<br/>`"LeftStickDown"` | **Left Stick (Up/Down)** | âŦī¸âŦī¸ |
|
|
265
|
+
| `2` | `"RightStickLeft"`<br/>`"RightStickRight"` | **Right Stick (Left/Right)** | âŦ
ī¸âĄī¸ |
|
|
266
|
+
| `3` | `"RightStickUp"`<br/>`"RightStickDown"` | **Right Stick (Up/Down)** | âŦī¸âŦī¸ |
|
|
208
267
|
|
|
209
268
|
#### Gamepad Layouts
|
|
210
269
|
|
|
211
270
|
```ts
|
|
212
|
-
gamepad.layout // "nintendo" | "xbox" | "playstation" | "logitech" | "steam" | "
|
|
271
|
+
gamepad.layout // "nintendo" | "xbox" | "playstation" | "logitech" | "steam" | "standard"
|
|
213
272
|
```
|
|
214
273
|
|
|
215
274
|
Layout detection is **highly non-standard** across major browsers, it should generally be used for aesthetic
|
|
@@ -224,10 +283,10 @@ only major brand controller that deviates from the standard.
|
|
|
224
283
|
> ***Nintendo:** Both the labels and physical positions of the A,B,X,Y buttons are different
|
|
225
284
|
> on Nintendo controllers.
|
|
226
285
|
>
|
|
227
|
-
> Set `GamepadDevice.defaultOptions.
|
|
286
|
+
> Set `GamepadDevice.defaultOptions.nintendoRemapMode` to apply the remapping as required.
|
|
228
287
|
>
|
|
229
|
-
> - `"physical"` _**(default)**_ – The A,B,X,Y button codes will refer the
|
|
230
|
-
> - `"accurate"` – The A,B,X,Y button codes will
|
|
288
|
+
> - `"physical"` _**(default)**_ – The A,B,X,Y button codes will refer the standard face button positions (Left=X, Top=Y, Bottom=A, Right=B).
|
|
289
|
+
> - `"accurate"` – The A,B,X,Y button codes will refer to the exact Nintendo labels (Left=Y, Top=X, Bottom=B, Right=A).
|
|
231
290
|
> - `"none"` – The A,B,X,Y button codes mapping stay at the default indices (Left=Y, Top=B, Bottom=X, Right=A).
|
|
232
291
|
>
|
|
233
292
|
> ```
|
|
@@ -247,25 +306,28 @@ only major brand controller that deviates from the standard.
|
|
|
247
306
|
You can manually override this per-gamepad, or for all gamepads:
|
|
248
307
|
|
|
249
308
|
```ts
|
|
250
|
-
|
|
251
|
-
GamepadDevice.defaultOptions.
|
|
309
|
+
// set default
|
|
310
|
+
GamepadDevice.defaultOptions.nintendoRemapMode = "none"
|
|
311
|
+
|
|
312
|
+
// set for a single gamepad
|
|
313
|
+
gamepad.options.nintendoRemapMode = "accurate"
|
|
252
314
|
```
|
|
253
315
|
|
|
254
316
|
#### GamepadDevice Events
|
|
255
317
|
|
|
256
318
|
| Event | Description | Payload |
|
|
257
319
|
|---|---|---|
|
|
258
|
-
| `"
|
|
320
|
+
| `"bind"` | `{name,button,buttonCode,device}` | A **named bind** button was pressed. |
|
|
259
321
|
| **Button presses:** | | |
|
|
260
322
|
| `"A"` | `{button,buttonCode,device}` | Standard layout button `"A"` was pressed. Equivalent to `0`. |
|
|
261
323
|
| `"B"` | `{button,buttonCode,device}` | Standard layout button `"B"` was pressed. Equivalent to `1`. |
|
|
262
324
|
| `"X"` | `{button,buttonCode,device}` | Standard layout button `"X"` was pressed. Equivalent to `2`. |
|
|
263
|
-
|
|
|
325
|
+
| âĻ | âĻ | âĻ |
|
|
264
326
|
| **Button presses (no label):** | | |
|
|
265
327
|
| `0` or `Button.A` | `{button,buttonCode,device}` | Button at offset `0` was pressed. |
|
|
266
328
|
| `1` or `Button.B` | `{button,buttonCode,device}` | Button at offset `1` was pressed. |
|
|
267
329
|
| `2` or `Button.X` | `{button,buttonCode,device}` | Button at offset `2` was pressed. |
|
|
268
|
-
|
|
|
330
|
+
| âĻ | âĻ | âĻ |
|
|
269
331
|
|
|
270
332
|
### Custom Devices
|
|
271
333
|
|
|
@@ -287,26 +349,26 @@ export const myDevice: CustomDevice = {
|
|
|
287
349
|
InputDevice.add( myDevice )
|
|
288
350
|
```
|
|
289
351
|
|
|
290
|
-
## Named
|
|
352
|
+
## Named Binds
|
|
291
353
|
|
|
292
|
-
Use
|
|
354
|
+
Use _named binds_ to create mappings between abstract inputs and the keys/buttons that trigger those inputs.
|
|
293
355
|
|
|
294
356
|
This allows you to change the keys/buttons later (e.g. allow users to override inputs).
|
|
295
357
|
|
|
296
358
|
```ts
|
|
297
359
|
// keyboard:
|
|
298
|
-
InputDevice.keyboard.
|
|
360
|
+
InputDevice.keyboard.configureBinds({
|
|
299
361
|
jump: [ "ArrowUp", "Space", "KeyW" ],
|
|
300
362
|
crouch: [ "ArrowDown", "KeyS" ],
|
|
301
363
|
toggleGraphics: [ "KeyB" ],
|
|
302
|
-
}
|
|
364
|
+
})
|
|
303
365
|
|
|
304
366
|
// all gamepads:
|
|
305
|
-
GamepadDevice.
|
|
306
|
-
jump: [ "A" ],
|
|
367
|
+
GamepadDevice.configureDefaultBinds({
|
|
368
|
+
jump: [ "A", "LeftStickUp" ],
|
|
307
369
|
crouch: [ "B", "X", "RightTrigger" ],
|
|
308
|
-
toggleGraphics: [ "
|
|
309
|
-
}
|
|
370
|
+
toggleGraphics: [ "RightStickUp", "RightStickDown" ],
|
|
371
|
+
})
|
|
310
372
|
```
|
|
311
373
|
|
|
312
374
|
These can then be used with either the real-time and event-based APIs.
|
|
@@ -315,11 +377,11 @@ These can then be used with either the real-time and event-based APIs.
|
|
|
315
377
|
|
|
316
378
|
```ts
|
|
317
379
|
// listen to all devices:
|
|
318
|
-
InputDevice.
|
|
380
|
+
InputDevice.onBind( "toggleGraphics", ( e ) => toggleGraphics() )
|
|
319
381
|
|
|
320
382
|
// listen to specific devices:
|
|
321
|
-
InputDevice.keyboard.
|
|
322
|
-
InputDevice.gamepads[0].
|
|
383
|
+
InputDevice.keyboard.onBind( "jump", ( e ) => doJump() )
|
|
384
|
+
InputDevice.gamepads[0].onBind( "jump", ( e ) => doJump() )
|
|
323
385
|
```
|
|
324
386
|
|
|
325
387
|
#### Real-time:
|
|
@@ -328,14 +390,14 @@ InputDevice.gamepads[0].onGroup( "jump", ( e ) => doJump() )
|
|
|
328
390
|
let jump = false, crouch = false, moveX = 0
|
|
329
391
|
|
|
330
392
|
const keyboard = InputDevice.keyboard
|
|
331
|
-
if ( keyboard.
|
|
332
|
-
if ( keyboard.
|
|
393
|
+
if ( keyboard.pressedBind( "jump" ) ) jump = true
|
|
394
|
+
if ( keyboard.pressedBind( "crouch" ) ) crouch = true
|
|
333
395
|
if ( keyboard.key.ArrowLeft ) moveX = -1
|
|
334
396
|
else if ( keyboard.key.ArrowRight ) moveX = 1
|
|
335
397
|
|
|
336
398
|
for ( const gamepad of InputDevice.gamepads ) {
|
|
337
|
-
if ( gamepad.
|
|
338
|
-
if ( gamepad.
|
|
399
|
+
if ( gamepad.pressedBind( "jump" ) ) jump = true
|
|
400
|
+
if ( gamepad.pressedBind( "crouch" ) ) crouch = true
|
|
339
401
|
|
|
340
402
|
// gamepads have additional analog inputs
|
|
341
403
|
// we're going to apply these only if touched
|
|
@@ -344,123 +406,69 @@ for ( const gamepad of InputDevice.gamepads ) {
|
|
|
344
406
|
}
|
|
345
407
|
```
|
|
346
408
|
|
|
347
|
-
##
|
|
348
|
-
|
|
349
|
-
Automatically traverse existing pointer/mouse based menus using the `Navigation` API.
|
|
350
|
-
|
|
351
|
-
```ts
|
|
352
|
-
// set root container
|
|
353
|
-
Navigation.stage = app.stage
|
|
354
|
-
|
|
355
|
-
const button = new ButtonSprite()
|
|
356
|
-
button.on( "mousedown", () => button.run( clickAnimation ) )
|
|
357
|
-
button.on( "mouseout", () => button.run( resetAnimation ) )
|
|
358
|
-
button.on( "mouseover", () => button.run( hoverAnimation ) )
|
|
359
|
-
|
|
360
|
-
app.stage.addChild( button )
|
|
361
|
-
|
|
362
|
-
button.isNavigatable // true
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
> [!NOTE]
|
|
366
|
-
> **isNavigatable:** By default, any element with `"mousedown"` or `"pointerdown"` handlers is navigatable.
|
|
409
|
+
## UINavigation API
|
|
367
410
|
|
|
368
|
-
|
|
369
|
-
> **Fallback Hover Effect:** If there is no `"pointerover"` or `"mouseover"` handler detected on a container, `Navigation`
|
|
370
|
-
> will apply abasic alpha effect to the selected item to indicate which container is currently the navigation target. This
|
|
371
|
-
> can be disabled by setting `Navigation.options.useFallbackHoverEffect` to `false`.
|
|
372
|
-
|
|
373
|
-
### Disable Navigation
|
|
374
|
-
|
|
375
|
-
You can **disable** the navigation API - either permanently or temporarily - like so:
|
|
411
|
+
_Traverse a UI using input devices._
|
|
376
412
|
|
|
377
413
|
```ts
|
|
378
|
-
|
|
414
|
+
UINavigation.configureWithRoot( app.stage ) // (or any Container)
|
|
379
415
|
```
|
|
380
416
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
UIs can be complex! The Navigation API allows you to take over some - or all - of the navigation elements.
|
|
384
|
-
|
|
385
|
-
You can create **NavigationResponder** controllers, which can be a `Container` that becomes the
|
|
386
|
-
"root" node for navigation. It can also just be any object (like a custom manager class).
|
|
387
|
-
|
|
388
|
-
It has a method called `handledNavigationIntent(): boolean` which can return a boolean saying whether
|
|
389
|
-
the navigation event was handled. If you return false here, it is bubbled up to the next parent in the
|
|
390
|
-
stack.
|
|
391
|
-
|
|
392
|
-
To add a responder, just use `Navigation.pushResponder( responder )` - and then remove it with `Navigation.popResponder()`.
|
|
417
|
+
You can manually take control of navigation using:
|
|
393
418
|
|
|
394
419
|
```ts
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
handledNavigationIntent( intent, device ): boolean {
|
|
398
|
-
if ( intent === "navigateUp" ) this.moveCursorUp()
|
|
399
|
-
else if ( intent === "navigateDown" ) this.moveCursorDown()
|
|
400
|
-
else if ( intent === "navigateBack" ) this.loseFocus()
|
|
401
|
-
else if ( intent === "trigger" ) this.clickCursorItem()
|
|
402
|
-
|
|
403
|
-
// we are going to return false here, which will propagates unhandled
|
|
404
|
-
// intents ("navigateLeft", "navigateRight") up to the next responder
|
|
405
|
-
// in the stack - which could be a parent view, etc.
|
|
406
|
-
return false
|
|
407
|
-
}
|
|
420
|
+
// take control
|
|
421
|
+
UINavigation.pushResponder( myModalView )
|
|
408
422
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
423
|
+
// relinquish control
|
|
424
|
+
UINavigation.popResponder()
|
|
425
|
+
```
|
|
412
426
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
}
|
|
427
|
+
The Navigation API is centered around the **UINavigation** manager, which
|
|
428
|
+
receives navigation intents from devices and forwards it to the UI context.
|
|
417
429
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
```
|
|
430
|
+
The **UINavigation** manager maintains a stack of responders, which can be a
|
|
431
|
+
`Container`, or any object that implements the `NavigationResponder` interface.
|
|
421
432
|
|
|
422
|
-
|
|
433
|
+
When a device sends a navigation intent, the **UINavigation** manager is
|
|
434
|
+
responsible for asking the **first responder** whether it can handle the intent.
|
|
423
435
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
{
|
|
427
|
-
handledNavigationIntent( intent, device ) {
|
|
428
|
-
// ignore navigation intents, but allow other navigatable
|
|
429
|
-
// views to be pushed on top of me - e.g. a dialog window:
|
|
430
|
-
return true
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
```
|
|
436
|
+
If it returns `false`, any other responders are checked (if they exist),
|
|
437
|
+
otherwise the default global navigation behavior kicks in.
|
|
434
438
|
|
|
435
|
-
### Default Navigation
|
|
439
|
+
### Default Global Navigation Behaviors
|
|
436
440
|
|
|
437
|
-
|
|
441
|
+
When a navigation intent is **not** handled manually by a responder, it is handled in one of the following ways:
|
|
438
442
|
|
|
439
|
-
|
|
443
|
+
| Intent | Behavior |
|
|
444
|
+
|---|---|
|
|
445
|
+
|`"navigate.back"`|<ul><li>No action.</li></ul>|
|
|
446
|
+
|`"navigate.left"`, `"navigate.right"`, `"navigate.up"`, `"navigate.down"`|<ul><li>Looks for the nearest `Container` where `container.isNavigatable` in the direction given, and if found, receives a `"deviceover"` event.</li><li>Additionally, if the newly focused container has registered an event handler for either `"pointerover"` or `"mouseover"` (in that order), it will fire that too.</li><li>If we were previously focused on a container, that previous container receives a `"deviceout"` event.</li><li>If the blurred container has register an event handler for either `"pointerout"` or `"mouseout"` (in that order), that event handler will be fired too.</li></ul>|
|
|
447
|
+
|`"navigate.trigger"`|<ul><li>Checks if we are currently focused on a container, and then issue a `"devicedown"` event.</li><li>If the focused container has registered an event handler for either `"pointerdown"` or `"mousedown"` (in that order), that event handler will be fired too.</li></ul>|
|
|
440
448
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
`"
|
|
444
|
-
`"
|
|
445
|
-
`"
|
|
446
|
-
`"navigateDown"` | `ArrowDown`, `KeyS` | Left Joystick (Down), `DPadUp`
|
|
447
|
-
`"navigateBack"` | `Escape`, `Backspace` | `B`, `Back`
|
|
448
|
-
`"trigger"` | `Enter,` `Space` | `A`
|
|
449
|
+
Container event | Description | Compatibility
|
|
450
|
+
-----------------|--------------------------------------------------------
|
|
451
|
+
`"devicedown"` | Target was triggered. | `"pointerdown"`, `"mousedown"`
|
|
452
|
+
`"deviceover"` | Target became focused. | `"pointerover"`, `"mouseover"`
|
|
453
|
+
`"deviceout"` | Target lost focus. | `"pointerout"`, `"mouseout"`
|
|
449
454
|
|
|
450
|
-
|
|
455
|
+
### Container Navigatability
|
|
451
456
|
|
|
452
|
-
|
|
457
|
+
Containers are extended with a few properties/accessors:
|
|
453
458
|
|
|
454
459
|
Container properties | type | default | description
|
|
455
460
|
---------------------|------|---------|--------------
|
|
456
|
-
`isNavigatable` | `boolean` | `false` | returns `true` if `navigationMode` is set to `"target"`, or is `"auto"` and a `"pointerdown"` or `"mousedown"` event handler is registered.
|
|
461
|
+
`isNavigatable` | `get(): boolean` | `false` | returns `true` if `navigationMode` is set to `"target"`, or is `"auto"` and a `"pointerdown"` or `"mousedown"` event handler is registered.
|
|
457
462
|
`navigationMode` | `"auto"` \| `"disabled"` \| `"target"` | `"auto"` | When set to `"auto"`, a `Container` can be navigated to if it has a `"pointerdown"` or `"mousedown"` event handler registered.
|
|
458
463
|
`navigationPriority` | `number` | `0` | The priority relative to other navigation items in this group.
|
|
459
464
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
465
|
+
> [!NOTE]
|
|
466
|
+
> **isNavigatable:** By default, any element with `"pointerdown"` or `"mousedown"` handlers is navigatable.
|
|
467
|
+
|
|
468
|
+
> [!WARNING]
|
|
469
|
+
> **Fallback Hover Effect:** If there is no `"pointerover"` or `"mouseover"` handler detected on a container, `UINavigation`
|
|
470
|
+
> will apply abasic alpha effect to the selected item to indicate which container is currently the navigation target. This
|
|
471
|
+
> can be disabled by setting `UINavigation.options.useFallbackHoverEffect` to `false`.
|
|
464
472
|
|
|
465
473
|
|
|
466
474
|
## Advanced usage
|
|
@@ -519,7 +527,7 @@ InputDevice.remove( onscreen )
|
|
|
519
527
|
You could set up multiple named inputs:
|
|
520
528
|
|
|
521
529
|
```ts
|
|
522
|
-
InputDevice.keyboard.
|
|
530
|
+
InputDevice.keyboard.configureBinds({
|
|
523
531
|
jump: [ "ArrowUp", "KeyW" ],
|
|
524
532
|
defend: [ "ArrowDown", "KeyS" ],
|
|
525
533
|
left: [ "ArrowLeft", "KeyA" ],
|
|
@@ -534,31 +542,31 @@ InputDevice.keyboard.options.namedGroups = {
|
|
|
534
542
|
p2_defend: [ "ArrowDown" ],
|
|
535
543
|
p2_left: [ "ArrowLeft" ],
|
|
536
544
|
p2_right: [ "ArrowRight" ],
|
|
537
|
-
}
|
|
545
|
+
})
|
|
538
546
|
```
|
|
539
547
|
|
|
540
548
|
and then switch groups depending on the mode:
|
|
541
549
|
|
|
542
550
|
```ts
|
|
543
|
-
if ( gameMode === "
|
|
551
|
+
if ( gameMode === "multiplayer" )
|
|
544
552
|
{
|
|
545
|
-
|
|
546
|
-
player1.
|
|
547
|
-
player1.
|
|
548
|
-
player1.moveX += device.
|
|
549
|
-
|
|
550
|
-
player2.jump
|
|
551
|
-
player2.defend = device.
|
|
552
|
-
player2.moveX += device.
|
|
553
|
-
player2.moveX += device.
|
|
553
|
+
player1.jump = device.pressedBind( "p1_jump" )
|
|
554
|
+
player1.defend = device.pressedBind( "p1_defend" )
|
|
555
|
+
player1.moveX += device.pressedBind( "p1_left" ) ? -1 : 0
|
|
556
|
+
player1.moveX += device.pressedBind( "p1_right" ) ? 1 : 0
|
|
557
|
+
|
|
558
|
+
player2.jump = device.pressedBind( "p2_jump" )
|
|
559
|
+
player2.defend = device.pressedBind( "p2_defend" )
|
|
560
|
+
player2.moveX += device.pressedBind( "p2_left" ) ? -1 : 0
|
|
561
|
+
player2.moveX += device.pressedBind( "p2_right" ) ? 1 : 0
|
|
554
562
|
}
|
|
555
563
|
else
|
|
556
564
|
{
|
|
557
|
-
|
|
558
|
-
player1.
|
|
559
|
-
player1.
|
|
560
|
-
player1.moveX += device.
|
|
561
|
-
|
|
562
|
-
player2
|
|
565
|
+
player1.jump = device.pressedBind( "jump" )
|
|
566
|
+
player1.defend = device.pressedBind( "defend" )
|
|
567
|
+
player1.moveX += device.pressedBind( "left" ) ? -1 : 0
|
|
568
|
+
player1.moveX += device.pressedBind( "right" ) ? 1 : 0
|
|
569
|
+
|
|
570
|
+
updateComputerPlayerInput( player2 )
|
|
563
571
|
}
|
|
564
572
|
```
|