excaliburjs/Excalibur — pull request #349 by kamranayub (2014-11-09 23:35:10+00:00)
Title: New Input API with Pointer, Gamepad, and Keyboard support

These are part of the new core rewrite. I cleaned up the input management in Excalibur and added Gamepad API support.

## Major Changes

- **Access to global input through `Engine.input`.** Keyboard, pointer, and Gamepad input is now accessible through the `Engine.input` property. You can subscribe to events or poll the status of certain keys or buttons in the case of keyboard/gamepad.
- **No longer propagate `key*` events to Actors.** The engine no longer propagates key events to Actors. This is because we will be moving to an engine singleton, so it seems like a waste of processing to propagate those events when you can listen to them globally. Also, when you listen for an event on an actor, it implies that event is scoped to them which isn't true for keyboard events, only Pointer events.
- **Simplified mouse/touch/stylus to Pointer Events.** All pointer events including mouse and touch have been normalized to conform to the W3C Pointer Events spec. The events currently implemented are `pointerup`, `pointerdown`, `pointermove`, and `pointercancel`. These events do not have the `pointer` prefix when listened to on the `Engine.input.pointer` event manager.
- **Actors do not propagate pointer events by default.** In order to reduce processing and un-needed overhead, actors opt-out of pointer event handling by default. You can opt-in by setting `Actor.inputEnabled` to `true`. Furthermore, they will not receive `move` events by default even with input enabled due to the extra processing overhead. You can opt-in to move events by setting `Actor.inputEnableMoveEvents` to `true`. This will capture `pointermove` events if they occur inside the bounding box of the actor.
- **Renamed `ex.InputKey` to `ex.Input.Keys` enumeration.** I moved the old input key enum to the Input namespace and changed the name to `Keys`.
- **Gamepad support.** Added Gamepad API support for Chrome 22+ and FireFox.

## Minor Changes

- Added `ex.Input.Buttons` and `ex.Input.Axes` enums for Gamepad.
- Removed old Mouse/Touch/Keyboard events. New events are `PointerEvent`, `KeyEvent`, `GamepadButtonEvent`, and `GamepadAxisEvent`.

## TODO

- [ ] Add multiple pointer (multi-touch) support
- [ ] Add 4 gamepads by default so you don't have to always check if pads exist
- [ ] Add ability to check pointer type and button type to handle mouse/touch explicitly

## Usage

### Keyboard

Access API using `Engine.input.keyboard`. You can then query key status using `isKeyDown`, `isKeyUp`, and `isKeyPressed`. You can also retrieve a list of all pressed keys using `getKeys`.

Keyboard publishes two events, `up` and `down` that are of type `KeyEvent`.

```js
game.input.keyboard.on("up", (ke: ex.Input.KeyEvent) => {
if (ke.key === ex.Input.Keys.Left) {
// etc
}
});
```

### Pointer

Access API using `Engine.input.pointer`. You can then listen to the events `up`, `down`, `move`, and `cancel` which are of type `PointerEvent`. Only one pointer is supported through the API right now.

If you want to opt-in an actor to receiving pointer events, enable `Actor.inputEnabled`. Events fired on Actor will be prefixed with `pointer`. Actors can also receive `move` events by setting `Actor.inputEnableMoveEvents` to `true`.

```js
game.input.pointer.on("up", (pe: ex.Input.PointerEvent) => {
// ...
});

player.enableInput = true;
player.on("pointerup", (pe: ex.Input.PointerEvent) => {
// ...
});
```

### Gamepad

Access API using `Engine.input.gamepads`. By default, the polling is disabled. Enable it by setting `Engine.input.gamepads.enabled` to `true`. You can then access individual pads via `pads` array. Before doing so, ensure you only handle Gamepad input if `count()` is greater than 0. On a gamepad, you can query the status of buttons via `isButtonPressed` and `getButton`. You can query axes via `getAxes`.

`Gamepad` publishes two events, `button` of type `GamepadButtonEvent` and `axis` of type `GamepadAxisEvent` that you can subscribe to.

```js
// Enable Gamepad support
game.input.gamepads.enabled = true;

player.on("update", () => {
var pad1 = game.input.gamepads.pads[0];

if (!pad1) return;

if (pad1.isButtonPressed(ex.Input.Buttons.DpadLeft) {
player.dx = -20;
}
});
```