@gg-web-engine/core 0.0.18 → 0.0.20

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 (46) hide show
  1. package/dist/2d/entities/gg-2d-entity.d.ts +5 -3
  2. package/dist/2d/entities/gg-2d-entity.js +6 -12
  3. package/dist/2d/entities/gg-positionable-2d-entity.js +2 -1
  4. package/dist/2d/gg-2d-world.js +2 -4
  5. package/dist/3d/entities/controllers/animators/camera-3d.animator.js +1 -1
  6. package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.d.ts +1 -0
  7. package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.js +20 -6
  8. package/dist/3d/entities/controllers/input/free-camera.controller.d.ts +8 -0
  9. package/dist/3d/entities/controllers/input/free-camera.controller.js +22 -11
  10. package/dist/3d/entities/controllers/input/orbit-camera.controller.d.ts +7 -1
  11. package/dist/3d/entities/controllers/input/orbit-camera.controller.js +24 -6
  12. package/dist/3d/entities/gg-3d-entity.d.ts +5 -3
  13. package/dist/3d/entities/gg-3d-entity.js +6 -12
  14. package/dist/3d/entities/gg-3d-map-graph.entity.d.ts +4 -1
  15. package/dist/3d/entities/gg-3d-map-graph.entity.js +6 -6
  16. package/dist/3d/entities/gg-3d-raycast-vehicle.entity.d.ts +9 -1
  17. package/dist/3d/entities/gg-3d-raycast-vehicle.entity.js +24 -11
  18. package/dist/3d/entities/gg-3d-trigger.entity.js +1 -0
  19. package/dist/3d/entities/gg-positionable-3d-entity.js +3 -2
  20. package/dist/3d/gg-3d-world.js +3 -5
  21. package/dist/3d/loader.js +2 -2
  22. package/dist/base/entities/base-gg-renderer.js +4 -3
  23. package/dist/base/entities/gg-entity.d.ts +2 -0
  24. package/dist/base/entities/gg-entity.js +14 -3
  25. package/dist/base/entities/gg-positionable-entity.d.ts +1 -1
  26. package/dist/base/entities/inline-controller.js +1 -1
  27. package/dist/base/entities/mixins/renderable-entity.mixin.d.ts +10 -0
  28. package/dist/base/entities/mixins/renderable-entity.mixin.js +57 -0
  29. package/dist/base/gg-world.d.ts +2 -1
  30. package/dist/base/gg-world.js +4 -3
  31. package/dist/base/inputs/direction.keyboard-input.d.ts +1 -1
  32. package/dist/base/inputs/direction.keyboard-input.js +34 -45
  33. package/dist/base/inputs/input.d.ts +8 -10
  34. package/dist/base/inputs/input.js +15 -31
  35. package/dist/base/inputs/keyboard.input.d.ts +14 -2
  36. package/dist/base/inputs/keyboard.input.js +47 -20
  37. package/dist/base/inputs/mouse.input.d.ts +3 -3
  38. package/dist/base/inputs/mouse.input.js +87 -103
  39. package/dist/base/math/point2.d.ts +8 -0
  40. package/dist/base/math/point2.js +17 -1
  41. package/dist/base/math/point3.d.ts +10 -0
  42. package/dist/base/math/point3.js +21 -1
  43. package/dist/base/math/quaternion.d.ts +3 -1
  44. package/dist/base/math/quaternion.js +5 -1
  45. package/package.json +5 -3
  46. package/tsconfig.json +1 -1
package/dist/3d/loader.js CHANGED
@@ -18,8 +18,8 @@ export var CachingStrategy;
18
18
  })(CachingStrategy || (CachingStrategy = {}));
19
19
  const defaultLoadOptions = {
20
20
  cachingStrategy: CachingStrategy.Nothing,
21
- position: { x: 0, y: 0, z: 0 },
22
- rotation: { x: 0, y: 0, z: 0, w: 1 },
21
+ position: Pnt3.O,
22
+ rotation: Qtrn.O,
23
23
  loadProps: true,
24
24
  };
25
25
  const cloneLoadResourcesResult = (loadResult) => ({
@@ -46,19 +46,20 @@ export class BaseGgRenderer extends GgEntity {
46
46
  onSpawned(world) {
47
47
  super.onSpawned(world);
48
48
  this._rendererSize$.next(null);
49
- if (this.rendererOptions.size == 'fullscreen' || this.rendererOptions.size instanceof Function) {
49
+ if (this.rendererOptions.size == 'fullscreen' || typeof this.rendererOptions.size === 'function') {
50
50
  if (this.canvas) {
51
51
  this.canvas.style.position = 'absolute';
52
52
  }
53
53
  merge(fromEvent(window, 'resize').pipe(auditTime(100)), fromEvent(window, 'orientationchange'))
54
54
  .pipe(takeUntil(this._onRemoved$), map(() => ({ x: window.innerWidth, y: window.innerHeight })), startWith({ x: window.innerWidth, y: window.innerHeight }))
55
55
  .subscribe(size => {
56
- this._rendererSize$.next(this.rendererOptions.size instanceof Function ? this.rendererOptions.size(size) : size);
56
+ this._rendererSize$.next(typeof this.rendererOptions.size === 'function' ? this.rendererOptions.size(size) : size);
57
57
  });
58
58
  }
59
59
  else if (this.rendererOptions.size instanceof Observable ||
60
60
  // for cases when project uses two separate rxjs packages, instanceof can return false for observable
61
- (!!this.rendererOptions.subscribe !== undefined && !!this.rendererOptions.pipe !== undefined)) {
61
+ (this.rendererOptions.size.subscribe !== undefined &&
62
+ this.rendererOptions.size.pipe !== undefined)) {
62
63
  this.rendererOptions.size.pipe(takeUntil(this._onRemoved$)).subscribe(newSize => {
63
64
  this._rendererSize$.next(newSize);
64
65
  });
@@ -36,7 +36,9 @@ export declare abstract class GgEntity {
36
36
  protected _active: boolean;
37
37
  get active(): boolean;
38
38
  set active(value: boolean);
39
+ parent: GgEntity | null;
39
40
  protected _children: GgEntity[];
41
+ get children(): GgEntity[];
40
42
  addChildren(...entities: GgEntity[]): void;
41
43
  removeChildren(entities: GgEntity[], dispose?: boolean): void;
42
44
  protected _onSpawned$: Subject<void>;
@@ -27,6 +27,7 @@ export class GgEntity {
27
27
  * The flag whether entity should listen to ticks. If set to false, ticks will not be propagated to this entity
28
28
  * */
29
29
  this._active = true;
30
+ this.parent = null;
30
31
  this._children = [];
31
32
  this._onSpawned$ = new Subject();
32
33
  this._onRemoved$ = new Subject();
@@ -46,7 +47,16 @@ export class GgEntity {
46
47
  set active(value) {
47
48
  this._active = value;
48
49
  }
50
+ get children() {
51
+ return [...this._children];
52
+ }
49
53
  addChildren(...entities) {
54
+ for (const entity of entities) {
55
+ if (entity.parent) {
56
+ entity.parent.removeChildren([entity]);
57
+ }
58
+ entity.parent = this;
59
+ }
50
60
  this._children.push(...entities);
51
61
  if (this._world) {
52
62
  for (const item of entities) {
@@ -56,8 +66,9 @@ export class GgEntity {
56
66
  }
57
67
  removeChildren(entities, dispose = false) {
58
68
  this._children = this._children.filter(c => !entities.includes(c));
59
- if (this._world) {
60
- for (const item of entities) {
69
+ for (const item of entities) {
70
+ item.parent = null;
71
+ if (this._world) {
61
72
  this._world.removeEntity(item, dispose);
62
73
  }
63
74
  }
@@ -80,7 +91,7 @@ export class GgEntity {
80
91
  // TODO add some flag to entity that it is disposed, and throw a normal error when trying to add such entity to world again
81
92
  dispose() {
82
93
  if (this.world) {
83
- this.world.removeEntity(this);
94
+ this.world.removeEntity(this, false);
84
95
  }
85
96
  this._onSpawned$.complete();
86
97
  this._onRemoved$.complete();
@@ -18,6 +18,6 @@ export declare abstract class GgPositionableEntity<D, R> extends GgEntity {
18
18
  get scale(): D;
19
19
  get scale$(): Observable<D>;
20
20
  set scale(value: D);
21
- protected constructor();
21
+ constructor();
22
22
  onSpawned(world: GgWorld<D, R>): void;
23
23
  }
@@ -10,6 +10,6 @@ export function createInlineTickController(world, tickOrder = GGTickOrder.CONTRO
10
10
  const controller = new InlineTickController(tickOrder);
11
11
  world.addEntity(controller);
12
12
  return controller.tick$.pipe(finalize(() => {
13
- world.removeEntity(controller);
13
+ world.removeEntity(controller, true);
14
14
  }));
15
15
  }
@@ -0,0 +1,10 @@
1
+ import { GgEntity } from '../gg-entity';
2
+ export declare abstract class RenderableEntityMixin extends GgEntity {
3
+ private _visible;
4
+ get visible(): boolean;
5
+ get worldVisible(): boolean;
6
+ set visible(value: boolean);
7
+ updateVisibility(): void;
8
+ addChildren(...entities: GgEntity[]): void;
9
+ removeChildren(entities: GgEntity[], dispose?: boolean): void;
10
+ }
@@ -0,0 +1,57 @@
1
+ import { GgEntity } from '../gg-entity';
2
+ const updateRecv = (item) => {
3
+ if (!!item.updateVisibility) {
4
+ item.updateVisibility();
5
+ }
6
+ else {
7
+ updateChildrenRecv(item);
8
+ }
9
+ };
10
+ const updateChildrenRecv = (item) => {
11
+ for (const child of item.children) {
12
+ updateRecv(child);
13
+ }
14
+ };
15
+ export class RenderableEntityMixin extends GgEntity {
16
+ constructor() {
17
+ super(...arguments);
18
+ this._visible = true;
19
+ }
20
+ get visible() {
21
+ return this._visible;
22
+ }
23
+ get worldVisible() {
24
+ let item = this;
25
+ while (true) {
26
+ if (item.visible === false) {
27
+ return false;
28
+ }
29
+ if (!item.parent) {
30
+ break;
31
+ }
32
+ item = item.parent;
33
+ }
34
+ return true;
35
+ }
36
+ set visible(value) {
37
+ this._visible = value;
38
+ this.updateVisibility();
39
+ }
40
+ updateVisibility() {
41
+ updateChildrenRecv(this);
42
+ }
43
+ addChildren(...entities) {
44
+ super.addChildren(...entities);
45
+ for (const entity of entities) {
46
+ updateRecv(entity);
47
+ }
48
+ }
49
+ removeChildren(entities, dispose = false) {
50
+ super.removeChildren(entities, dispose);
51
+ if (!dispose) {
52
+ for (const entity of entities) {
53
+ updateRecv(entity);
54
+ }
55
+ }
56
+ }
57
+ }
@@ -3,6 +3,7 @@ import { GgEntity } from './entities/gg-entity';
3
3
  import { GgPhysicsWorld } from './interfaces/gg-physics-world';
4
4
  import { GgVisualScene } from './interfaces/gg-visual-scene';
5
5
  import { KeyboardInput } from './inputs/keyboard.input';
6
+ import { RenderableEntityMixin } from './entities/mixins/renderable-entity.mixin';
6
7
  import { GgPositionableEntity } from './entities/gg-positionable-entity';
7
8
  export declare abstract class GgWorld<D, R, V extends GgVisualScene<D, R> = GgVisualScene<D, R>, P extends GgPhysicsWorld<D, R> = GgPhysicsWorld<D, R>> {
8
9
  readonly visualScene: V;
@@ -22,7 +23,7 @@ export declare abstract class GgWorld<D, R, V extends GgVisualScene<D, R> = GgVi
22
23
  get worldTime(): number;
23
24
  createClock(autoStart: boolean): PausableClock;
24
25
  dispose(): void;
25
- abstract addPrimitiveRigidBody(descr: any, position?: D, rotation?: R): GgPositionableEntity<D, R>;
26
+ abstract addPrimitiveRigidBody(descr: any, position?: D, rotation?: R): GgPositionableEntity<D, R> & RenderableEntityMixin;
26
27
  addEntity(entity: GgEntity): void;
27
28
  removeEntity(entity: GgEntity, dispose?: boolean): void;
28
29
  protected commands: {
@@ -27,8 +27,9 @@ export class GgWorld {
27
27
  this.commands = {};
28
28
  GgStatic.instance.worlds.push(this);
29
29
  GgStatic.instance.selectedWorld = this;
30
- this.keyboardInput.start().then();
30
+ this.keyboardInput.start();
31
31
  if (consoleEnabled) {
32
+ // TODO this listener should be outside of the world
32
33
  this.keyboardInput
33
34
  .bind('Backquote')
34
35
  .pipe(filter(x => x))
@@ -120,7 +121,7 @@ export class GgWorld {
120
121
  }
121
122
  dispose() {
122
123
  this.worldClock.stop();
123
- this.keyboardInput.stop().then();
124
+ this.keyboardInput.stop();
124
125
  for (let i = 0; i < this.children.length; i++) {
125
126
  this.children[i].onRemoved();
126
127
  this.children[i].dispose();
@@ -140,7 +141,7 @@ export class GgWorld {
140
141
  this.tickListeners.sort((l1, l2) => l1.tickOrder - l2.tickOrder);
141
142
  entity.onSpawned(this);
142
143
  }
143
- removeEntity(entity, dispose = true) {
144
+ removeEntity(entity, dispose = false) {
144
145
  if (entity.world) {
145
146
  if (entity.world !== this) {
146
147
  throw new Error('Entity is not a part of this world');
@@ -37,5 +37,5 @@ export declare class DirectionKeyboardInput extends Input {
37
37
  /**
38
38
  * Called when the input handling should start.
39
39
  */
40
- protected startInternal(): Promise<void>;
40
+ protected startInternal(): void;
41
41
  }
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { combineLatest, Subject, takeUntil } from 'rxjs';
11
2
  import { map } from 'rxjs/operators';
12
3
  import { Input } from './input';
@@ -40,42 +31,40 @@ export class DirectionKeyboardInput extends Input {
40
31
  * Called when the input handling should start.
41
32
  */
42
33
  startInternal() {
43
- return __awaiter(this, void 0, void 0, function* () {
44
- // Initialize an array to hold the keys to listen for
45
- const keys = [[], [], [], []];
46
- // Add the "wasd" keys to the array if specified in the keymap
47
- if (this.keymap.includes('wasd')) {
48
- keys[0].push('KeyW');
49
- keys[1].push('KeyA');
50
- keys[2].push('KeyS');
51
- keys[3].push('KeyD');
34
+ // Initialize an array to hold the keys to listen for
35
+ const keys = [[], [], [], []];
36
+ // Add the "wasd" keys to the array if specified in the keymap
37
+ if (this.keymap.includes('wasd')) {
38
+ keys[0].push('KeyW');
39
+ keys[1].push('KeyA');
40
+ keys[2].push('KeyS');
41
+ keys[3].push('KeyD');
42
+ }
43
+ // Add the arrow keys to the array if specified in the keymap
44
+ if (this.keymap.includes('arrows')) {
45
+ keys[0].push('ArrowUp');
46
+ keys[1].push('ArrowLeft');
47
+ keys[2].push('ArrowDown');
48
+ keys[3].push('ArrowRight');
49
+ }
50
+ // Bind to the keyboard events for the specified keys
51
+ combineLatest(keys.map(x => this.keyboard.bindMany(...x)))
52
+ .pipe(
53
+ // Stop listening when the `stop$` signal is received
54
+ takeUntil(this.stop$),
55
+ // Map the key states to a `DirectionKeyboardOutput` object
56
+ map(moveDirection => {
57
+ const result = {};
58
+ if (moveDirection.includes(true)) {
59
+ const [f, l, b, r] = moveDirection;
60
+ if (f != b)
61
+ result.upDown = f;
62
+ if (l != r)
63
+ result.leftRight = l;
52
64
  }
53
- // Add the arrow keys to the array if specified in the keymap
54
- if (this.keymap.includes('arrows')) {
55
- keys[0].push('ArrowUp');
56
- keys[1].push('ArrowLeft');
57
- keys[2].push('ArrowDown');
58
- keys[3].push('ArrowRight');
59
- }
60
- // Bind to the keyboard events for the specified keys
61
- combineLatest(keys.map(x => this.keyboard.bindMany(...x)))
62
- .pipe(
63
- // Stop listening when the `stop$` signal is received
64
- takeUntil(this.stop$),
65
- // Map the key states to a `DirectionKeyboardOutput` object
66
- map(moveDirection => {
67
- const result = {};
68
- if (moveDirection.includes(true)) {
69
- const [f, l, b, r] = moveDirection;
70
- if (f != b)
71
- result.upDown = f;
72
- if (l != r)
73
- result.leftRight = l;
74
- }
75
- return result;
76
- }))
77
- // Emit the resulting `DirectionKeyboardOutput` object through the `_output$` subject
78
- .subscribe(this._output$);
79
- });
65
+ return result;
66
+ }))
67
+ // Emit the resulting `DirectionKeyboardOutput` object through the `_output$` subject
68
+ .subscribe(o => this._output$.next(o));
80
69
  }
81
70
  }
@@ -24,27 +24,25 @@ export declare abstract class Input<TStartParams extends any[] = [], TStopParams
24
24
  */
25
25
  get running(): boolean;
26
26
  /**
27
- * An asynchronous method that starts the input. Do not override it
27
+ * A method that starts the input. Do not override it
28
28
  * @param args - An array of input arguments for the start method.
29
29
  * @returns A Promise that resolves when the input is started.
30
30
  */
31
- start(...args: TStartParams): Promise<void>;
31
+ start(...args: TStartParams): void;
32
32
  /**
33
- * An asynchronous method that stops the input. Do not override it
33
+ * A method that stops the input. Do not override it
34
34
  * @param args - An array of input arguments for the stop method.
35
- * @returns A Promise that resolves when the input is stopped.
36
35
  */
37
- stop(...args: TStopParams): Promise<void>;
36
+ stop(...args: TStopParams): void;
38
37
  /**
39
- * An abstract asynchronous method that starts the input.
38
+ * An abstract method that starts the input.
40
39
  * @param args - An array of input arguments for the start method.
41
- * @returns A Promise that resolves when the process is started.
42
40
  */
43
- protected abstract startInternal(...args: TStartParams): Promise<void>;
41
+ protected abstract startInternal(...args: TStartParams): void;
44
42
  /**
45
- * An asynchronous method that stops the input.
43
+ * A method that stops the input.
46
44
  * @param args - An array of input arguments for the stop method.
47
45
  * @returns A Promise that resolves when the process is stopped.
48
46
  */
49
- protected stopInternal(...args: TStopParams): Promise<void>;
47
+ protected stopInternal(...args: TStopParams): void;
50
48
  }
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { Subject } from 'rxjs';
11
2
  /**
12
3
  * An abstract class that provides basic implementation for Input class.
@@ -37,40 +28,33 @@ export class Input {
37
28
  return this._running;
38
29
  }
39
30
  /**
40
- * An asynchronous method that starts the input. Do not override it
31
+ * A method that starts the input. Do not override it
41
32
  * @param args - An array of input arguments for the start method.
42
33
  * @returns A Promise that resolves when the input is started.
43
34
  */
44
35
  start(...args) {
45
- return __awaiter(this, void 0, void 0, function* () {
46
- if (this.running) {
47
- return;
48
- }
49
- yield this.startInternal(...args);
50
- this._running = true;
51
- });
36
+ if (this.running) {
37
+ return;
38
+ }
39
+ this.startInternal(...args);
40
+ this._running = true;
52
41
  }
53
42
  /**
54
- * An asynchronous method that stops the input. Do not override it
43
+ * A method that stops the input. Do not override it
55
44
  * @param args - An array of input arguments for the stop method.
56
- * @returns A Promise that resolves when the input is stopped.
57
45
  */
58
46
  stop(...args) {
59
- return __awaiter(this, void 0, void 0, function* () {
60
- if (!this.running) {
61
- return;
62
- }
63
- this.stop$.next();
64
- yield this.stopInternal(...args);
65
- this._running = false;
66
- });
47
+ if (!this.running) {
48
+ return;
49
+ }
50
+ this.stop$.next();
51
+ this.stopInternal(...args);
52
+ this._running = false;
67
53
  }
68
54
  /**
69
- * An asynchronous method that stops the input.
55
+ * A method that stops the input.
70
56
  * @param args - An array of input arguments for the stop method.
71
57
  * @returns A Promise that resolves when the process is stopped.
72
58
  */
73
- stopInternal(...args) {
74
- return __awaiter(this, void 0, void 0, function* () { });
75
- }
59
+ stopInternal(...args) { }
76
60
  }
@@ -7,12 +7,22 @@ import { Observable } from 'rxjs';
7
7
  */
8
8
  export declare class KeyboardInput extends Input {
9
9
  private bindings;
10
+ /**
11
+ * Flag which disables handling key downs, when document has some "typeable" element focused
12
+ */
13
+ skipKeyDownsOnExternalFocus: boolean;
14
+ /**
15
+ * Which element types should filter key downs when focused
16
+ */
17
+ externalFocusBlacklist: {
18
+ new (): HTMLElement;
19
+ }[];
10
20
  /**
11
21
  * Creates a new instance of the `KeyboardInput` class.
12
22
  */
13
23
  constructor();
14
- protected startInternal(): Promise<void>;
15
- protected stopInternal(): Promise<void>;
24
+ protected startInternal(): void;
25
+ protected stopInternal(): void;
16
26
  /**
17
27
  * Creates an observable that emits a boolean whenever a key with the given code is pressed or released
18
28
  * @param code The key code to bind the observable to
@@ -42,4 +52,6 @@ export declare class KeyboardInput extends Input {
42
52
  */
43
53
  emulateKeyPress(code: string): void;
44
54
  private handleKeys;
55
+ private onPointerLockChange;
56
+ resetAllKeys(): void;
45
57
  }
@@ -1,15 +1,6 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { Input } from './input';
11
2
  import { BehaviorSubject, combineLatest, finalize, NEVER } from 'rxjs';
12
- import { map } from 'rxjs/operators';
3
+ import { distinctUntilChanged, map } from 'rxjs/operators';
13
4
  /**
14
5
  * A main keyboard input: it does not have own key bindings, but provides an API to bind keys.
15
6
  * It is responsible for listening key up/down events (when running!) and emit the events to subscribers.
@@ -22,19 +13,35 @@ export class KeyboardInput extends Input {
22
13
  constructor() {
23
14
  super();
24
15
  this.bindings = {};
16
+ /**
17
+ * Flag which disables handling key downs, when document has some "typeable" element focused
18
+ */
19
+ this.skipKeyDownsOnExternalFocus = true;
20
+ /**
21
+ * Which element types should filter key downs when focused
22
+ */
23
+ this.externalFocusBlacklist = [
24
+ HTMLInputElement,
25
+ HTMLTextAreaElement,
26
+ HTMLSelectElement,
27
+ HTMLButtonElement,
28
+ ];
25
29
  this.handleKeys = this.handleKeys.bind(this);
30
+ this.resetAllKeys = this.resetAllKeys.bind(this);
31
+ this.onPointerLockChange = this.onPointerLockChange.bind(this);
26
32
  }
27
33
  startInternal() {
28
- return __awaiter(this, void 0, void 0, function* () {
29
- window.addEventListener('keydown', this.handleKeys);
30
- window.addEventListener('keyup', this.handleKeys);
31
- });
34
+ window.addEventListener('keydown', this.handleKeys);
35
+ window.addEventListener('keyup', this.handleKeys);
36
+ window.addEventListener('blur', this.resetAllKeys);
37
+ document.addEventListener('pointerlockchange', this.onPointerLockChange);
32
38
  }
33
39
  stopInternal() {
34
- return __awaiter(this, void 0, void 0, function* () {
35
- window.removeEventListener('keydown', this.handleKeys);
36
- window.removeEventListener('keyup', this.handleKeys);
37
- });
40
+ window.removeEventListener('keydown', this.handleKeys);
41
+ window.removeEventListener('keyup', this.handleKeys);
42
+ window.removeEventListener('blur', this.resetAllKeys);
43
+ document.addEventListener('pointerlockchange', this.onPointerLockChange);
44
+ this.resetAllKeys();
38
45
  }
39
46
  /**
40
47
  * Creates an observable that emits a boolean whenever a key with the given code is pressed or released
@@ -47,7 +54,7 @@ export class KeyboardInput extends Input {
47
54
  }
48
55
  const subj = new BehaviorSubject(false);
49
56
  this.bindings[code].push(subj);
50
- return subj.pipe(finalize(() => {
57
+ return subj.pipe(distinctUntilChanged(), finalize(() => {
51
58
  this.bindings[code].splice(this.bindings[code].indexOf(subj), 1);
52
59
  subj.complete();
53
60
  }));
@@ -80,7 +87,7 @@ export class KeyboardInput extends Input {
80
87
  this.bindings[codes[i]].splice(this.bindings[codes[i]].indexOf(subjects[i]), 1);
81
88
  subjects[i].complete();
82
89
  }
83
- }), map(values => values.includes(true)));
90
+ }), map(values => values.includes(true)), distinctUntilChanged());
84
91
  }
85
92
  /**
86
93
  * Emulates a key down event for the given key code
@@ -119,8 +126,28 @@ export class KeyboardInput extends Input {
119
126
  return;
120
127
  }
121
128
  const pressed = e.type == 'keydown';
129
+ if (pressed && this.skipKeyDownsOnExternalFocus && document.activeElement) {
130
+ for (const k of this.externalFocusBlacklist) {
131
+ if (document.activeElement instanceof k) {
132
+ return;
133
+ }
134
+ }
135
+ }
122
136
  for (const subj of this.bindings[e.code] || []) {
123
137
  subj.next(pressed);
124
138
  }
125
139
  }
140
+ onPointerLockChange() {
141
+ // In chrome, if we press key, then exit pointer lock with Escape key, and then release key, the event will not be fired and key will "stuck" in down state
142
+ if (!document.pointerLockElement) {
143
+ this.resetAllKeys();
144
+ }
145
+ }
146
+ resetAllKeys() {
147
+ for (const code in this.bindings) {
148
+ for (const subj of this.bindings[code] || []) {
149
+ subj.next(false);
150
+ }
151
+ }
152
+ }
126
153
  }
@@ -56,13 +56,13 @@ export declare class MouseInput extends Input<[], [unlockPointer?: boolean]> {
56
56
  /**
57
57
  An observable of the wheel scrolling.
58
58
  */
59
+ private _wheel$;
59
60
  get wheel$(): Observable<number>;
60
61
  get isPointerLocked(): boolean;
61
62
  private readonly options;
62
63
  private _delta$;
63
64
  private _position$;
64
65
  private _multiTouchPositions$;
65
- private _wheel$;
66
66
  private stopped$;
67
67
  private _state$;
68
68
  get state(): MouseInputState;
@@ -79,12 +79,12 @@ export declare class MouseInput extends Input<[], [unlockPointer?: boolean]> {
79
79
  @param {MouseInputOptions} options - The options for the MouseInput.
80
80
  */
81
81
  constructor(options?: Partial<MouseInputOptions>);
82
- protected startInternal(): Promise<void>;
82
+ protected startInternal(): void;
83
83
  /**
84
84
  Stop listening for mouse movement events.
85
85
  @param {boolean} [unlockPointer=true] - Whether to exit pointer lock.
86
86
  */
87
- protected stopInternal(unlockPointer?: boolean): Promise<void>;
87
+ protected stopInternal(unlockPointer?: boolean): void;
88
88
  /**
89
89
  Request pointer lock on the canvas element.
90
90
  */