@pmndrs/viverse 0.2.1 → 0.2.2

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 (39) hide show
  1. package/dist/{input → action}/action.d.ts +19 -20
  2. package/dist/{input → action}/action.js +56 -39
  3. package/dist/{input → action}/index.d.ts +6 -5
  4. package/dist/{input → action}/index.js +5 -4
  5. package/dist/action/keyboard.d.ts +26 -0
  6. package/dist/action/keyboard.js +89 -0
  7. package/dist/action/pointer-capture.d.ts +8 -0
  8. package/dist/{input → action}/pointer-capture.js +16 -22
  9. package/dist/action/pointer-lock.d.ts +6 -0
  10. package/dist/action/pointer-lock.js +49 -0
  11. package/dist/action/pointer.d.ts +18 -0
  12. package/dist/action/pointer.js +90 -0
  13. package/dist/action/screen-joystick.d.ts +16 -0
  14. package/dist/{input → action}/screen-joystick.js +36 -33
  15. package/dist/action/screen-jump-button.d.ts +6 -0
  16. package/dist/action/screen-jump-button.js +32 -0
  17. package/dist/camera.d.ts +3 -3
  18. package/dist/camera.js +3 -3
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/simple-character/apply-input-options.d.ts +2 -2
  22. package/dist/simple-character/apply-input-options.js +28 -22
  23. package/dist/simple-character/index.d.ts +17 -9
  24. package/dist/simple-character/index.js +13 -9
  25. package/dist/simple-character/state/jump-start.js +1 -1
  26. package/dist/simple-character/state/movement.js +1 -1
  27. package/dist/simple-character/update-input-velocity.d.ts +1 -1
  28. package/dist/simple-character/update-input-velocity.js +2 -2
  29. package/dist/simple-character/update-rotation.js +1 -1
  30. package/dist/utils.js +1 -1
  31. package/package.json +1 -1
  32. package/dist/input/keyboard.d.ts +0 -39
  33. package/dist/input/keyboard.js +0 -96
  34. package/dist/input/pointer-capture.d.ts +0 -15
  35. package/dist/input/pointer-lock.d.ts +0 -12
  36. package/dist/input/pointer-lock.js +0 -34
  37. package/dist/input/screen-joystick.d.ts +0 -23
  38. package/dist/input/screen-jump-button.d.ts +0 -6
  39. package/dist/input/screen-jump-button.js +0 -44
@@ -1,24 +1,29 @@
1
- import { MoveForwardAction, MoveBackwardAction, MoveLeftAction, MoveRightAction, RunAction } 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
- export class ScreenJoystickInput {
6
- abortController = new AbortController();
5
+ export class ScreenJoystickLocomotionActionBindings {
6
+ abortSignal;
7
7
  root;
8
8
  handle;
9
9
  pointerId;
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 = {};
16
- constructor(domElement) {
17
- const parent = domElement.parentElement ?? domElement;
18
- const joy = document.createElement('div');
19
- joy.className = 'viverse-joystick mobile-only';
20
- parent.appendChild(joy);
21
- this.root = joy;
10
+ forwardWriter;
11
+ backwardWriter;
12
+ leftWriter;
13
+ rightWriter;
14
+ runWriter;
15
+ //options
16
+ runDistancePx;
17
+ deadZonePx;
18
+ constructor(domElement, abortSignal) {
19
+ this.abortSignal = abortSignal;
20
+ this.forwardWriter = MoveForwardAction.createWriter(this.abortSignal);
21
+ this.backwardWriter = MoveBackwardAction.createWriter(this.abortSignal);
22
+ this.leftWriter = MoveLeftAction.createWriter(this.abortSignal);
23
+ this.rightWriter = MoveRightAction.createWriter(this.abortSignal);
24
+ this.runWriter = RunAction.createWriter(this.abortSignal);
25
+ this.root = document.createElement('div');
26
+ this.root.className = 'viverse-joystick mobile-only';
22
27
  this.root.style.position = 'absolute';
23
28
  this.root.style.bottom = '24px';
24
29
  this.root.style.left = '24px';
@@ -31,10 +36,12 @@ export class ScreenJoystickInput {
31
36
  this.root.style.userSelect = 'none';
32
37
  this.root.style.setProperty('-webkit-user-select', 'none');
33
38
  this.root.style.setProperty('-webkit-touch-callout', 'none');
34
- const handle = document.createElement('div');
35
- handle.className = 'viverse-joystick-handle';
36
- joy.appendChild(handle);
37
- this.handle = handle;
39
+ const parent = domElement.parentElement ?? domElement;
40
+ parent.appendChild(this.root);
41
+ this.abortSignal.addEventListener('abort', () => this.root.remove(), { once: true });
42
+ this.handle = document.createElement('div');
43
+ this.handle.className = 'viverse-joystick-handle';
44
+ this.root.appendChild(this.handle);
38
45
  this.handle.style.position = 'absolute';
39
46
  this.handle.style.left = '50%';
40
47
  this.handle.style.top = '50%';
@@ -55,9 +62,9 @@ export class ScreenJoystickInput {
55
62
  }
56
63
  e.preventDefault();
57
64
  e.stopPropagation();
58
- joy.setPointerCapture(e.pointerId);
65
+ this.root.setPointerCapture(e.pointerId);
59
66
  this.pointerId = e.pointerId;
60
- const rect = joy.getBoundingClientRect();
67
+ const rect = this.root.getBoundingClientRect();
61
68
  const joyCenterX = rect.left + rect.width / 2;
62
69
  const joyCenterY = rect.top + rect.height / 2;
63
70
  this.updateHandle(e.clientX - joyCenterX, e.clientY - joyCenterY);
@@ -68,7 +75,7 @@ export class ScreenJoystickInput {
68
75
  }
69
76
  e.preventDefault();
70
77
  e.stopPropagation();
71
- const rect = joy.getBoundingClientRect();
78
+ const rect = this.root.getBoundingClientRect();
72
79
  const joyCenterX = rect.left + rect.width / 2;
73
80
  const joyCenterY = rect.top + rect.height / 2;
74
81
  this.updateHandle(e.clientX - joyCenterX, e.clientY - joyCenterY);
@@ -78,26 +85,22 @@ export class ScreenJoystickInput {
78
85
  return;
79
86
  }
80
87
  this.pointerId = undefined;
81
- joy.releasePointerCapture(e.pointerId);
88
+ this.root.releasePointerCapture(e.pointerId);
82
89
  e.preventDefault();
83
90
  this.resetHandle();
84
91
  };
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 });
89
- }
90
- dispose() {
91
- this.abortController.abort();
92
- this.root.remove();
92
+ this.root.addEventListener('pointerdown', onPointerDown, { signal: this.abortSignal });
93
+ this.root.addEventListener('pointermove', onPointerMove, { signal: this.abortSignal });
94
+ this.root.addEventListener('pointerup', onPointerEnd, { signal: this.abortSignal });
95
+ this.root.addEventListener('pointercancel', onPointerEnd, { signal: this.abortSignal });
93
96
  }
94
97
  updateHandle(dx, dy) {
95
98
  const distanceToCenter = Math.hypot(dx, dy) || 1;
96
99
  const clampedX = (dx / distanceToCenter) * Math.min(distanceToCenter, JoystickRadius);
97
100
  const clampedY = (dy / distanceToCenter) * Math.min(distanceToCenter, JoystickRadius);
98
101
  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;
102
+ const deadZone = this.deadZonePx ?? DefaultDeadZonePx;
103
+ const runDistance = this.runDistancePx ?? DefaultRunDistancePx;
101
104
  const moveY = distanceToCenter <= deadZone ? 0 : -clampedY / JoystickRadius;
102
105
  const moveX = distanceToCenter <= deadZone ? 0 : clampedX / JoystickRadius;
103
106
  this.forwardWriter.write(Math.max(0, moveY));
@@ -0,0 +1,6 @@
1
+ export declare const defaultScreenButtonStyles: Partial<CSSStyleDeclaration>;
2
+ export declare const jumpButtonImage = "url(\"data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%20fill=%22none%22%20stroke=%22%23444%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%3E%3Cpolyline%20points=%2218%2015%2012%209%206%2015%22/%3E%3C/svg%3E\")";
3
+ export declare class ScreenButtonJumpActionBindings {
4
+ readonly root: HTMLDivElement;
5
+ constructor(domElement: HTMLElement, abortSignal: AbortSignal);
6
+ }
@@ -0,0 +1,32 @@
1
+ import { JumpAction } from './index.js';
2
+ import { PointerButtonActionBinding } from './pointer.js';
3
+ export const defaultScreenButtonStyles = {
4
+ position: 'absolute',
5
+ bottom: '32px',
6
+ right: '126px',
7
+ minWidth: '64px',
8
+ height: '64px',
9
+ borderRadius: '9999px',
10
+ pointerEvents: 'auto',
11
+ touchAction: 'none',
12
+ userSelect: 'none',
13
+ background: 'rgba(255,255,255,0.3)',
14
+ backgroundRepeat: 'no-repeat',
15
+ backgroundPosition: 'center',
16
+ backgroundSize: '50%',
17
+ };
18
+ Object.assign(defaultScreenButtonStyles, { '-webkit-user-select': 'none' });
19
+ export const jumpButtonImage = 'url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%20fill=%22none%22%20stroke=%22%23444%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%3E%3Cpolyline%20points=%2218%2015%2012%209%206%2015%22/%3E%3C/svg%3E")';
20
+ export class ScreenButtonJumpActionBindings {
21
+ root;
22
+ constructor(domElement, abortSignal) {
23
+ const parent = domElement.parentElement ?? domElement;
24
+ this.root = document.createElement('div');
25
+ this.root.className = 'viverse-button viverse-jump mobile-only';
26
+ parent.appendChild(this.root);
27
+ abortSignal.addEventListener('abort', () => this.root.remove(), { once: true });
28
+ Object.assign(this.root.style, defaultScreenButtonStyles);
29
+ new PointerButtonActionBinding(JumpAction, this.root, abortSignal);
30
+ this.root.addEventListener('pointerdown', (e) => e.stopPropagation(), { signal: abortSignal });
31
+ }
32
+ }
package/dist/camera.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Object3D, Vector3, Vector3Tuple, Ray } from 'three';
2
- export declare const FirstPersonCharacterCameraBehavior: SimpleCharacterCameraBehaviorOptions;
3
- export type SimpleCharacterCameraBehaviorOptions = {
2
+ export declare const FirstPersonCharacterCameraBehavior: CharacterCameraBehaviorOptions;
3
+ export type CharacterCameraBehaviorOptions = {
4
4
  /**
5
5
  * @default true
6
6
  */
@@ -73,6 +73,6 @@ export declare class CharacterCameraBehavior {
73
73
  /**
74
74
  * @param delta in seconds
75
75
  */
76
- update(camera: Object3D, target: Object3D, deltaTime: number, raycast?: (ray: Ray, far: number) => number | undefined, options?: SimpleCharacterCameraBehaviorOptions): void;
76
+ update(camera: Object3D, target: Object3D, deltaTime: number, raycast?: (ray: Ray, far: number) => number | undefined, options?: CharacterCameraBehaviorOptions): void;
77
77
  dispose(): void;
78
78
  }
package/dist/camera.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Vector3, Euler, Ray, Quaternion } from 'three';
2
2
  import { clamp } from 'three/src/math/MathUtils.js';
3
- import { RotatePitchAction, RotateYawAction, ZoomAction } from './input/index.js';
3
+ import { RotatePitchAction, RotateYawAction, ZoomAction } from './action/index.js';
4
4
  export const FirstPersonCharacterCameraBehavior = {
5
5
  characterBaseOffset: [0, 1.6, 0],
6
6
  zoom: { maxDistance: 0, minDistance: 0 },
@@ -77,7 +77,7 @@ export class CharacterCameraBehavior {
77
77
  characterWorldPosition.add(chracterBaseOffsetHelper);
78
78
  camera.getWorldPosition(deltaHelper);
79
79
  deltaHelper.sub(characterWorldPosition);
80
- // apply rotation input to rotationYaw and rotationPitch if not disabled or first update
80
+ // apply rotation actions to rotationYaw and rotationPitch if not disabled or first update
81
81
  let rotationOptions = options.rotation ?? true;
82
82
  if (!this.firstUpdate && rotationOptions !== false) {
83
83
  rotationOptions = rotationOptions === true ? {} : rotationOptions;
@@ -96,7 +96,7 @@ export class CharacterCameraBehavior {
96
96
  camera.rotation.set(this.rotationPitch, this.rotationYaw, 0, 'YXZ');
97
97
  rayHelper.direction.set(0, 0, 1).applyEuler(camera.rotation);
98
98
  rayHelper.origin.copy(characterWorldPosition);
99
- // apply zoom input to zoomDistance if not disabled or first update
99
+ // apply zoom action to zoomDistance if not disabled or first update
100
100
  let zoomOptions = options.zoom ?? true;
101
101
  if (!this.firstUpdate && zoomOptions !== false) {
102
102
  zoomOptions = zoomOptions === true ? {} : zoomOptions;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from './utils.js';
2
- export * from './input/index.js';
2
+ export * from './action/index.js';
3
3
  export * from './utils.js';
4
4
  export * from './camera.js';
5
5
  export * from './physics/index.js';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from './utils.js';
2
- export * from './input/index.js';
2
+ export * from './action/index.js';
3
3
  export * from './utils.js';
4
4
  export * from './camera.js';
5
5
  export * from './physics/index.js';
@@ -1,2 +1,2 @@
1
- import { SimpleCharacterInputOptions } from './index.js';
2
- export declare function applySimpleCharacterInputOptions(inputs: Array<unknown>, options?: SimpleCharacterInputOptions): void;
1
+ import { SimpleCharacterActionBindingOptions } from './index.js';
2
+ export declare function applySimpleCharacterActionBindingOptions(actionBindingsList: Array<unknown>, options?: SimpleCharacterActionBindingOptions): void;
@@ -1,28 +1,34 @@
1
- import { DefaultJumpKeys, DefaultMoveBackwardKeys, DefaultMoveForwardKeys, DefaultMoveLeftKeys, DefaultMoveRightKeys, DefaultRunKeys, LocomotionKeyboardInput, } from '../input/keyboard.js';
2
- import { PointerCaptureInput } from '../input/pointer-capture.js';
3
- import { PointerLockInput } from '../input/pointer-lock.js';
4
- import { ScreenJoystickInput } from '../input/screen-joystick.js';
5
- export function applySimpleCharacterInputOptions(inputs, options) {
6
- for (const input of inputs) {
7
- if (input instanceof ScreenJoystickInput) {
8
- input.options.deadZonePx = options?.screenJoystickDeadZonePx;
9
- input.options.runDistancePx = options?.screenJoystickRunDistancePx;
1
+ import { DefaultJumpKeys, DefaultMoveBackwardKeys, DefaultMoveForwardKeys, DefaultMoveLeftKeys, DefaultMoveRightKeys, DefaultRunKeys, KeyboardLocomotionActionBindings, } from '../action/keyboard.js';
2
+ import { PointerCaptureRotateZoomActionBindings } from '../action/pointer-capture.js';
3
+ import { PointerLockRotateZoomActionBindings } from '../action/pointer-lock.js';
4
+ import { ScreenJoystickLocomotionActionBindings } from '../action/screen-joystick.js';
5
+ export function applySimpleCharacterActionBindingOptions(actionBindingsList, options) {
6
+ for (const actionBindings of actionBindingsList) {
7
+ if (actionBindings instanceof ScreenJoystickLocomotionActionBindings) {
8
+ actionBindings.deadZonePx = options?.screenJoystickDeadZonePx;
9
+ actionBindings.runDistancePx = options?.screenJoystickRunDistancePx;
10
10
  }
11
- if (input instanceof PointerCaptureInput) {
12
- input.options.rotationSpeed = options?.pointerCaptureRotationSpeed;
13
- input.options.zoomSpeed = options?.pointerCaptureZoomSpeed;
11
+ if (actionBindings instanceof PointerCaptureRotateZoomActionBindings) {
12
+ actionBindings.rotationSpeed = options?.pointerCaptureRotationSpeed;
13
+ actionBindings.zoomSpeed = options?.pointerCaptureZoomSpeed;
14
14
  }
15
- if (input instanceof PointerLockInput) {
16
- input.options.rotationSpeed = options?.pointerLockRotationSpeed;
17
- input.options.zoomSpeed = options?.pointerLockZoomSpeed;
15
+ if (actionBindings instanceof PointerLockRotateZoomActionBindings) {
16
+ actionBindings.rotationSpeed = options?.pointerLockRotationSpeed;
17
+ actionBindings.zoomSpeed = options?.pointerLockZoomSpeed;
18
18
  }
19
- if (input instanceof LocomotionKeyboardInput) {
20
- input.forward.options.keys = options?.keyboardMoveForwardKeys ?? DefaultMoveForwardKeys;
21
- input.backward.options.keys = options?.keyboardMoveBackwardKeys ?? DefaultMoveBackwardKeys;
22
- input.left.options.keys = options?.keyboardMoveLeftKeys ?? DefaultMoveLeftKeys;
23
- input.right.options.keys = options?.keyboardMoveRightKeys ?? DefaultMoveRightKeys;
24
- input.run.options.keys = options?.keyboardRunKeys ?? DefaultRunKeys;
25
- input.jump.options.keys = options?.keyboardJumpKeys ?? DefaultJumpKeys;
19
+ if (actionBindings instanceof KeyboardLocomotionActionBindings) {
20
+ actionBindings.moveForwardBinding.keys = options?.keyboardMoveForwardKeys ?? DefaultMoveForwardKeys;
21
+ actionBindings.moveBackwardBinding.requiresPointerLock = options?.keyboardRequiresPointerLock;
22
+ actionBindings.moveBackwardBinding.keys = options?.keyboardMoveBackwardKeys ?? DefaultMoveBackwardKeys;
23
+ actionBindings.moveBackwardBinding.requiresPointerLock = options?.keyboardRequiresPointerLock;
24
+ actionBindings.moveLeftBinding.keys = options?.keyboardMoveLeftKeys ?? DefaultMoveLeftKeys;
25
+ actionBindings.moveLeftBinding.requiresPointerLock = options?.keyboardRequiresPointerLock;
26
+ actionBindings.moveRightBinding.keys = options?.keyboardMoveRightKeys ?? DefaultMoveRightKeys;
27
+ actionBindings.moveRightBinding.requiresPointerLock = options?.keyboardRequiresPointerLock;
28
+ actionBindings.runBinding.keys = options?.keyboardRunKeys ?? DefaultRunKeys;
29
+ actionBindings.runBinding.requiresPointerLock = options?.keyboardRequiresPointerLock;
30
+ actionBindings.jumpBinding.keys = options?.keyboardJumpKeys ?? DefaultJumpKeys;
31
+ actionBindings.jumpBinding.requiresPointerLock = options?.keyboardRequiresPointerLock;
26
32
  }
27
33
  }
28
34
  }
@@ -1,6 +1,6 @@
1
1
  import { Group, Object3D, Object3DEventMap, AnimationAction } from 'three';
2
2
  import { CharacterAnimationOptions } from '../animation/index.js';
3
- import { CharacterCameraBehavior, SimpleCharacterCameraBehaviorOptions } from '../camera.js';
3
+ import { CharacterCameraBehavior, CharacterCameraBehaviorOptions } from '../camera.js';
4
4
  import { CharacterModelOptions, CharacterModel } from '../model/index.js';
5
5
  import { BvhCharacterPhysicsOptions, BvhCharacterPhysics, BvhPhysicsWorld } from '../physics/index.js';
6
6
  export type SimpleCharacterState = {
@@ -51,7 +51,7 @@ export type SimpleCharacterAnimationOptions = {
51
51
  /**
52
52
  * @default "movement"
53
53
  */
54
- yawRotationBasdOn?: 'camera' | 'movement';
54
+ yawRotationBasedOn?: 'camera' | 'movement';
55
55
  /**
56
56
  * @default 10
57
57
  */
@@ -61,13 +61,14 @@ export type SimpleCharacterAnimationOptions = {
61
61
  */
62
62
  crossFadeDuration?: number;
63
63
  };
64
- export type SimpleCharacterInputOptions = {
64
+ export type SimpleCharacterActionBindingOptions = {
65
65
  screenJoystickRunDistancePx?: number;
66
66
  screenJoystickDeadZonePx?: number;
67
67
  pointerCaptureRotationSpeed?: number;
68
68
  pointerCaptureZoomSpeed?: number;
69
69
  pointerLockRotationSpeed?: number;
70
70
  pointerLockZoomSpeed?: number;
71
+ keyboardRequiresPointerLock?: boolean;
71
72
  keyboardMoveForwardKeys?: Array<string>;
72
73
  keyboardMoveBackwardKeys?: Array<string>;
73
74
  keyboardMoveLeftKeys?: Array<string>;
@@ -76,16 +77,24 @@ export type SimpleCharacterInputOptions = {
76
77
  keyboardJumpKeys?: Array<string>;
77
78
  };
78
79
  export type SimpleCharacterOptions = {
80
+ /**
81
+ * @deprecated use actionBindings instead
82
+ */
79
83
  readonly input?: ReadonlyArray<{
80
- new (domElement: HTMLElement): {
81
- dispose(): void;
82
- };
84
+ new (domElement: HTMLElement, abortSignal: AbortSignal): any;
85
+ }>;
86
+ readonly actionBindings?: ReadonlyArray<{
87
+ new (domElement: HTMLElement, abortSignal: AbortSignal): any;
83
88
  }>;
84
- inputOptions?: SimpleCharacterInputOptions;
89
+ /**
90
+ * @deprecated use actionBindingOptions instead
91
+ */
92
+ inputOptions?: SimpleCharacterActionBindingOptions;
93
+ actionBindingOptions?: SimpleCharacterActionBindingOptions;
85
94
  movement?: SimpleCharacterMovementOptions;
86
95
  readonly model?: CharacterModelOptions | boolean;
87
96
  physics?: BvhCharacterPhysicsOptions;
88
- cameraBehavior?: SimpleCharacterCameraBehaviorOptions;
97
+ cameraBehavior?: CharacterCameraBehaviorOptions;
89
98
  readonly animation?: SimpleCharacterAnimationOptions;
90
99
  };
91
100
  export declare class SimpleCharacter extends Group<Object3DEventMap & {
@@ -103,7 +112,6 @@ export declare class SimpleCharacter extends Group<Object3DEventMap & {
103
112
  private readonly updateTimeline;
104
113
  private readonly graph;
105
114
  private readonly abortController;
106
- private readonly inputs;
107
115
  lastJump: number;
108
116
  readonly abortSignal: AbortSignal;
109
117
  constructor(camera: Object3D, world: BvhPhysicsWorld, domElement: HTMLElement, options?: SimpleCharacterOptions);
@@ -1,20 +1,20 @@
1
1
  import { VRM, VRMUtils } from '@pixiv/three-vrm';
2
2
  import { runTimeline, GraphTimeline } from '@pmndrs/timeline';
3
3
  import { Group, Vector3 } from 'three';
4
+ import { applySimpleCharacterActionBindingOptions } from './apply-input-options.js';
5
+ import { KeyboardLocomotionActionBindings, PointerCaptureRotateZoomActionBindings, ScreenJoystickLocomotionActionBindings, ScreenButtonJumpActionBindings, } from '../action/index.js';
4
6
  import { CharacterCameraBehavior } from '../camera.js';
5
- import { LocomotionKeyboardInput, PointerCaptureInput, ScreenJoystickInput, ScreenJumpButtonInput, } from '../input/index.js';
6
7
  import { loadCharacterModel, flattenCharacterModelOptions, } from '../model/index.js';
7
8
  import { BvhCharacterPhysics } from '../physics/index.js';
9
+ import { shouldJump } from '../utils.js';
8
10
  import { loadSimpleCharacterJumpDownState } from './state/jump-down.js';
9
11
  import { loadSimpleCharacterJumpForwardAction, loadSimpleCharacterJumpForwardState } from './state/jump-forward.js';
10
12
  import { loadSimpleCharacterJumpLoopState } from './state/jump-loop.js';
11
13
  import { loadSimpleCharacterJumpStartState } from './state/jump-start.js';
12
14
  import { loadSimpleCharacterJumpUpAction, loadSimpleCharacterJumpUpState } from './state/jump-up.js';
13
15
  import { loadSimpleCharacterMovingState } from './state/movement.js';
14
- import { updateSimpleCharacterInputVelocity } from './update-input-velocity.js';
16
+ import { updateSimpleCharacterVelocity } from './update-input-velocity.js';
15
17
  import { updateSimpleCharacterRotation } from './update-rotation.js';
16
- import { shouldJump } from '../utils.js';
17
- import { applySimpleCharacterInputOptions } from './apply-input-options.js';
18
18
  export class SimpleCharacter extends Group {
19
19
  camera;
20
20
  world;
@@ -27,7 +27,6 @@ export class SimpleCharacter extends Group {
27
27
  updateTimeline;
28
28
  graph = new GraphTimeline('moving');
29
29
  abortController = new AbortController();
30
- inputs;
31
30
  lastJump = 0;
32
31
  abortSignal = this.abortController.signal;
33
32
  constructor(camera, world, domElement, options = {}) {
@@ -35,8 +34,14 @@ export class SimpleCharacter extends Group {
35
34
  this.camera = camera;
36
35
  this.world = world;
37
36
  this.options = options;
38
- this.inputs = (options.input ?? [ScreenJoystickInput, ScreenJumpButtonInput, PointerCaptureInput, LocomotionKeyboardInput]).map((Input) => new Input(domElement));
39
- applySimpleCharacterInputOptions(this.inputs, options.inputOptions);
37
+ const actionBindingsList = (options.actionBindings ??
38
+ options.input ?? [
39
+ ScreenJoystickLocomotionActionBindings,
40
+ ScreenButtonJumpActionBindings,
41
+ PointerCaptureRotateZoomActionBindings,
42
+ KeyboardLocomotionActionBindings,
43
+ ]).map((ActionBindings) => new ActionBindings(domElement, this.abortController.signal));
44
+ applySimpleCharacterActionBindingOptions(actionBindingsList, options.actionBindingOptions ?? options.inputOptions);
40
45
  // camera behavior
41
46
  this.cameraBehavior = new CharacterCameraBehavior();
42
47
  // physics
@@ -81,7 +86,7 @@ export class SimpleCharacter extends Group {
81
86
  if (this.model != null) {
82
87
  updateSimpleCharacterRotation(delta, this.physics, this.camera, this.model, this.options.animation);
83
88
  }
84
- updateSimpleCharacterInputVelocity(this.camera, this.physics, this.options.movement);
89
+ updateSimpleCharacterVelocity(this.camera, this.physics, this.options.movement);
85
90
  this.updateTimeline?.(undefined, delta);
86
91
  this.model?.mixer.update(delta);
87
92
  if (this.model instanceof VRM) {
@@ -94,7 +99,6 @@ export class SimpleCharacter extends Group {
94
99
  this.abortController.abort();
95
100
  this.parent?.remove(this);
96
101
  this.model?.scene.dispatchEvent({ type: 'dispose' });
97
- this.inputs.forEach((input) => input.dispose());
98
102
  this.cameraBehavior.dispose();
99
103
  VRMUtils.deepDispose(this);
100
104
  }
@@ -1,5 +1,5 @@
1
1
  import { action, timePassed } from '@pmndrs/timeline';
2
- import { RunAction } from '../../input/index.js';
2
+ import { RunAction } from '../../action/index.js';
3
3
  import { startAnimation } from '../../utils.js';
4
4
  import { DefaultCrossFadeDuration, DefaultJumDelay } from '../defaults.js';
5
5
  export async function loadSimpleCharacterJumpStartState(jumpUp, jumpForward, state, options) {
@@ -1,7 +1,7 @@
1
1
  import { action } from '@pmndrs/timeline';
2
+ import { RunAction } from '../../action/index.js';
2
3
  import { IdleAnimationUrl, RunAnimationUrl, WalkAnimationUrl } from '../../animation/default.js';
3
4
  import { flattenCharacterAnimationOptions, loadCharacterAnimation } from '../../animation/index.js';
4
- import { RunAction } from '../../input/index.js';
5
5
  import { shouldJump, startAnimation } from '../../utils.js';
6
6
  import { DefaultCrossFadeDuration } from '../defaults.js';
7
7
  export async function loadSimpleCharacterMovingState(state, options) {
@@ -1,4 +1,4 @@
1
1
  import { Object3D } from 'three';
2
2
  import type { SimpleCharacterMovementOptions } from './index.js';
3
3
  import type { BvhCharacterPhysics } from '../physics/index.js';
4
- export declare function updateSimpleCharacterInputVelocity(camera: Object3D, physics: BvhCharacterPhysics, options?: SimpleCharacterMovementOptions): void;
4
+ export declare function updateSimpleCharacterVelocity(camera: Object3D, physics: BvhCharacterPhysics, options?: SimpleCharacterMovementOptions): void;
@@ -1,8 +1,8 @@
1
1
  import { Euler, Quaternion } from 'three';
2
- import { RunAction, MoveLeftAction, MoveRightAction, MoveForwardAction, MoveBackwardAction } from '../input/index.js';
2
+ import { RunAction, MoveLeftAction, MoveRightAction, MoveForwardAction, MoveBackwardAction } from '../action/index.js';
3
3
  const cameraEuler = new Euler();
4
4
  const cameraRotation = new Quaternion();
5
- export function updateSimpleCharacterInputVelocity(camera, physics, options) {
5
+ export function updateSimpleCharacterVelocity(camera, physics, options) {
6
6
  cameraEuler.setFromQuaternion(camera.getWorldQuaternion(cameraRotation), 'YXZ');
7
7
  cameraEuler.x = 0;
8
8
  cameraEuler.z = 0;
@@ -7,7 +7,7 @@ const inputDirection = new Vector3();
7
7
  const quaternion = new Quaternion();
8
8
  export function updateSimpleCharacterRotation(delta, physics, camera, model, options) {
9
9
  // Character yaw rotation logic
10
- const basedOn = options?.yawRotationBasdOn ?? 'movement';
10
+ const basedOn = options?.yawRotationBasedOn ?? 'movement';
11
11
  // compute goalTargetEuler
12
12
  if (basedOn === 'camera') {
13
13
  goalTargetEuler.setFromQuaternion(camera.getWorldQuaternion(quaternion), 'YXZ');
package/dist/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- import { JumpAction } from './input/index.js';
1
+ import { JumpAction } from './action/index.js';
2
2
  export function getIsMobileMediaQuery() {
3
3
  if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
4
4
  return undefined;
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "peerDependencies": {
22
22
  "three": "*"
23
23
  },
24
- "version": "0.2.1",
24
+ "version": "0.2.2",
25
25
  "type": "module",
26
26
  "dependencies": {
27
27
  "@pixiv/three-vrm": "^3.4.2",
@@ -1,39 +0,0 @@
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
- }
16
- export type LocomotionKeyboardInputOptions = {
17
- keyboardMoveForwardKeys?: Array<string>;
18
- keyboardMoveBackwardKeys?: Array<string>;
19
- keyboardMoveLeftKeys?: Array<string>;
20
- keyboardMoveRightKeys?: Array<string>;
21
- keyboardRunKeys?: Array<string>;
22
- keyboardJumpKeys?: Array<string>;
23
- };
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;
37
- constructor(domElement: HTMLElement);
38
- dispose(): void;
39
- }
@@ -1,96 +0,0 @@
1
- import { JumpAction, MoveBackwardAction, MoveForwardAction, MoveLeftAction, MoveRightAction, RunAction, } from './index.js';
2
- export class KeyboardInput {
3
- domElement;
4
- abortController = new AbortController();
5
- options = {};
6
- abortSignal = this.abortController.signal;
7
- pressedKeys = new Set();
8
- constructor(domElement) {
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;
35
- }
36
- const wasEmpty = this.pressedKeys.size === 0;
37
- this.pressedKeys.add(e.code);
38
- if (wasEmpty && this.pressedKeys.size > 0) {
39
- write(true);
40
- }
41
- }, { signal: this.abortController.signal });
42
- this.domElement.addEventListener('keyup', (e) => {
43
- if (!isWatched(e)) {
44
- return;
45
- }
46
- if (this.pressedKeys.delete(e.code) && this.pressedKeys.size === 0) {
47
- write(false);
48
- }
49
- }, { signal: this.abortController.signal });
50
- // Handle focus loss to clear pressed keys
51
- this.domElement.addEventListener('blur', () => {
52
- if (this.pressedKeys.size > 0) {
53
- this.pressedKeys.clear();
54
- write(false);
55
- }
56
- }, { signal: this.abortController.signal });
57
- }
58
- dispose() {
59
- this.abortController.abort();
60
- }
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,15 +0,0 @@
1
- /**
2
- * @requires to manually execute `domElement.setPointerCapture(pointerId)` on pointerdown
3
- */
4
- export declare class PointerCaptureInput {
5
- private readonly domElement;
6
- private readonly abortController;
7
- private activePointers;
8
- private lastPinchDist;
9
- options: {
10
- rotationSpeed?: number;
11
- zoomSpeed?: number;
12
- };
13
- constructor(domElement: HTMLElement);
14
- dispose(): void;
15
- }