pixijs-input-devices 0.5.7 → 0.6.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 CHANGED
@@ -4,10 +4,10 @@
4
4
 
5
5
  | | |
6
6
  | ------ | ------ |
7
- | 🎮 Interface [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 &amp; 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](#uinavigation-api) _(optional)_ |
7
+ | 🎮 Handle [keyboard](#keyboarddevice), [gamepads](#gamepaddevice), and [more](#custom-devices)! | 🚀 [Real-time](#real-time) &amp; [event-driven](#keyboarddevice-events) APIs |
8
+ | ⚡ Highly-optimized for [performance](https://web.dev/articles/inp) | 🧭 Built-in [UI navigation](#uinavigation-api) |
9
+ | 🔮 Highly configurable (with sensible defaults) | 🪄 Supports [input binding](#named-binds) |
10
+ | ✅ Cross-platform &amp; 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> | 🌐 Automatic [Intl layouts](#keyboard-layout---detection) detection |
11
11
  | 🍃 Zero dependencies & tree-shakeable | ✨ Supports PixiJS v8, v7, v6.3+ |
12
12
 
13
13
 
@@ -16,37 +16,43 @@
16
16
  *Handle device inputs with ease.*
17
17
 
18
18
  ```ts
19
- import { InputDevice, GamepadDevice } from "pixijs-input-devices";
19
+ import { InputDevice, GamepadDevice } from "pixijs-input-devices"
20
+
20
21
 
21
22
  // Set named binds
22
- InputDevice.keyboard.configureBinds({ jump: [ "Space" ] })
23
- GamepadDevice.configureDefaultBinds({ jump: [ "A", "LeftStickUp" ] })
23
+ GamepadDevice.configureDefaultBinds({
24
+ jump: [ "Face1" ]
25
+ })
26
+ InputDevice.keyboard.configureBinds({
27
+ jump: [ "ArrowUp", "Space" ]
28
+ })
24
29
 
25
30
  // Use binds
26
- for ( const device of InputDevice.devices ) {
27
- if ( device.pressedBind("jump") ) doJump()
31
+ for (const device of InputDevice.devices) {
32
+ if (device.bindDown("jump")) // ...
28
33
  }
29
34
 
30
35
  // Event-driven
31
- InputDevice.onBind( "jump", ({ device }) =>
32
- if ( device.type === "gamepad" ) {
33
- e.device.playVibration({ duration: 100 })
36
+ InputDevice.onBindDown("jump", ({ device }) => {
37
+ if (device.type === "gamepad") {
38
+ device.playVibration({ duration: 50 })
34
39
  }
35
- );
40
+ })
36
41
  ```
37
42
 
38
43
  ## Getting Started with PixiJS Input Devices
39
44
 
40
- *Everything you need to quickly integrate powerful device management.*
45
+ *Everything you need to quickly integrate device management.*
41
46
 
42
- **PixiJS Input Devices** adds first-class support for input device management and input handling. It also provides an optional navigation manager
43
- that can enable input devices to traverse pointer-based UIs.
47
+ **PixiJS Input Devices** adds first-class support for input devices, and
48
+ provides a simple, but powerful navigation manager that can enable devices to
49
+ navigate existing pointer-based UIs.
44
50
 
45
- The core concepts are:
51
+ The key concepts are:
46
52
 
47
53
  1. **Devices:** _Any human interface device_
48
54
  2. **Binds:** _Custom, named input actions that can be triggered by assigned keys or buttons_
49
- 3. **UINavigation:** _A global controller that allows non-pointer devices to navigate UIs_
55
+ 3. **UINavigation:** _Navigation manager for non-pointer devices to navigate UIs_
50
56
 
51
57
  > [!NOTE]
52
58
  > _See [UINavigation API](#uinavigation-api) for more information._
@@ -69,10 +75,11 @@ yarn add pixijs-input-devices --dev
69
75
  **2.** Register the update loop:
70
76
 
71
77
  ```ts
72
- import { Ticker } from 'pixi.js';
73
- import { InputDevice } from 'pixijs-input-devices';
78
+ import { Ticker } from 'pixi.js'
79
+ import { InputDevice } from 'pixijs-input-devices'
80
+
74
81
 
75
- Ticker.shared.add(ticker => InputDevice.update());
82
+ Ticker.shared.add(() => InputDevice.update())
76
83
  ```
77
84
 
78
85
  > [!TIP]
@@ -81,14 +88,15 @@ Ticker.shared.add(ticker => InputDevice.update());
81
88
  **3.** (Optional) enable the UINavigation API
82
89
 
83
90
  ```ts
84
- import * as PIXI from 'pixi.js';
85
- import { UINavigation, registerPixiJSNavigationMixin } from 'pixijs-input-devices';
91
+ import * as PIXI from 'pixi.js'
92
+ import { UINavigation, registerPixiJSNavigationMixin } from 'pixijs-input-devices'
93
+
86
94
 
87
95
  const app = new PIXI.Application(/*…*/)
88
96
 
89
97
  // enable the navigation API
90
- UINavigation.configureWithRoot( app.stage )
91
- registerPixiJSNavigationMixin( PIXI.Container )
98
+ UINavigation.configureWithRoot(app.stage)
99
+ registerPixiJSNavigationMixin(PIXI.Container)
92
100
  ```
93
101
 
94
102
  ✨ You are now ready to use inputs!
@@ -101,46 +109,65 @@ The `InputDevice` singleton controls all device discovery.
101
109
 
102
110
  ```ts
103
111
  InputDevice.keyboard // KeyboardDevice
104
- InputDevice.gamepads // Array<GamepadDevice>
105
- InputDevice.custom // Array<CustomDevice>
112
+ InputDevice.gamepads // GamepadDevice[]
113
+ InputDevice.custom // Device[]
106
114
  ```
107
115
 
108
116
  You can access all **active/connected** devices using `.devices`:
109
117
 
110
118
  ```ts
111
- for ( const device of InputDevice.devices ) { // …
119
+ for (const device of InputDevice.devices) { // …
112
120
  ```
113
121
 
114
122
  #### InputDevice - properties
115
123
 
124
+ The `InputDevice` manager provides the following **context capability** properties:
125
+
116
126
  | Property | Type | Description |
117
127
  |---|---|---|
118
- | `InputDevice.isMobile` | `boolean` | Whether the context is mobile (including tablets). |
119
- | `InputDevice.isTouchCapable` | `boolean` | Whether the context has touchscreen capability. |
128
+ | `InputDevice.hasMouseLikePointer` | `boolean` | Whether the context has a mouse/trackpad. |
129
+ | `InputDevice.isMobile` | `boolean` | Whether the context is mobile capable. |
130
+ | `InputDevice.isTouchCapable` | `boolean` | Whether the context is touchscreen capable. |
131
+
132
+ As well as shortcuts to **connected devices**:
133
+
134
+ | Accessor | Type | Description |
135
+ |---|---|---|
120
136
  | `InputDevice.lastInteractedDevice` | `Device?` | The most recently interacted device (or first if multiple). |
121
137
  | `InputDevice.devices` | `Device[]` | All active, connected devices. |
122
138
  | `InputDevice.keyboard` | `KeyboardDevice` | The global keyboard. |
123
139
  | `InputDevice.gamepads` | `GamepadDevice[]` | Connected gamepads. |
124
- | `InputDevice.custom` | `CustomDevice[]` | Custom devices. |
140
+ | `InputDevice.custom` | `CustomDevice[]` | Any custom devices. |
125
141
 
126
142
  #### InputDevice - on() Events
127
143
 
128
144
  Access global events directly through the manager:
129
145
 
130
146
  ```ts
131
- InputDevice.on( "deviceadded", ({ device }) => {
132
- // a device was connected
147
+ InputDevice.on("deviceadded", ({ device }) => {
148
+ // new device was connected or became available
133
149
  // do additional setup here, show a dialog, etc.
134
150
  })
135
151
 
136
- InputDevice.off( "deviceadded" ) // stop listening
152
+ InputDevice.off("deviceadded") // stop listening
137
153
  ```
138
154
 
139
155
  | Event | Description | Payload |
140
156
  |---|---|---|
141
157
  | `"deviceadded"` | `{device}` | A device has been added. |
142
158
  | `"deviceremoved"` | `{device}` | A device has been removed. |
159
+ | `"lastdevicechanged"` | `{device}` | The _last interacted device_ has changed. |
160
+
143
161
 
162
+ #### InputDevice - onBindDown() Events
163
+
164
+ You may also subscribe globally to **named bind** events:
165
+
166
+ ```ts
167
+ InputDevice.onBindDown("my_custom_bind", (event) => {
168
+ // a bound input waas triggered
169
+ })
170
+ ```
144
171
 
145
172
  ### KeyboardDevice
146
173
 
@@ -149,7 +176,7 @@ Unlike gamepads & custom devices, there is a single global keyboard device.
149
176
  ```ts
150
177
  let keyboard = InputDevice.keyboard
151
178
 
152
- if ( keyboard.key.ControlLeft ) { // …
179
+ if (keyboard.key.ControlLeft) { // …
153
180
  ```
154
181
 
155
182
  > [!NOTE]
@@ -161,7 +188,7 @@ if ( keyboard.key.ControlLeft ) { // …
161
188
  ```ts
162
189
  keyboard.layout // "AZERTY" | "JCUKEN" | "QWERTY" | "QWERTZ"
163
190
 
164
- keyboard.getKeyLabel( "KeyZ" ) // Я
191
+ keyboard.getKeyLabel("KeyZ") // Я
165
192
  ```
166
193
 
167
194
  > [!NOTE]
@@ -169,7 +196,7 @@ keyboard.getKeyLabel( "KeyZ" ) // Я
169
196
  > Almost every keyboard is one of these four (or a regional derivative &ndash; e.g. Hangeul,
170
197
  > Kana). There is no built-in detection for specialist or esoteric layouts (e.g. Dvorak, Colemak, BÉPO).
171
198
  >
172
- > The `keyboard.getKeyLabel( key )` uses the [KeyboardLayoutMap API](https://caniuse.com/mdn-api_keyboardlayoutmap)
199
+ > The `keyboard.getKeyLabel(key)` uses the [KeyboardLayoutMap API](https://caniuse.com/mdn-api_keyboardlayoutmap)
173
200
  > when available, before falling back to default AZERTY, JCUKEN, QWERTY or QWERTZ key values.
174
201
 
175
202
  The keyboard layout is automatically detected from (in order):
@@ -184,7 +211,7 @@ You can also manually force the layout:
184
211
  // force layout
185
212
  InputDevice.keyboard.layout = "JCUKEN"
186
213
 
187
- InputDevice.keyboard.getKeyLabel( "KeyW" ) // "Ц"
214
+ InputDevice.keyboard.getKeyLabel("KeyW") // "Ц"
188
215
  InputDevice.keyboard.layoutSource // "manual"
189
216
  ```
190
217
 
@@ -193,7 +220,7 @@ InputDevice.keyboard.layoutSource // "manual"
193
220
  | Event | Description | Payload |
194
221
  |---|---|---|
195
222
  | `"layoutdetected"` | `{layout,layoutSource,device}` | The keyboard layout (`"QWERTY"`, `"QWERTZ"`, `"AZERTY"`, or `"JCUKEN"`) has been detected, either from the native API or from keypresses. |
196
- | `"bind"` | `{name,event,keyCode,keyLabel,device}` | A **named bind** key was pressed. |
223
+ | `"binddown"` | `{name,event,keyCode,keyLabel,device}` | A **named bind** key was pressed. |
197
224
  | **Key presses:** | | |
198
225
  | `"KeyA"` | `{event,keyCode,keyLabel,device}` | The `"KeyA"` was pressed. |
199
226
  | `"KeyB"` | `{event,keyCode,keyLabel,device}` | The `"KeyB"` was pressed. |
@@ -208,11 +235,22 @@ Gamepads are automatically detected via the browser API when first interacted wi
208
235
  Gamepad accessors are modelled around the "Standard Controller Layout":
209
236
 
210
237
  ```ts
211
- let gamepad = InputDevice.gamepads[0]
238
+ const gamepad = InputDevice.gamepads[0];
239
+
240
+ if (gamepad.button.DpadDown)
241
+ {
242
+ // button pressed
243
+ }
212
244
 
213
- if ( gamepad.button.Start ) { // …
214
- if ( gamepad.leftTrigger > 0.25 ) { // …
215
- if ( gamepad.leftJoystick.x > 0.5 ) { // …
245
+ if (gamepad.leftTrigger > 0.25)
246
+ {
247
+ // trigger pulled
248
+ }
249
+
250
+ if (gamepad.leftJoystick.x < -0.33)
251
+ {
252
+ // joystick moved
253
+ }
216
254
  ```
217
255
 
218
256
  > [!TIP]
@@ -236,24 +274,24 @@ gamepad.playVibration({
236
274
 
237
275
  The gamepad buttons reference **Standard Controller Layout**:
238
276
 
239
- | Button Index | GamepadCode | Description | Xbox | Playstation | Nintendo<sup>[[?]](#gamepad---nintendo-layout-remapping)</sup> |
277
+ | Button # | GamepadCode | Description | Xbox Series X | Playstation 5 DualSense® | Nintendo Switch™ Pro |
240
278
  |:---:|:---|:---|:---:|:---:|:---:|
241
- | `0` | `"A"` | **Face Button 0** | A | Cross | A |
242
- | `1` | `"B"` | **Face Button 1** | B | Circle | X* |
243
- | `2` | `"X"` | **Face Button 2** | X | Square | B* |
244
- | `3` | `"Y"` | **Face Button 3** | Y | Triangle | Y |
279
+ | `0` | `"Face1"` | **Face Button 1** | A | Cross | B |
280
+ | `1` | `"Face2"` | **Face Button 2** | B | Circle | A |
281
+ | `2` | `"Face3"` | **Face Button 3** | X | Square | Y |
282
+ | `3` | `"Face4"` | **Face Button 4** | Y | Triangle | X |
245
283
  | `4` | `"LeftShoulder"` | **Left Shoulder** | LB | L1 | L |
246
284
  | `5` | `"RightShoulder"` | **Right Shoulder** | RB | R1 | R |
247
285
  | `6` | `"LeftTrigger"` | **Left Trigger** | LT | L2 | ZL |
248
286
  | `7` | `"RightTrigger"` | **Right Trigger** | RT | R2 | ZR |
249
- | `8` | `"Back"` | **Back** | Back | Options | Minus |
250
- | `9` | `"Start"` | **Start** | Start | Select | Plus |
251
- | `10` | `"LeftStickClick"` | **Left Stick Click** | LSB | L3 | L3 |
252
- | `11` | `"RightStickClick"` | **Right Stick Click** | RSB | R3 | R3 |
253
- | `12` | `"DPadUp"` | **D-Pad Up** | ⬆️ | ⬆️ | ⬆️ |
254
- | `13` | `"DPadDown"` | **D-Pad Down** | ⬇️ | ⬇️ | ⬇️ |
255
- | `14` | `"DPadLeft"` | **D-Pad Left** | ⬅️ | ⬅️ | ⬅️ |
256
- | `15` | `"DPadRight"` | **D-Pad Right** | ➡️ | ➡️ | ➡️ |
287
+ | `8` | `"Back"` | **Back** | View | Options | Minus |
288
+ | `9` | `"Start"` | **Start** | Menu | Select | Plus |
289
+ | `10` | `"LeftStickClick"` | **Left Stick (Click)** | LSB | L3 | L3 |
290
+ | `11` | `"RightStickClick"` | **Right Stick (Click)** | RSB | R3 | R3 |
291
+ | `12` | `"DpadUp"` | **D-Pad Up** | ⬆️ | ⬆️ | ⬆️ |
292
+ | `13` | `"DpadDown"` | **D-Pad Down** | ⬇️ | ⬇️ | ⬇️ |
293
+ | `14` | `"DpadLeft"` | **D-Pad Left** | ⬅️ | ⬅️ | ⬅️ |
294
+ | `15` | `"DpadRight"` | **D-Pad Right** | ➡️ | ➡️ | ➡️ |
257
295
 
258
296
  #### Gamepad Axis Codes
259
297
 
@@ -261,71 +299,32 @@ Bindable helpers are available for the joysticks too:
261
299
 
262
300
  | Axis # | GamepadCode | Standard | Layout
263
301
  |:---:|:---:|:---:|:---:|
264
- | `0` | `"LeftStickLeft"`<br/>`"LeftStickRight"` | **Left Stick (Left/Right)** | ⬅️➡️ |
265
- | `1` | `"LeftStickUp"`<br/>`"LeftStickDown"` | **Left Stick (Up/Down)** | ⬆️⬇️ |
266
- | `2` | `"RightStickLeft"`<br/>`"RightStickRight"` | **Right Stick (Left/Right)** | ⬅️➡️ |
267
- | `3` | `"RightStickUp"`<br/>`"RightStickDown"` | **Right Stick (Up/Down)** | ⬆️⬇️ |
302
+ | `0` | `"LeftStickLeft"`<br/>`"LeftStickRight"` | **Left Stick (X-Axis)** | ⬅️➡️ |
303
+ | `1` | `"LeftStickUp"`<br/>`"LeftStickDown"` | **Left Stick (Y-Axis)** | ⬆️⬇️ |
304
+ | `2` | `"RightStickLeft"`<br/>`"RightStickRight"` | **Right Stick (X-Axis)** | ⬅️➡️ |
305
+ | `3` | `"RightStickUp"`<br/>`"RightStickDown"` | **Right Stick (Y-Axis)** | ⬆️⬇️ |
268
306
 
269
307
  > [!TIP]
270
- > Set the `joystick.threshold` option in `GamepadDevice.defaultOptions` to control when this is triggered.
308
+ > Set the `joystick.pressThreshold` option in `GamepadDevice.defaultOptions` to adjust event sensitivity.
271
309
 
272
310
  #### Gamepad Layouts
273
311
 
274
312
  ```ts
275
- gamepad.layout // "nintendo" | "xbox" | "playstation" | "logitech" | "steam" | "standard"
313
+ gamepad.layout // "xbox_one"
276
314
  ```
277
315
 
278
- Layout detection is **highly non-standard** across major browsers, it should generally be used for aesthetic
279
- improvements (e.g. showing [device-specific icons](https://thoseawesomeguys.com/prompts/)).
280
-
281
- There is some limited layout remapping support built-in for Nintendo controllers, which appear to be the
282
- only major brand controller that deviates from the standard.
283
-
284
- ##### Gamepad - Nintendo Layout Remapping
285
-
286
- > [!CAUTION]
287
- > ***Nintendo:** Both the labels and physical positions of the A,B,X,Y buttons are different
288
- > on Nintendo controllers.
289
- >
290
- > Set `GamepadDevice.defaultOptions.nintendoRemapMode` to apply the remapping as required.
291
- >
292
- > - `"physical"` _**(default)**_ &ndash; The A,B,X,Y button codes will refer the standard face button positions (Left=X, Top=Y, Bottom=A, Right=B).
293
- > - `"accurate"` &ndash; The A,B,X,Y button codes will refer to the exact Nintendo labels (Left=Y, Top=X, Bottom=B, Right=A).
294
- > - `"none"` &ndash; The A,B,X,Y button codes mapping stay at the default indices (Left=Y, Top=B, Bottom=X, Right=A).
295
- >
296
- > ```
297
- > standard nintendo nintendo nintendo
298
- > layout "physical" "accurate" "none"
299
- > reference (default)
300
- >
301
- > Y Y X B
302
- > X B X B Y A Y A
303
- > A A B X
304
- >
305
- > 3 3 2 1
306
- > 2 1 2 1 3 0 3 0
307
- > 0 0 1 2
308
- > ```
309
-
310
- You can manually override this per-gamepad, or for all gamepads:
311
-
312
- ```ts
313
- // set default
314
- GamepadDevice.defaultOptions.nintendoRemapMode = "none"
315
-
316
- // set for a single gamepad
317
- gamepad.options.nintendoRemapMode = "accurate"
318
- ```
316
+ Gamepad device layout reporting is a non-standard API, and should only be used for aesthetic
317
+ enhancements improvements (i.e. [display layout-specific icons](https://thoseawesomeguys.com/prompts/)).
319
318
 
320
319
  #### GamepadDevice Events
321
320
 
322
321
  | Event | Description | Payload |
323
322
  |---|---|---|
324
- | `"bind"` | `{name,button,buttonCode,device}` | A **named bind** button was pressed. |
323
+ | `"binddown"` | `{name,button,buttonCode,device}` | A **named bind** button was pressed. |
325
324
  | **Button presses:** | | |
326
- | `"A"` | `{button,buttonCode,device}` | Standard layout button `"A"` was pressed. Equivalent to `0`. |
327
- | `"B"` | `{button,buttonCode,device}` | Standard layout button `"B"` was pressed. Equivalent to `1`. |
328
- | `"X"` | `{button,buttonCode,device}` | Standard layout button `"X"` was pressed. Equivalent to `2`. |
325
+ | `"Face1"` | `{button,buttonCode,device}` | Standard layout button `"Face1"` was pressed. Equivalent to `0`. |
326
+ | `"Face2"` | `{button,buttonCode,device}` | Standard layout button `"Face2"` was pressed. Equivalent to `1`. |
327
+ | `"Face3"` | `{button,buttonCode,device}` | Standard layout button `"Face3"` was pressed. Equivalent to `2`. |
329
328
  | … | … | … |
330
329
  | **Button presses (no label):** | | |
331
330
  | `0` or `Button.A` | `{button,buttonCode,device}` | Button at offset `0` was pressed. |
@@ -340,17 +339,16 @@ You can add custom devices to the device manager so it will be polled togehter a
340
339
  ```ts
341
340
  import { type CustomDevice, InputDevice } from "pixijs-input-devices"
342
341
 
343
- export const myDevice: CustomDevice = {
344
- id: "on-screen-buttons",
342
+ export const onScreenButtonsDevice: CustomDevice = {
345
343
  type: "custom",
344
+ id: "OnScreen",
346
345
  meta: {},
347
-
348
- update: ( now: number ) => {
346
+ update: (now: number) => {
349
347
  // polling update
350
348
  }
351
- }
349
+ };
352
350
 
353
- InputDevice.add( myDevice )
351
+ InputDevice.add(onScreenButtonsDevice);
354
352
  ```
355
353
 
356
354
  ## Named Binds
@@ -369,8 +367,8 @@ InputDevice.keyboard.configureBinds({
369
367
 
370
368
  // all gamepads:
371
369
  GamepadDevice.configureDefaultBinds({
372
- jump: [ "A", "LeftStickUp" ],
373
- crouch: [ "B", "X", "RightTrigger" ],
370
+ jump: [ "Face1", "LeftStickUp" ],
371
+ crouch: [ "Face2", "Face3", "RightTrigger" ],
374
372
  toggleGraphics: [ "RightStickUp", "RightStickDown" ],
375
373
  })
376
374
  ```
@@ -381,11 +379,11 @@ These can then be used with either the real-time and event-based APIs.
381
379
 
382
380
  ```ts
383
381
  // listen to all devices:
384
- InputDevice.onBind( "toggleGraphics", ( e ) => toggleGraphics() )
382
+ InputDevice.onBindDown("toggleGraphics", (e) => toggleGraphics())
385
383
 
386
384
  // listen to specific devices:
387
- InputDevice.keyboard.onBind( "jump", ( e ) => doJump() )
388
- InputDevice.gamepads[0].onBind( "jump", ( e ) => doJump() )
385
+ InputDevice.keyboard.onBindDown("jump", (e) => doJump())
386
+ InputDevice.gamepads[0].onBindDown("jump", (e) => doJump())
389
387
  ```
390
388
 
391
389
  #### Real-time:
@@ -394,19 +392,19 @@ InputDevice.gamepads[0].onBind( "jump", ( e ) => doJump() )
394
392
  let jump = false, crouch = false, moveX = 0
395
393
 
396
394
  const keyboard = InputDevice.keyboard
397
- if ( keyboard.pressedBind( "jump" ) ) jump = true
398
- if ( keyboard.pressedBind( "crouch" ) ) crouch = true
399
- if ( keyboard.key.ArrowLeft ) moveX = -1
400
- else if ( keyboard.key.ArrowRight ) moveX = 1
395
+ if (keyboard.bindDown("jump")) jump = true
396
+ if (keyboard.bindDown("crouch")) crouch = true
397
+ if (keyboard.key.ArrowLeft) moveX = -1
398
+ else if (keyboard.key.ArrowRight) moveX = 1
401
399
 
402
- for ( const gamepad of InputDevice.gamepads ) {
403
- if ( gamepad.pressedBind( "jump" ) ) jump = true
404
- if ( gamepad.pressedBind( "crouch" ) ) crouch = true
400
+ for (const gamepad of InputDevice.gamepads) {
401
+ if (gamepad.bindDown("jump")) jump = true
402
+ if (gamepad.bindDown("crouch")) crouch = true
405
403
 
406
404
  // gamepads have additional analog inputs
407
405
  // we're going to apply these only if touched
408
- if ( gamepad.leftJoystick.x != 0 ) moveX = gamepad.leftJoystick.x
409
- if ( gamepad.leftTrigger > 0 ) moveX *= ( 1 - gamepad.leftTrigger )
406
+ if (gamepad.leftJoystick.x != 0) moveX = gamepad.leftJoystick.x
407
+ if (gamepad.leftTrigger > 0) moveX *= (1 - gamepad.leftTrigger)
410
408
  }
411
409
  ```
412
410
 
@@ -414,19 +412,34 @@ for ( const gamepad of InputDevice.gamepads ) {
414
412
 
415
413
  _Traverse a UI using input devices._
416
414
 
415
+ ### Quick setup
416
+
417
+ Set up navigation once using:
418
+
417
419
  ```ts
418
- UINavigation.configureWithRoot( app.stage ) // (or any Container)
420
+ UINavigation.configureWithRoot(app.stage) // any root container
421
+ registerPixiJSNavigationMixin(PIXI.Container)
419
422
  ```
420
423
 
421
- You can manually take control of navigation using:
424
+ Navigation should now work automatically if your buttons handle these events:
422
425
 
423
- ```ts
424
- // take control
425
- UINavigation.pushResponder( myModalView )
426
+ - `"pointerdown"` &ndash; i.e. Trigger / show press effect
426
427
 
427
- // relinquish control
428
- UINavigation.popResponder()
429
- ```
428
+ But in order to really make use, you should also set:
429
+
430
+ - `"pointerover"` &ndash; i.e. Select / show hover effect
431
+ - `"pointerout"` &ndash; i.e. Deselect / reset
432
+
433
+ > [!TIP]
434
+ > 🖱️ **Seamless navigation:** Manually set `UINavigation.focusTarget = <target>`
435
+ > inside any `"pointerover"` handlers to allow mouse/pointers to update the
436
+ > navigation context for all devices.
437
+
438
+ > [!TIP]
439
+ > **Auto-focus:** Set a container's `navigationPriority` to a value above `0`
440
+ > to become the default selection in a context.
441
+
442
+ ### How it works
430
443
 
431
444
  The Navigation API is centered around the **UINavigation** manager, which
432
445
  receives navigation intents from devices and forwards it to the UI context.
@@ -450,21 +463,21 @@ When a navigation intent is **not** handled manually by a responder, it is handl
450
463
  |`"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>|
451
464
  |`"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>|
452
465
 
453
- Container event | Description | Compatibility
454
- -----------------|--------------------------------------------------------
455
- `"devicedown"` | Target was triggered. | `"pointerdown"`, `"mousedown"`
456
- `"deviceover"` | Target became focused. | `"pointerover"`, `"mouseover"`
457
- `"deviceout"` | Target lost focus. | `"pointerout"`, `"mouseout"`
466
+ | Container event | Description | Compatibility
467
+ |-----------------|-------------|------------------------------------------
468
+ | `"devicedown"` | Target was triggered. | `"pointerdown"`, `"mousedown"`
469
+ | `"deviceover"` | Target became focused. | `"pointerover"`, `"mouseover"`
470
+ | `"deviceout"` | Target lost focus. | `"pointerout"`, `"mouseout"`
458
471
 
459
472
  ### Container Navigatability
460
473
 
461
474
  Containers are extended with a few properties/accessors:
462
475
 
463
- Container properties | type | default | description
464
- ---------------------|------|---------|--------------
465
- `isNavigatable` | `get(): boolean` | `false` | returns `true` if `navigationMode` is set to `"target"`, or is `"auto"` and a `"pointerdown"` or `"mousedown"` event handler is registered.
466
- `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.
467
- `navigationPriority` | `number` | `0` | The priority relative to other navigation items in this group.
476
+ | Container properties | type | default | description
477
+ |---------------------|------|---------|--------------
478
+ | `isNavigatable` | `get(): boolean` | `false` | returns `true` if `navigationMode` is set to `"target"`, |or is `"auto"` and a `"pointerdown"` or `"mousedown"` event handler is registered.
479
+ | `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.
480
+ | `navigationPriority` | `number` | `0` | The priority relative to other navigation items in this group.
468
481
 
469
482
  > [!NOTE]
470
483
  > **isNavigatable:** By default, any element with `"pointerdown"` or `"mousedown"` handlers is navigatable.
@@ -472,8 +485,7 @@ Container properties | type | default | description
472
485
  > [!WARNING]
473
486
  > **Fallback Hover Effect:** If there is no `"pointerover"` or `"mouseover"` handler detected on a container, `UINavigation`
474
487
  > will apply abasic alpha effect to the selected item to indicate which container is currently the navigation target. This
475
- > can be disabled by setting `UINavigation.options.useFallbackHoverEffect` to `false`.
476
-
488
+ > can be disabled by setting `UINavigation.options.enableFallbackOverEffect` to `false`.
477
489
 
478
490
  ### Default Binds
479
491
 
@@ -481,12 +493,24 @@ The keyboard and gamepad devices are preconfigured with the following binds, fee
481
493
 
482
494
  Navigation Intent Bind | Keyboard | Gamepad
483
495
  ---|---|---
484
- `"navigate.left"` | "ArrowLeft", "KeyA" | "DPadLeft", "LeftStickLeft"
485
- `"navigate.right"` | "ArrowRight", "KeyD" | "DPadRight", "LeftStickRight"
486
- `"navigate.up"` | "ArrowUp", "KeyW" | "DPadUp", "LeftStickUp"
487
- `"navigate.down"` | "ArrowDown", "KeyS" | "DPadDown", "LeftStickDown"
488
- `"navigate.trigger"` | "Enter", "Space" | "A"
489
- `"navigate.back"` | "Escape", "Backspace" | "B", "Back"
496
+ `"navigate.left"` | "ArrowLeft", "KeyA" | "DpadLeft", "LeftStickLeft"
497
+ `"navigate.right"` | "ArrowRight", "KeyD" | "DpadRight", "LeftStickRight"
498
+ `"navigate.up"` | "ArrowUp", "KeyW" | "DpadUp", "LeftStickUp"
499
+ `"navigate.down"` | "ArrowDown", "KeyS" | "DpadDown", "LeftStickDown"
500
+ `"navigate.trigger"` | "Enter", "Space" | "Face1"
501
+ `"navigate.back"` | "Escape", "Backspace" | "Face2", "Back"
502
+
503
+ ### Manual control for submenus & modal views
504
+
505
+ You can manually take control of navigation using:
506
+
507
+ ```ts
508
+ // take control
509
+ UINavigation.pushResponder(myModalView)
510
+
511
+ // relinquish control
512
+ UINavigation.popResponder()
513
+ ```
490
514
 
491
515
  ## Advanced usage
492
516
 
@@ -502,9 +526,9 @@ InputDevice.on("deviceconnected", ({ device }) =>
502
526
  device.meta.localPlayerId = 123
503
527
  )
504
528
 
505
- for ( const device of InputDevice.devices )
529
+ for (const device of InputDevice.devices)
506
530
  {
507
- if ( device.meta.localPlayerId === 123 )
531
+ if (device.meta.localPlayerId === 123)
508
532
  {
509
533
  // use assigned input device!
510
534
  }
@@ -517,26 +541,29 @@ You can easily map an on-screen input device using the `CustomDevice` interface.
517
541
 
518
542
  ```ts
519
543
  export class OnScreenInputContainer extends Container implements CustomDevice {
520
- id = "onscreen";
521
- type = "custom" as const;
522
- meta: Record<string, any> = {};
544
+ id = "onscreen"
545
+ type = "custom" as const
546
+ meta: Record<string, any> = {}
523
547
 
524
548
  inputs = {
525
549
  moveX: 0.0
526
550
  jump: false,
527
551
  }
528
552
 
529
- update( now )
553
+ update(now)
530
554
  {
531
- this.moveX = this._virtualJoystick.x
532
- this.jump = this._jumpButton.isTouching()
555
+ this.inputs.moveX = this._virtualJoystick.x
556
+ this.inputs.jump = this._jumpButton.isTouching()
533
557
  }
558
+
559
+ // e.g. disable named binds for onscreen joysticks:
560
+ bindDown(name){ return false }
534
561
  }
535
562
 
536
- const onscreen = new OnScreenInputContainer();
563
+ const onscreen = new OnScreenInputContainer()
537
564
 
538
- InputDevice.add( onscreen )
539
- InputDevice.remove( onscreen )
565
+ InputDevice.add(onscreen)
566
+ InputDevice.remove(onscreen)
540
567
  ```
541
568
 
542
569
  ### Two Users; One Keyboard
@@ -558,32 +585,32 @@ InputDevice.keyboard.configureBinds({
558
585
  p2_jump: [ "ArrowUp" ],
559
586
  p2_defend: [ "ArrowDown" ],
560
587
  p2_left: [ "ArrowLeft" ],
561
- p2_right: [ "ArrowRight" ],
588
+ p2_right: [ "ArrowRight" ]
562
589
  })
563
590
  ```
564
591
 
565
592
  and then switch groups depending on the mode:
566
593
 
567
594
  ```ts
568
- if ( gameMode === "multiplayer" )
595
+ if (gameMode === "multiplayer")
569
596
  {
570
- player1.jump = device.pressedBind( "p1_jump" )
571
- player1.defend = device.pressedBind( "p1_defend" )
572
- player1.moveX += device.pressedBind( "p1_left" ) ? -1 : 0
573
- player1.moveX += device.pressedBind( "p1_right" ) ? 1 : 0
574
-
575
- player2.jump = device.pressedBind( "p2_jump" )
576
- player2.defend = device.pressedBind( "p2_defend" )
577
- player2.moveX += device.pressedBind( "p2_left" ) ? -1 : 0
578
- player2.moveX += device.pressedBind( "p2_right" ) ? 1 : 0
597
+ player1.jump = device.bindDown("p1_jump")
598
+ player1.defend = device.bindDown("p1_defend")
599
+ player1.moveX += device.bindDown("p1_left") ? -1 : 0
600
+ player1.moveX += device.bindDown("p1_right") ? 1 : 0
601
+
602
+ player2.jump = device.bindDown("p2_jump")
603
+ player2.defend = device.bindDown("p2_defend")
604
+ player2.moveX += device.bindDown("p2_left") ? -1 : 0
605
+ player2.moveX += device.bindDown("p2_right") ? 1 : 0
579
606
  }
580
607
  else
581
608
  {
582
- player1.jump = device.pressedBind( "jump" )
583
- player1.defend = device.pressedBind( "defend" )
584
- player1.moveX += device.pressedBind( "left" ) ? -1 : 0
585
- player1.moveX += device.pressedBind( "right" ) ? 1 : 0
609
+ player1.jump = device.bindDown("jump")
610
+ player1.defend = device.bindDown("defend")
611
+ player1.moveX += device.bindDown("left") ? -1 : 0
612
+ player1.moveX += device.bindDown("right") ? 1 : 0
586
613
 
587
- updateComputerPlayerInput( player2 )
614
+ updateComputerPlayerInput(player2)
588
615
  }
589
616
  ```