Skip to content

Commit

Permalink
fix: #2263 keyboard wasPressed works in onPostUpdate lifecycle (#…
Browse files Browse the repository at this point in the history
…2270)

Closes #2263 

This PR adjusts the keyboard & gamepad input updates to the actual end of the frame after the `onPostUpdate` lifecycle. Additionally this adds a number of missing key codes in the `ex.Input.Keys` enum.
  • Loading branch information
eonarheim authored Mar 12, 2022
1 parent ebffe15 commit 0142834
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 25 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions sandbox/tests/input/keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
6 changes: 3 additions & 3 deletions src/engine/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
92 changes: 70 additions & 22 deletions src/engine/Input/Keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export enum Keys {
ShiftRight = 'ShiftRight',
AltLeft = 'AltLeft',
AltRight = 'AltRight',
ControlLeft = 'ControlLeft',
ControlRight = 'ControlRight',
MetaLeft = 'MetaLeft',
MetaRight = 'MetaRight',

// NUMBERS
Key0 = 'Digit0',
Expand All @@ -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',
Expand Down Expand Up @@ -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'
}

/**
Expand Down Expand Up @@ -222,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;
Expand Down Expand Up @@ -288,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
}));
}
}
}
55 changes: 55 additions & 0 deletions src/spec/EngineSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 0142834

Please sign in to comment.