123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- import { Observable } from "../Misc/observable";
- import { Engine } from '../Engines/engine';
- import { IDisposable } from '../scene';
- import { Nullable } from '../types';
- /**
- * This class will take all inputs from Keyboard, Pointer, and
- * any Gamepads and provide a polling system that all devices
- * will use. This class assumes that there will only be one
- * pointer device and one keyboard.
- */
- export class DeviceInputSystem implements IDisposable {
- // Static
- /** POINTER_DEVICE */
- public static readonly POINTER_DEVICE: string = "Pointer";
- /** KEYBOARD_DEVICE */
- public static readonly KEYBOARD_DEVICE: string = "Keyboard";
- /**
- * Observable to be triggered when a device is connected
- */
- public onDeviceConnectedObservable = new Observable<string>();
- /**
- * Observable to be triggered when a device is disconnected
- */
- public onDeviceDisconnectedObservable = new Observable<string>();
- // Private Members
- private _inputs: { [key: string]: Array<Nullable<number>> } = {};
- private _gamepads: Array<string>;
- private _keyboardActive: boolean = false;
- private _pointerActive: boolean = false;
- private _elementToAttachTo: HTMLElement;
- private _keyboardDownEvent = (evt: any) => { };
- private _keyboardUpEvent = (evt: any) => { };
- private _pointerMoveEvent = (evt: any) => { };
- private _pointerDownEvent = (evt: any) => { };
- private _pointerUpEvent = (evt: any) => { };
- private _gamepadConnectedEvent = (evt: any) => { };
- private _gamepadDisconnectedEvent = (evt: any) => { };
- private static _MAX_KEYCODES: number = 222;
- private static _MAX_POINTER_INPUTS: number = 7;
- /**
- * Default Constructor
- * @param engine - engine to pull input element from
- */
- constructor(engine: Engine) {
- const inputElement = engine.getInputElement();
- if (inputElement) {
- this._elementToAttachTo = inputElement;
- this._handleKeyActions();
- this._handlePointerActions();
- this._handleGamepadActions();
- }
- }
- // Public functions
- /**
- * Checks for current device input value, given an id and input index
- * @param deviceName Id of connected device
- * @param inputIndex Index of device input
- * @returns Current value of input
- */
- public pollInput(deviceName: string, inputIndex: number): Nullable<number> {
- const device = this._inputs[deviceName];
- if (!device) {
- throw `Unable to find device ${deviceName}`;
- }
- this._updateDevice(deviceName, inputIndex);
- if (device[inputIndex] === undefined) {
- throw `Unable to find input ${inputIndex} on device ${deviceName}`;
- }
- return device[inputIndex];
- }
- /**
- * Dispose of all the eventlisteners and clears the observables
- */
- public dispose() {
- this.onDeviceConnectedObservable.clear();
- this.onDeviceDisconnectedObservable.clear();
- // Keyboard Events
- if (this._keyboardActive) {
- window.removeEventListener("keydown", this._keyboardDownEvent);
- window.removeEventListener("keyup", this._keyboardUpEvent);
- }
- // Pointer Events
- if (this._pointerActive) {
- this._elementToAttachTo.removeEventListener("pointermove", this._pointerMoveEvent);
- this._elementToAttachTo.removeEventListener("pointerdown", this._pointerDownEvent);
- this._elementToAttachTo.removeEventListener("pointerup", this._pointerUpEvent);
- }
- // Gamepad Events
- window.removeEventListener("gamepadconnected", this._gamepadConnectedEvent);
- window.removeEventListener("gamepaddisconnected", this._gamepadDisconnectedEvent);
- }
- // Private functions
- /**
- * Add device and inputs to device map
- * @param deviceName Assigned name of device (may be SN)
- * @param numberOfInputs Number of input entries to create for given device
- */
- private _registerDevice(deviceName: string, numberOfInputs: number) {
- if (!this._inputs[deviceName]) {
- const device = new Array<Nullable<number>>(numberOfInputs);
- for (let i = 0; i < numberOfInputs; i++) {
- device[i] = null;
- }
- this._inputs[deviceName] = device;
- this.onDeviceConnectedObservable.notifyObservers(deviceName);
- }
- }
- /**
- * Given a specific device name, remove that device from the device map
- * @param deviceName Name of device to be removed
- */
- private _unregisterDevice(deviceName: string) {
- if (this._inputs[deviceName]) {
- delete this._inputs[deviceName];
- this.onDeviceDisconnectedObservable.notifyObservers(deviceName);
- }
- }
- /**
- * Handle all actions that come from keyboard interaction
- */
- private _handleKeyActions() {
- this._keyboardDownEvent = ((evt) => {
- if (!this._keyboardActive) {
- this._keyboardActive = true;
- this._registerDevice(DeviceInputSystem.KEYBOARD_DEVICE, DeviceInputSystem._MAX_KEYCODES);
- }
- const kbKey = this._inputs[DeviceInputSystem.KEYBOARD_DEVICE];
- if (kbKey) {
- kbKey[evt.keyCode] = 1;
- }
- });
- this._keyboardUpEvent = ((evt) => {
- const kbKey = this._inputs[DeviceInputSystem.KEYBOARD_DEVICE];
- if (kbKey) {
- kbKey[evt.keyCode] = 0;
- }
- });
- window.addEventListener("keydown", this._keyboardDownEvent);
- window.addEventListener("keyup", this._keyboardUpEvent);
- }
- /**
- * Handle all actions that come from pointer interaction
- */
- private _handlePointerActions() {
- this._pointerMoveEvent = ((evt) => {
- const deviceName = `${DeviceInputSystem.POINTER_DEVICE}-${evt.pointerId}`;
- if (!this._pointerActive) {
- this._pointerActive = true;
- this._registerDevice(deviceName, DeviceInputSystem._MAX_POINTER_INPUTS);
- }
- const pointer = this._inputs[deviceName];
- if (pointer) {
- pointer[0] = evt.clientX;
- pointer[1] = evt.clientY;
- }
- });
- this._pointerDownEvent = ((evt) => {
- const deviceName = `${DeviceInputSystem.POINTER_DEVICE}-${evt.pointerId}`;
- if (!this._pointerActive) {
- this._pointerActive = true;
- this._registerDevice(deviceName, DeviceInputSystem._MAX_POINTER_INPUTS);
- }
- const pointer = this._inputs[deviceName];
- if (pointer) {
- pointer[0] = evt.clientX;
- pointer[1] = evt.clientY;
- pointer[evt.button + 2] = 1;
- }
- });
- this._pointerUpEvent = ((evt) => {
- const deviceName = `${DeviceInputSystem.POINTER_DEVICE}-${evt.pointerId}`;
- const pointer = this._inputs[deviceName];
- if (pointer) {
- pointer[evt.button + 2] = 0;
- }
- if (evt.pointerId != 1) // Don't unregister the mouse
- {
- this._unregisterDevice(deviceName);
- }
- });
- this._elementToAttachTo.addEventListener("pointermove", this._pointerMoveEvent);
- this._elementToAttachTo.addEventListener("pointerdown", this._pointerDownEvent);
- this._elementToAttachTo.addEventListener("pointerup", this._pointerUpEvent);
- }
- /**
- * Handle all actions that come from gamepad interaction
- */
- private _handleGamepadActions() {
- this._gamepadConnectedEvent = ((evt: any) => {
- const deviceName = `${evt.gamepad.id}-${evt.gamepad.index}`;
- this._registerDevice(deviceName, evt.gamepad.buttons.length + evt.gamepad.axes.length);
- this._gamepads = this._gamepads || new Array<string>(evt.gamepad.index + 1);
- this._gamepads[evt.gamepad.index] = deviceName;
- });
- this._gamepadDisconnectedEvent = ((evt: any) => {
- const deviceName = this._gamepads[evt.gamepad.index];
- this._unregisterDevice(deviceName);
- delete this._gamepads[evt.gamepad.index];
- });
- window.addEventListener("gamepadconnected", this._gamepadConnectedEvent);
- window.addEventListener("gamepaddisconnected", this._gamepadDisconnectedEvent);
- }
- /**
- * Update all non-event based devices with each frame
- */
- private _updateDevice(deviceName: string, inputIndex: number) {
- // Gamepads
- const gamepads = navigator.getGamepads();
- // Look for current gamepad and get updated values
- for (const gp of gamepads) {
- if (gp && deviceName == this._gamepads[gp.index]) {
- const device = this._inputs[deviceName];
- if (inputIndex >= gp.buttons.length) {
- device[inputIndex] = gp.axes[inputIndex - gp.buttons.length].valueOf();
- }
- else {
- device[inputIndex] = gp.buttons[inputIndex].value;
- }
- }
- }
- }
- }
|