From 4af37e09fae9f62023545999cd833de3c3c80111 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Tue, 8 Mar 2022 22:42:32 -0600 Subject: [PATCH 1/2] fix: #2263 keyboard `wasPressed` works in `onPostUpdate` --- CHANGELOG.md | 2 ++ sandbox/tests/input/keyboard.ts | 4 ++++ src/engine/Engine.ts | 6 +++--- src/engine/Input/Keyboard.ts | 25 ++++++++++++++++++++++++- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a011aaa1e..d76afacc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +- Fixed issue #2263 where keyboard input `wasPressed` was not working in the `onPostUpdate` lifecycle +- Fixed issue #2263 where there were some keys missing from the `ex.Input.Keys` enum, including `Enter` - Fixed issue where Rectangle line renderer did not respect z order ### Updates diff --git a/sandbox/tests/input/keyboard.ts b/sandbox/tests/input/keyboard.ts index 7b0c7d0dc..bee632390 100644 --- a/sandbox/tests/input/keyboard.ts +++ b/sandbox/tests/input/keyboard.ts @@ -16,6 +16,10 @@ game.on('postupdate', (ue: ex.PostUpdateEvent) => { .join(', '); label.text = keys; + + if (game.input.keyboard.wasPressed(ex.Input.Keys.Enter)) { + console.log("Enter Pressed"); + } }); game.start(); diff --git a/src/engine/Engine.ts b/src/engine/Engine.ts index 81117b25d..d786863cb 100644 --- a/src/engine/Engine.ts +++ b/src/engine/Engine.ts @@ -1074,12 +1074,12 @@ O|===|* >________________>\n\ return !a.animation.isDone(); }); + // Publish update event + this._postupdate(delta); + // Update input listeners this.input.keyboard.update(); this.input.gamepads.update(); - - // Publish update event - this._postupdate(delta); } /** diff --git a/src/engine/Input/Keyboard.ts b/src/engine/Input/Keyboard.ts index 104ff76af..91c1f8b9b 100644 --- a/src/engine/Input/Keyboard.ts +++ b/src/engine/Input/Keyboard.ts @@ -46,6 +46,10 @@ export enum Keys { ShiftRight = 'ShiftRight', AltLeft = 'AltLeft', AltRight = 'AltRight', + ControlLeft = 'ControlLeft', + ControlRight = 'ControlRight', + MetaLeft = 'MetaLeft', + MetaRight = 'MetaRight', // NUMBERS Key0 = 'Digit0', @@ -69,6 +73,20 @@ export enum Keys { Digit8 = 'Digit8', Digit9 = 'Digit9', + // FUNCTION KEYS + F1 = 'F1', + F2 = 'F2', + F3 = 'F3', + F4 = 'F4', + F5 = 'F5', + F6 = 'F6', + F7 = 'F7', + F8 = 'F8', + F9 = 'F9', + F10 = 'F10', + F11 = 'F11', + F12 = 'F12', + // LETTERS A = 'KeyA', B = 'KeyB', @@ -148,8 +166,13 @@ export enum Keys { // OTHER Space = 'Space', + Backspace = 'Backspace', + Delete = 'Delete', Esc = 'Escape', - Escape = 'Escape' + Escape = 'Escape', + Enter = 'Enter', + NumpadEnter = 'NumpadEnter', + ContextMenu = 'ContextMenu' } /** From 016c3b64590c96a41b21540a717725093e80c12f Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Fri, 11 Mar 2022 21:42:04 -0600 Subject: [PATCH 2/2] add tests --- src/engine/Input/Keyboard.ts | 67 +++++++++++++++++++++++++----------- src/spec/EngineSpec.ts | 55 +++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 21 deletions(-) diff --git a/src/engine/Input/Keyboard.ts b/src/engine/Input/Keyboard.ts index 91c1f8b9b..484fd5f96 100644 --- a/src/engine/Input/Keyboard.ts +++ b/src/engine/Input/Keyboard.ts @@ -245,31 +245,35 @@ export class Keyboard extends Class { }); // key up is on window because canvas cannot have focus - global.addEventListener('keyup', (ev: KeyboardEvent) => { - const code = ev.code as Keys; - const key = this._keys.indexOf(code); - this._keys.splice(key, 1); - this._keysUp.push(code); - const keyEvent = new KeyEvent(code, ev.key, ev); - - // alias the old api, we may want to deprecate this in the future - this.eventDispatcher.emit('up', keyEvent); - this.eventDispatcher.emit('release', keyEvent); - }); + global.addEventListener('keyup', this._handleKeyUp); // key down is on window because canvas cannot have focus - global.addEventListener('keydown', (ev: KeyboardEvent) => { - const code = ev.code as Keys; - if (this._keys.indexOf(code) === -1) { - this._keys.push(code); - this._keysDown.push(code); - const keyEvent = new KeyEvent(code, ev.key, ev); - this.eventDispatcher.emit('down', keyEvent); - this.eventDispatcher.emit('press', keyEvent); - } - }); + global.addEventListener('keydown', this._handleKeyDown); } + private _handleKeyDown = (ev: KeyboardEvent) => { + const code = ev.code as Keys; + if (this._keys.indexOf(code) === -1) { + this._keys.push(code); + this._keysDown.push(code); + const keyEvent = new KeyEvent(code, ev.key, ev); + this.eventDispatcher.emit('down', keyEvent); + this.eventDispatcher.emit('press', keyEvent); + } + }; + + private _handleKeyUp = (ev: KeyboardEvent) => { + const code = ev.code as Keys; + const key = this._keys.indexOf(code); + this._keys.splice(key, 1); + this._keysUp.push(code); + const keyEvent = new KeyEvent(code, ev.key, ev); + + // alias the old api, we may want to deprecate this in the future + this.eventDispatcher.emit('up', keyEvent); + this.eventDispatcher.emit('release', keyEvent); + }; + public update() { // Reset keysDown and keysUp after update is complete this._keysDown.length = 0; @@ -311,4 +315,25 @@ export class Keyboard extends Class { public wasReleased(key: Keys): boolean { return this._keysUp.indexOf(key) > -1; } + + /** + * Trigger a manual key event + * @param type + * @param key + * @param character + */ + public triggerEvent(type: 'down' | 'up', key: Keys, character?: string) { + if (type === 'down') { + this._handleKeyDown(new KeyboardEvent('keydown', { + code: key, + key: character ?? null + })); + } + if (type === 'up') { + this._handleKeyUp(new KeyboardEvent('keyup', { + code: key, + key: character ?? null + })); + } + } } diff --git a/src/spec/EngineSpec.ts b/src/spec/EngineSpec.ts index 71d78f966..cb8774919 100644 --- a/src/spec/EngineSpec.ts +++ b/src/spec/EngineSpec.ts @@ -195,6 +195,61 @@ describe('The engine', () => { expect(fired).toHaveBeenCalledTimes(2); }); + it('will update keyboard & gamepad events after postupdate', () => { + const postupdate = jasmine.createSpy('postupdate'); + spyOn(engine.input.keyboard, 'update').and.callThrough(); + spyOn(engine.input.gamepads, 'update').and.callThrough(); + engine.on('postupdate', postupdate); + + const clock = engine.clock as ex.TestClock; + + clock.step(1); + + expect(postupdate).toHaveBeenCalledBefore(engine.input.keyboard.update); + expect(postupdate).toHaveBeenCalledBefore(engine.input.gamepads.update); + }); + + it('will fire wasPressed in onPostUpdate handler', (done) => { + engine.input.keyboard.triggerEvent('down', ex.Input.Keys.Enter); + engine.on('postupdate', () => { + if (engine.input.keyboard.wasPressed(ex.Input.Keys.Enter)) { + done(); + } + }); + + const clock = engine.clock as ex.TestClock; + clock.step(1); + }); + + it('will fire wasReleased in onPostUpdate handler', (done) => { + engine.input.keyboard.triggerEvent('up', ex.Input.Keys.Enter); + engine.on('postupdate', () => { + if (engine.input.keyboard.wasReleased(ex.Input.Keys.Enter)) { + done(); + } + }); + + const clock = engine.clock as ex.TestClock; + clock.step(1); + }); + + it('will fire isHeld in onPostUpdate handler', () => { + engine.input.keyboard.triggerEvent('down', ex.Input.Keys.Enter); + const held = jasmine.createSpy('held'); + engine.on('postupdate', () => { + if (engine.input.keyboard.isHeld(ex.Input.Keys.Enter)) { + held(); + } + }); + + const clock = engine.clock as ex.TestClock; + clock.step(1); + clock.step(1); + clock.step(1); + + expect(held).toHaveBeenCalledTimes(3); + }); + it('should emit a predraw event', () => { const fired = jasmine.createSpy('fired'); engine.on('predraw', fired);