Przeglądaj źródła

Merge pull request #7920 from PolygonalSun/polygonalsun/device-input

Device Input System - Step 1
David Catuhe 5 lat temu
rodzic
commit
5af096eee0

+ 1 - 0
dist/preview release/what's new.md

@@ -15,6 +15,7 @@
 
 - Simplified code contributions by fully automating the dev setup with gitpod ([nisarhassan12](https://github.com/nisarhassan12))
 - Add a `CascadedShadowMap.IsSupported` method and log an error instead of throwing an exception when CSM is not supported ([Popov72](https://github.com/Popov72))
+- Added initial code for DeviceInputSystem ([PolygonalSun](https://github.com/PolygonalSun))
 
 ### Engine
 

+ 262 - 0
src/DeviceInput/deviceInputSystem.ts

@@ -0,0 +1,262 @@
+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;
+                }
+            }
+        }
+    }
+}

+ 1 - 0
src/DeviceInput/index.ts

@@ -0,0 +1 @@
+export * from "./deviceInputSystem";

+ 1 - 0
src/index.ts

@@ -9,6 +9,7 @@ export * from "./Cameras/index";
 export * from "./Collisions/index";
 export * from "./Culling/index";
 export * from "./Debug/index";
+export * from "./DeviceInput/index";
 export * from "./Engines/index";
 export * from "./Events/index";
 export * from "./Gamepads/index";