@pmndrs/viverse 0.2.0 → 0.2.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.
Files changed (48) hide show
  1. package/dist/animation/bone-map.d.ts +4 -0
  2. package/dist/animation/bone-map.js +11 -0
  3. package/dist/animation/default.d.ts +9 -1
  4. package/dist/animation/default.js +16 -9
  5. package/dist/animation/index.d.ts +8 -13
  6. package/dist/animation/index.js +61 -39
  7. package/dist/animation/mask.d.ts +4 -1
  8. package/dist/animation/mask.js +50 -0
  9. package/dist/camera.d.ts +6 -2
  10. package/dist/camera.js +22 -7
  11. package/dist/input/action.d.ts +41 -0
  12. package/dist/input/action.js +97 -0
  13. package/dist/input/index.d.ts +12 -26
  14. package/dist/input/index.js +17 -70
  15. package/dist/input/keyboard.d.ts +28 -5
  16. package/dist/input/keyboard.js +83 -69
  17. package/dist/input/pointer-capture.d.ts +5 -10
  18. package/dist/input/pointer-capture.js +9 -28
  19. package/dist/input/pointer-lock.d.ts +5 -10
  20. package/dist/input/pointer-lock.js +7 -28
  21. package/dist/input/screen-joystick.d.ts +11 -6
  22. package/dist/input/screen-joystick.js +31 -34
  23. package/dist/input/screen-jump-button.d.ts +2 -4
  24. package/dist/input/screen-jump-button.js +7 -12
  25. package/dist/model/index.d.ts +1 -2
  26. package/dist/simple-character/apply-input-options.d.ts +2 -0
  27. package/dist/simple-character/apply-input-options.js +28 -0
  28. package/dist/simple-character/index.d.ts +21 -6
  29. package/dist/simple-character/index.js +12 -17
  30. package/dist/simple-character/state/jump-down.js +2 -1
  31. package/dist/simple-character/state/jump-forward.js +4 -2
  32. package/dist/simple-character/state/jump-loop.js +2 -1
  33. package/dist/simple-character/state/jump-start.js +2 -2
  34. package/dist/simple-character/state/jump-up.js +4 -2
  35. package/dist/simple-character/state/movement.js +12 -8
  36. package/dist/simple-character/update-input-velocity.d.ts +1 -2
  37. package/dist/simple-character/update-input-velocity.js +4 -4
  38. package/dist/utils.d.ts +5 -5
  39. package/dist/utils.js +15 -6
  40. package/package.json +2 -2
  41. package/dist/animation/bvh.d.ts +0 -4
  42. package/dist/animation/bvh.js +0 -8
  43. package/dist/animation/fbx.d.ts +0 -4
  44. package/dist/animation/fbx.js +0 -8
  45. package/dist/animation/gltf.d.ts +0 -3
  46. package/dist/animation/gltf.js +0 -8
  47. package/dist/animation/vrma.d.ts +0 -3
  48. package/dist/animation/vrma.js +0 -8
@@ -1,77 +1,24 @@
1
- export class InputSystem {
2
- options;
3
- inputs = [];
4
- constructor(options = {}) {
5
- this.options = options;
6
- }
7
- add(input) {
8
- this.inputs.push(input);
9
- }
10
- remove(input) {
11
- const index = this.inputs.indexOf(input);
12
- if (index === -1) {
13
- return;
14
- }
15
- this.inputs.splice(index, 1);
16
- }
17
- dispose() {
18
- this.inputs.forEach((input) => input.dispose?.());
19
- this.inputs.length = 0;
20
- }
21
- get(field) {
22
- let current;
23
- for (const input of this.inputs) {
24
- const result = input.get(field, this.options);
25
- if (result == null) {
26
- continue;
27
- }
28
- if (current == undefined) {
29
- current = result;
30
- continue;
31
- }
32
- current = field.combine(current, result);
33
- }
34
- return current ?? field.default;
1
+ import { EventAction, StateAction, DeltaAction } from './action.js';
2
+ export function BooleanOr(...values) {
3
+ let value = false;
4
+ for (let i = 0; i < values.length; i++) {
5
+ value ||= values[i];
35
6
  }
7
+ return value;
36
8
  }
37
- export const MoveForwardField = {
38
- default: 0,
39
- combine: Math.max,
40
- };
41
- export const MoveBackwardField = {
42
- default: 0,
43
- combine: Math.max,
44
- };
45
- export const MoveLeftField = {
46
- default: 0,
47
- combine: Math.max,
48
- };
49
- export const MoveRightField = {
50
- default: 0,
51
- combine: Math.max,
52
- };
53
- export const LastTimeJumpPressedField = {
54
- default: null,
55
- combine: (v1, v2) => (v1 == null && v2 == null ? null : Math.min(v1 ?? Infinity, v2 ?? Infinity)),
56
- };
57
- export const RunField = {
58
- default: false,
59
- combine: (v1, v2) => v1 || v2,
60
- };
61
- export const DeltaZoomField = {
62
- default: 0,
63
- combine: Math.max,
64
- };
65
- export const DeltaYawField = {
66
- default: 0,
67
- combine: Math.max,
68
- };
69
- export const DeltaPitchField = {
70
- default: 0,
71
- combine: Math.max,
72
- };
9
+ const sum = (...values) => values.reduce((a, b) => a + b, 0);
10
+ export const MoveForwardAction = new StateAction(Math.max, 0);
11
+ export const MoveBackwardAction = new StateAction(Math.max, 0);
12
+ export const MoveLeftAction = new StateAction(Math.max, 0);
13
+ export const MoveRightAction = new StateAction(Math.max, 0);
14
+ export const RunAction = new StateAction(BooleanOr, false);
15
+ export const JumpAction = new EventAction();
16
+ export const ZoomAction = new DeltaAction(sum, 0);
17
+ export const RotateYawAction = new DeltaAction(sum, 0);
18
+ export const RotatePitchAction = new DeltaAction(sum, 0);
73
19
  export * from './pointer-lock.js';
74
20
  export * from './pointer-capture.js';
75
21
  export * from './keyboard.js';
76
22
  export * from './screen-joystick.js';
77
23
  export * from './screen-jump-button.js';
24
+ export * from './action.js';
@@ -1,4 +1,18 @@
1
- import { Input, InputField } from './index.js';
1
+ import { EventAction, StateAction } from './index.js';
2
+ export declare class KeyboardInput {
3
+ private readonly domElement;
4
+ private readonly abortController;
5
+ options: Partial<{
6
+ keys: Array<string>;
7
+ }>;
8
+ abortSignal: AbortSignal;
9
+ private readonly pressedKeys;
10
+ constructor(domElement: HTMLElement);
11
+ bindEvent(action: EventAction): void;
12
+ bindValue(action: StateAction<boolean>): void;
13
+ bindValue<T>(action: StateAction<T>, map: (value: boolean) => T): void;
14
+ dispose(): void;
15
+ }
2
16
  export type LocomotionKeyboardInputOptions = {
3
17
  keyboardMoveForwardKeys?: Array<string>;
4
18
  keyboardMoveBackwardKeys?: Array<string>;
@@ -7,10 +21,19 @@ export type LocomotionKeyboardInputOptions = {
7
21
  keyboardRunKeys?: Array<string>;
8
22
  keyboardJumpKeys?: Array<string>;
9
23
  };
10
- export declare class LocomotionKeyboardInput implements Input<LocomotionKeyboardInputOptions> {
11
- private readonly abortController;
12
- private readonly keyState;
24
+ export declare const DefaultMoveForwardKeys: string[];
25
+ export declare const DefaultMoveBackwardKeys: string[];
26
+ export declare const DefaultMoveLeftKeys: string[];
27
+ export declare const DefaultMoveRightKeys: string[];
28
+ export declare const DefaultRunKeys: string[];
29
+ export declare const DefaultJumpKeys: string[];
30
+ export declare class LocomotionKeyboardInput {
31
+ forward: KeyboardInput;
32
+ left: KeyboardInput;
33
+ right: KeyboardInput;
34
+ backward: KeyboardInput;
35
+ jump: KeyboardInput;
36
+ run: KeyboardInput;
13
37
  constructor(domElement: HTMLElement);
14
- get<T>(field: InputField<T>, options: LocomotionKeyboardInputOptions): T | undefined;
15
38
  dispose(): void;
16
39
  }
@@ -1,82 +1,96 @@
1
- import { MoveForwardField, MoveBackwardField, MoveLeftField, MoveRightField, LastTimeJumpPressedField, RunField, } from './index.js';
2
- const DefaultMoveForwardKeys = ['KeyW'];
3
- const DefaultMoveBackwardKeys = ['KeyS'];
4
- const DefaultMoveLeftKeys = ['KeyA'];
5
- const DefaultMoveRightKeys = ['KeyD'];
6
- const DefaultRunKeys = ['ShiftRight', 'ShiftLeft'];
7
- const DefaultJumpKeys = ['Space'];
8
- export class LocomotionKeyboardInput {
1
+ import { JumpAction, MoveBackwardAction, MoveForwardAction, MoveLeftAction, MoveRightAction, RunAction, } from './index.js';
2
+ export class KeyboardInput {
3
+ domElement;
9
4
  abortController = new AbortController();
10
- keyState = new Map();
5
+ options = {};
6
+ abortSignal = this.abortController.signal;
7
+ pressedKeys = new Set();
11
8
  constructor(domElement) {
12
- domElement.tabIndex = 0;
13
- domElement.addEventListener('keydown', (event) => {
14
- let state = this.keyState.get(event.code);
15
- const now = performance.now() / 1000;
16
- if (state == null) {
17
- this.keyState.set(event.code, (state = { pressTime: now }));
9
+ this.domElement = domElement;
10
+ }
11
+ bindEvent(action) {
12
+ const isWatched = (e) => this.options.keys == null || this.options.keys.length === 0 || this.options.keys.includes(e.code);
13
+ this.domElement.addEventListener('keydown', (e) => {
14
+ if (!isWatched(e) || e.repeat) {
15
+ return;
16
+ }
17
+ action.emit({});
18
+ }, { signal: this.abortController.signal });
19
+ }
20
+ bindValue(action, map) {
21
+ const writer = action.createWriter(this.abortSignal);
22
+ const isWatched = (e) => this.options.keys == null || this.options.keys.length === 0 || this.options.keys.includes(e.code);
23
+ let isPressed = false;
24
+ const write = (value) => {
25
+ if (isPressed === value) {
26
+ return;
27
+ }
28
+ isPressed = value;
29
+ writer.write(map?.(value) ?? value);
30
+ };
31
+ this.domElement.tabIndex = 0;
32
+ this.domElement.addEventListener('keydown', (e) => {
33
+ if (!isWatched(e)) {
34
+ return;
18
35
  }
19
- else {
20
- state.pressTime = now;
36
+ const wasEmpty = this.pressedKeys.size === 0;
37
+ this.pressedKeys.add(e.code);
38
+ if (wasEmpty && this.pressedKeys.size > 0) {
39
+ write(true);
21
40
  }
22
- }, {
23
- signal: this.abortController.signal,
24
- });
25
- domElement.addEventListener('keyup', (event) => {
26
- let state = this.keyState.get(event.code);
27
- const now = performance.now() / 1000;
28
- if (state == null) {
29
- this.keyState.set(event.code, (state = { releaseTime: now }));
41
+ }, { signal: this.abortController.signal });
42
+ this.domElement.addEventListener('keyup', (e) => {
43
+ if (!isWatched(e)) {
44
+ return;
30
45
  }
31
- else {
32
- state.releaseTime = now;
46
+ if (this.pressedKeys.delete(e.code) && this.pressedKeys.size === 0) {
47
+ write(false);
33
48
  }
34
- }, {
35
- signal: this.abortController.signal,
36
- });
49
+ }, { signal: this.abortController.signal });
37
50
  // Handle focus loss to clear pressed keys
38
- domElement.addEventListener('blur', () => this.keyState.clear(), {
39
- signal: this.abortController.signal,
40
- });
41
- }
42
- get(field, options) {
43
- if (field === LastTimeJumpPressedField) {
44
- const jumpKeys = options.keyboardJumpKeys ?? DefaultJumpKeys;
45
- const pressed = jumpKeys
46
- .map((key) => this.keyState.get(key)?.pressTime ?? null)
47
- .filter((t) => t != null);
48
- return (pressed.length > 0 ? Math.max(...pressed) : null);
49
- }
50
- let keys;
51
- switch (field) {
52
- case MoveForwardField:
53
- keys = options.keyboardMoveForwardKeys ?? DefaultMoveForwardKeys;
54
- break;
55
- case MoveBackwardField:
56
- keys = options.keyboardMoveBackwardKeys ?? DefaultMoveBackwardKeys;
57
- break;
58
- case MoveLeftField:
59
- keys = options.keyboardMoveLeftKeys ?? DefaultMoveLeftKeys;
60
- break;
61
- case MoveRightField:
62
- keys = options.keyboardMoveRightKeys ?? DefaultMoveRightKeys;
63
- break;
64
- case RunField:
65
- keys = options.keyboardRunKeys ?? DefaultRunKeys;
66
- break;
67
- }
68
- if (keys == null) {
69
- return undefined;
70
- }
71
- return keys.some((key) => {
72
- const state = this.keyState.get(key);
73
- if (state?.pressTime == null) {
74
- return false;
51
+ this.domElement.addEventListener('blur', () => {
52
+ if (this.pressedKeys.size > 0) {
53
+ this.pressedKeys.clear();
54
+ write(false);
75
55
  }
76
- return state.releaseTime == null || state.pressTime > state.releaseTime;
77
- });
56
+ }, { signal: this.abortController.signal });
78
57
  }
79
58
  dispose() {
80
59
  this.abortController.abort();
81
60
  }
82
61
  }
62
+ export const DefaultMoveForwardKeys = ['KeyW'];
63
+ export const DefaultMoveBackwardKeys = ['KeyS'];
64
+ export const DefaultMoveLeftKeys = ['KeyA'];
65
+ export const DefaultMoveRightKeys = ['KeyD'];
66
+ export const DefaultRunKeys = ['ShiftRight', 'ShiftLeft'];
67
+ export const DefaultJumpKeys = ['Space'];
68
+ export class LocomotionKeyboardInput {
69
+ forward;
70
+ left;
71
+ right;
72
+ backward;
73
+ jump;
74
+ run;
75
+ constructor(domElement) {
76
+ this.forward = new KeyboardInput(domElement);
77
+ this.forward.options.keys = DefaultMoveForwardKeys;
78
+ this.forward.bindValue(MoveForwardAction, (isPressed) => (isPressed ? 1 : 0));
79
+ this.left = new KeyboardInput(domElement);
80
+ this.left.options.keys = DefaultMoveLeftKeys;
81
+ this.left.bindValue(MoveLeftAction, (isPressed) => (isPressed ? 1 : 0));
82
+ this.right = new KeyboardInput(domElement);
83
+ this.right.options.keys = DefaultMoveRightKeys;
84
+ this.right.bindValue(MoveRightAction, (isPressed) => (isPressed ? 1 : 0));
85
+ this.backward = new KeyboardInput(domElement);
86
+ this.backward.options.keys = DefaultMoveBackwardKeys;
87
+ this.backward.bindValue(MoveBackwardAction, (isPressed) => (isPressed ? 1 : 0));
88
+ this.jump = new KeyboardInput(domElement);
89
+ this.jump.options.keys = DefaultJumpKeys;
90
+ this.jump.bindEvent(JumpAction);
91
+ this.run = new KeyboardInput(domElement);
92
+ this.run.options.keys = DefaultRunKeys;
93
+ this.run.bindValue(RunAction);
94
+ }
95
+ dispose() { }
96
+ }
@@ -1,20 +1,15 @@
1
- import { Input, InputField } from './index.js';
2
- export type PointerCaptureInputOptions = {
3
- pointerCaptureRotationSpeed?: number;
4
- pointerCaptureZoomSpeed?: number;
5
- };
6
1
  /**
7
2
  * @requires to manually execute `domElement.setPointerCapture(pointerId)` on pointerdown
8
3
  */
9
- export declare class PointerCaptureInput implements Input<PointerCaptureInputOptions> {
4
+ export declare class PointerCaptureInput {
10
5
  private readonly domElement;
11
6
  private readonly abortController;
12
- private deltaZoom;
13
- private deltaYaw;
14
- private deltaPitch;
15
7
  private activePointers;
16
8
  private lastPinchDist;
9
+ options: {
10
+ rotationSpeed?: number;
11
+ zoomSpeed?: number;
12
+ };
17
13
  constructor(domElement: HTMLElement);
18
- get<T>(field: InputField<T>, options: PointerCaptureInputOptions): T | undefined;
19
14
  dispose(): void;
20
15
  }
@@ -1,15 +1,13 @@
1
- import { DeltaPitchField, DeltaYawField, DeltaZoomField } from './index.js';
1
+ import { RotatePitchAction, RotateYawAction, ZoomAction } from './index.js';
2
2
  /**
3
3
  * @requires to manually execute `domElement.setPointerCapture(pointerId)` on pointerdown
4
4
  */
5
5
  export class PointerCaptureInput {
6
6
  domElement;
7
7
  abortController = new AbortController();
8
- deltaZoom = 0;
9
- deltaYaw = 0;
10
- deltaPitch = 0;
11
8
  activePointers = new Map();
12
9
  lastPinchDist = null;
10
+ options = {};
13
11
  constructor(domElement) {
14
12
  this.domElement = domElement;
15
13
  domElement.addEventListener('pointerdown', (event) => {
@@ -31,14 +29,16 @@ export class PointerCaptureInput {
31
29
  const pts = Array.from(this.activePointers.values());
32
30
  if (this.lastPinchDist != null) {
33
31
  const d = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
34
- this.deltaZoom += this.lastPinchDist - d;
32
+ const zoomSpeed = this.options.zoomSpeed ?? 0.0001;
33
+ ZoomAction.write((this.lastPinchDist - d) * zoomSpeed);
35
34
  this.lastPinchDist = d;
36
35
  }
37
36
  event.preventDefault();
38
37
  return;
39
38
  }
40
- this.deltaYaw -= event.movementX / window.innerHeight;
41
- this.deltaPitch -= event.movementY / window.innerHeight;
39
+ const rotationSpeed = this.options.rotationSpeed ?? 0.4;
40
+ RotateYawAction.write(-(event.movementX / window.innerHeight) * rotationSpeed);
41
+ RotatePitchAction.write(-(event.movementY / window.innerHeight) * rotationSpeed);
42
42
  }, {
43
43
  signal: this.abortController.signal,
44
44
  });
@@ -62,31 +62,12 @@ export class PointerCaptureInput {
62
62
  });
63
63
  domElement.addEventListener('wheel', (event) => {
64
64
  event.preventDefault();
65
- this.deltaZoom += event.deltaY;
65
+ const zoomSpeed = this.options.zoomSpeed ?? 0.0001;
66
+ ZoomAction.write(event.deltaY * zoomSpeed);
66
67
  }, {
67
68
  signal: this.abortController.signal,
68
69
  });
69
70
  }
70
- get(field, options) {
71
- const rotationSpeed = options.pointerCaptureRotationSpeed ?? 0.4;
72
- const zoomSpeed = options.pointerCaptureZoomSpeed ?? 0.0001;
73
- let result;
74
- switch (field) {
75
- case DeltaPitchField:
76
- result = (this.deltaPitch * rotationSpeed);
77
- this.deltaPitch = 0;
78
- break;
79
- case DeltaYawField:
80
- result = (this.deltaYaw * rotationSpeed);
81
- this.deltaYaw = 0;
82
- break;
83
- case DeltaZoomField:
84
- result = (this.deltaZoom * zoomSpeed);
85
- this.deltaZoom = 0;
86
- break;
87
- }
88
- return result;
89
- }
90
71
  dispose() {
91
72
  this.abortController.abort();
92
73
  }
@@ -1,17 +1,12 @@
1
- import { Input, InputField } from './index.js';
2
- export type PointerLockInputOptions = {
3
- pointerLockRotationSpeed?: number;
4
- pointerLockZoomSpeed?: number;
5
- };
6
1
  /**
7
2
  * @requires to manually execute `domElement.requestPointerLock()`
8
3
  */
9
- export declare class PointerLockInput implements Input<PointerLockInputOptions> {
4
+ export declare class PointerLockInput {
10
5
  private readonly abortController;
11
- private deltaZoom;
12
- private deltaYaw;
13
- private deltaPitch;
6
+ options: {
7
+ rotationSpeed?: number;
8
+ zoomSpeed?: number;
9
+ };
14
10
  constructor(domElement: HTMLElement);
15
- get<T>(field: InputField<T>, options: PointerLockInputOptions): T | undefined;
16
11
  dispose(): void;
17
12
  }
@@ -1,21 +1,19 @@
1
- import { DeltaPitchField, DeltaYawField, DeltaZoomField } from './index.js';
1
+ import { RotatePitchAction, RotateYawAction, ZoomAction } from './index.js';
2
2
  /**
3
3
  * @requires to manually execute `domElement.requestPointerLock()`
4
4
  */
5
5
  export class PointerLockInput {
6
6
  abortController = new AbortController();
7
- deltaZoom = 0;
8
- deltaYaw = 0;
9
- deltaPitch = 0;
7
+ options = {};
10
8
  constructor(domElement) {
11
9
  domElement.addEventListener('pointermove', (event) => {
12
10
  if (document.pointerLockElement != domElement) {
13
11
  return;
14
12
  }
15
- // Compute based on domElement bounds instead of window.innerHeight
16
13
  const rect = domElement.getBoundingClientRect();
17
- this.deltaYaw -= event.movementX / rect.height;
18
- this.deltaPitch -= event.movementY / rect.height;
14
+ const rotationSpeed = this.options.rotationSpeed ?? 0.4;
15
+ RotateYawAction.write(-(event.movementX / rect.height) * rotationSpeed);
16
+ RotatePitchAction.write(-(event.movementY / rect.height) * rotationSpeed);
19
17
  }, {
20
18
  signal: this.abortController.signal,
21
19
  });
@@ -23,32 +21,13 @@ export class PointerLockInput {
23
21
  if (document.pointerLockElement != domElement) {
24
22
  return;
25
23
  }
26
- this.deltaZoom += event.deltaY;
24
+ const zoomSpeed = this.options.zoomSpeed ?? 0.0001;
25
+ ZoomAction.write(event.deltaY * zoomSpeed);
27
26
  event.preventDefault();
28
27
  }, {
29
28
  signal: this.abortController.signal,
30
29
  });
31
30
  }
32
- get(field, options) {
33
- const rotationSpeed = options.pointerLockRotationSpeed ?? 0.4;
34
- const zoomSpeed = options.pointerLockZoomSpeed ?? 0.0001;
35
- let result;
36
- switch (field) {
37
- case DeltaPitchField:
38
- result = (this.deltaPitch * rotationSpeed);
39
- this.deltaPitch = 0;
40
- break;
41
- case DeltaYawField:
42
- result = (this.deltaYaw * rotationSpeed);
43
- this.deltaYaw = 0;
44
- break;
45
- case DeltaZoomField:
46
- result = (this.deltaZoom * zoomSpeed);
47
- this.deltaZoom = 0;
48
- break;
49
- }
50
- return result;
51
- }
52
31
  dispose() {
53
32
  this.abortController.abort();
54
33
  }
@@ -1,17 +1,22 @@
1
- import { Input, InputField } from './index.js';
2
1
  export type ScreenJoystickInputOptions = {
3
2
  screenJoystickRunDistancePx?: number;
4
3
  screenJoystickDeadZonePx?: number;
5
4
  };
6
- export declare class ScreenJoystickInput implements Input<ScreenJoystickInputOptions> {
5
+ export declare class ScreenJoystickInput {
6
+ private readonly abortController;
7
7
  readonly root: HTMLDivElement;
8
8
  private readonly handle;
9
9
  private pointerId;
10
- private distanceToCenter;
11
- private clampedX;
12
- private clampedY;
10
+ private forwardWriter;
11
+ private backwardWriter;
12
+ private leftWriter;
13
+ private rightWriter;
14
+ private runWriter;
15
+ options: {
16
+ runDistancePx?: number;
17
+ deadZonePx?: number;
18
+ };
13
19
  constructor(domElement: HTMLElement);
14
- get<T>(field: InputField<T>, options: ScreenJoystickInputOptions): T | undefined;
15
20
  dispose(): void;
16
21
  private updateHandle;
17
22
  private resetHandle;
@@ -1,14 +1,18 @@
1
- import { MoveForwardField, MoveBackwardField, MoveLeftField, MoveRightField, RunField, } from './index.js';
1
+ import { MoveForwardAction, MoveBackwardAction, MoveLeftAction, MoveRightAction, RunAction } from './index.js';
2
2
  const DefaultDeadZonePx = 24;
3
3
  const DefaultRunDistancePx = 46;
4
4
  const JoystickRadius = 56;
5
5
  export class ScreenJoystickInput {
6
+ abortController = new AbortController();
6
7
  root;
7
8
  handle;
8
9
  pointerId;
9
- distanceToCenter = 0;
10
- clampedX = 0;
11
- clampedY = 0;
10
+ forwardWriter = MoveForwardAction.createWriter(this.abortController.signal);
11
+ backwardWriter = MoveBackwardAction.createWriter(this.abortController.signal);
12
+ leftWriter = MoveLeftAction.createWriter(this.abortController.signal);
13
+ rightWriter = MoveRightAction.createWriter(this.abortController.signal);
14
+ runWriter = RunAction.createWriter(this.abortController.signal);
15
+ options = {};
12
16
  constructor(domElement) {
13
17
  const parent = domElement.parentElement ?? domElement;
14
18
  const joy = document.createElement('div');
@@ -78,43 +82,36 @@ export class ScreenJoystickInput {
78
82
  e.preventDefault();
79
83
  this.resetHandle();
80
84
  };
81
- joy.addEventListener('pointerdown', onPointerDown);
82
- joy.addEventListener('pointermove', onPointerMove);
83
- joy.addEventListener('pointerup', onPointerEnd);
84
- joy.addEventListener('pointercancel', onPointerEnd);
85
- }
86
- get(field, options) {
87
- switch (field) {
88
- case MoveForwardField:
89
- case MoveBackwardField:
90
- const moveY = this.distanceToCenter <= (options.screenJoystickDeadZonePx ?? DefaultDeadZonePx)
91
- ? 0
92
- : -this.clampedY / JoystickRadius;
93
- return field === MoveForwardField ? Math.max(0, moveY) : Math.max(0, -moveY);
94
- case MoveLeftField:
95
- case MoveRightField:
96
- const moveX = this.distanceToCenter <= (options.screenJoystickDeadZonePx ?? DefaultDeadZonePx)
97
- ? 0
98
- : this.clampedX / JoystickRadius;
99
- return field === MoveLeftField ? Math.max(0, moveX) : Math.max(0, moveX);
100
- case RunField:
101
- return (this.distanceToCenter > (options.screenJoystickRunDistancePx ?? DefaultRunDistancePx));
102
- }
103
- return undefined;
85
+ joy.addEventListener('pointerdown', onPointerDown, { signal: this.abortController.signal });
86
+ joy.addEventListener('pointermove', onPointerMove, { signal: this.abortController.signal });
87
+ joy.addEventListener('pointerup', onPointerEnd, { signal: this.abortController.signal });
88
+ joy.addEventListener('pointercancel', onPointerEnd, { signal: this.abortController.signal });
104
89
  }
105
90
  dispose() {
91
+ this.abortController.abort();
106
92
  this.root.remove();
107
93
  }
108
94
  updateHandle(dx, dy) {
109
- this.distanceToCenter = Math.hypot(dx, dy) || 1;
110
- this.clampedX = (dx / this.distanceToCenter) * Math.min(this.distanceToCenter, JoystickRadius);
111
- this.clampedY = (dy / this.distanceToCenter) * Math.min(this.distanceToCenter, JoystickRadius);
112
- this.handle.style.transform = `translate(-50%,-50%) translate(${this.clampedX}px, ${this.clampedY}px)`;
95
+ const distanceToCenter = Math.hypot(dx, dy) || 1;
96
+ const clampedX = (dx / distanceToCenter) * Math.min(distanceToCenter, JoystickRadius);
97
+ const clampedY = (dy / distanceToCenter) * Math.min(distanceToCenter, JoystickRadius);
98
+ this.handle.style.transform = `translate(-50%,-50%) translate(${clampedX}px, ${clampedY}px)`;
99
+ const deadZone = this.options.deadZonePx ?? DefaultDeadZonePx;
100
+ const runDistance = this.options.runDistancePx ?? DefaultRunDistancePx;
101
+ const moveY = distanceToCenter <= deadZone ? 0 : -clampedY / JoystickRadius;
102
+ const moveX = distanceToCenter <= deadZone ? 0 : clampedX / JoystickRadius;
103
+ this.forwardWriter.write(Math.max(0, moveY));
104
+ this.backwardWriter.write(Math.max(0, -moveY));
105
+ this.leftWriter.write(Math.max(0, -moveX));
106
+ this.rightWriter.write(Math.max(0, moveX));
107
+ this.runWriter.write(distanceToCenter > runDistance);
113
108
  }
114
109
  resetHandle() {
115
110
  this.handle.style.transform = 'translate(-50%,-50%)';
116
- this.distanceToCenter = 0;
117
- this.clampedX = 0;
118
- this.clampedY = 0;
111
+ this.forwardWriter.write(0);
112
+ this.backwardWriter.write(0);
113
+ this.leftWriter.write(0);
114
+ this.rightWriter.write(0);
115
+ this.runWriter.write(false);
119
116
  }
120
117
  }
@@ -1,8 +1,6 @@
1
- import { Input, InputField } from './index.js';
2
- export declare class ScreenJumpButtonInput implements Input<{}> {
1
+ export declare class ScreenJumpButtonInput {
2
+ private readonly abortController;
3
3
  readonly root: HTMLDivElement;
4
- private lastJumpTime;
5
4
  constructor(domElement: HTMLElement);
6
- get<T>(field: InputField<T>): T | undefined;
7
5
  dispose(): void;
8
6
  }
@@ -1,7 +1,7 @@
1
- import { LastTimeJumpPressedField } from './index.js';
1
+ import { JumpAction } from './index.js';
2
2
  export class ScreenJumpButtonInput {
3
+ abortController = new AbortController();
3
4
  root;
4
- lastJumpTime = null;
5
5
  constructor(domElement) {
6
6
  const parent = domElement.parentElement ?? domElement;
7
7
  const btn = document.createElement('div');
@@ -27,23 +27,18 @@ export class ScreenJumpButtonInput {
27
27
  const onPress = (e) => {
28
28
  e.preventDefault();
29
29
  e.stopPropagation();
30
- this.lastJumpTime = performance.now() / 1000;
30
+ JumpAction.emit();
31
31
  };
32
32
  const stopPropagation = (e) => {
33
33
  e.stopPropagation();
34
34
  e.preventDefault();
35
35
  };
36
- this.root.addEventListener('pointerdown', onPress);
37
- this.root.addEventListener('pointermove', stopPropagation);
38
- this.root.addEventListener('pointerup', stopPropagation);
39
- }
40
- get(field) {
41
- if (field === LastTimeJumpPressedField) {
42
- return this.lastJumpTime;
43
- }
44
- return undefined;
36
+ this.root.addEventListener('pointerdown', onPress, { signal: this.abortController.signal });
37
+ this.root.addEventListener('pointermove', stopPropagation, { signal: this.abortController.signal });
38
+ this.root.addEventListener('pointerup', stopPropagation, { signal: this.abortController.signal });
45
39
  }
46
40
  dispose() {
41
+ this.abortController.abort();
47
42
  this.root.remove();
48
43
  }
49
44
  }