gamepadManager.ts 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import { Observable } from "../Misc/observable";
  2. import { DomManagement } from "../Misc/domManagement";
  3. import { Nullable } from "../types";
  4. import { Scene } from "../scene";
  5. import { PoseEnabledControllerHelper } from "../Gamepads/Controllers/poseEnabledController";
  6. import { Xbox360Pad } from "./xboxGamepad";
  7. import { Gamepad, GenericPad } from "./gamepad";
  8. import { Engine } from '../Engines/engine';
  9. import { DualShockPad } from './dualShockGamepad';
  10. /**
  11. * Manager for handling gamepads
  12. */
  13. export class GamepadManager {
  14. private _babylonGamepads: Array<Gamepad> = [];
  15. private _oneGamepadConnected: boolean = false;
  16. /** @hidden */
  17. public _isMonitoring: boolean = false;
  18. private _gamepadEventSupported: boolean;
  19. private _gamepadSupport?: () => Array<any>;
  20. /**
  21. * observable to be triggered when the gamepad controller has been connected
  22. */
  23. public onGamepadConnectedObservable: Observable<Gamepad>;
  24. /**
  25. * observable to be triggered when the gamepad controller has been disconnected
  26. */
  27. public onGamepadDisconnectedObservable = new Observable<Gamepad>();
  28. private _onGamepadConnectedEvent: Nullable<(evt: any) => void>;
  29. private _onGamepadDisconnectedEvent: Nullable<(evt: any) => void>;
  30. /**
  31. * Initializes the gamepad manager
  32. * @param _scene BabylonJS scene
  33. */
  34. constructor(private _scene?: Scene) {
  35. if (!DomManagement.IsWindowObjectExist()) {
  36. this._gamepadEventSupported = false;
  37. } else {
  38. this._gamepadEventSupported = 'GamepadEvent' in window;
  39. this._gamepadSupport = (navigator.getGamepads ||
  40. navigator.webkitGetGamepads || navigator.msGetGamepads || navigator.webkitGamepads);
  41. }
  42. this.onGamepadConnectedObservable = new Observable<Gamepad>((observer) => {
  43. // This will be used to raise the onGamepadConnected for all gamepads ALREADY connected
  44. for (var i in this._babylonGamepads) {
  45. let gamepad = this._babylonGamepads[i];
  46. if (gamepad && gamepad._isConnected) {
  47. this.onGamepadConnectedObservable.notifyObserver(observer, gamepad);
  48. }
  49. }
  50. });
  51. this._onGamepadConnectedEvent = (evt) => {
  52. let gamepad = evt.gamepad;
  53. if (gamepad.index in this._babylonGamepads) {
  54. if (this._babylonGamepads[gamepad.index].isConnected) {
  55. return;
  56. }
  57. }
  58. let newGamepad: Gamepad;
  59. if (this._babylonGamepads[gamepad.index]) {
  60. newGamepad = this._babylonGamepads[gamepad.index];
  61. newGamepad.browserGamepad = gamepad;
  62. newGamepad._isConnected = true;
  63. } else {
  64. newGamepad = this._addNewGamepad(gamepad);
  65. }
  66. this.onGamepadConnectedObservable.notifyObservers(newGamepad);
  67. this._startMonitoringGamepads();
  68. };
  69. this._onGamepadDisconnectedEvent = (evt) => {
  70. let gamepad = evt.gamepad;
  71. // Remove the gamepad from the list of gamepads to monitor.
  72. for (var i in this._babylonGamepads) {
  73. if (this._babylonGamepads[i].index === gamepad.index) {
  74. let disconnectedGamepad = this._babylonGamepads[i];
  75. disconnectedGamepad._isConnected = false;
  76. this.onGamepadDisconnectedObservable.notifyObservers(disconnectedGamepad);
  77. disconnectedGamepad.dispose && disconnectedGamepad.dispose();
  78. break;
  79. }
  80. }
  81. };
  82. if (this._gamepadSupport) {
  83. //first add already-connected gamepads
  84. this._updateGamepadObjects();
  85. if (this._babylonGamepads.length) {
  86. this._startMonitoringGamepads();
  87. }
  88. // Checking if the gamepad connected event is supported (like in Firefox)
  89. if (this._gamepadEventSupported) {
  90. let hostWindow = this._scene ? this._scene.getEngine().getHostWindow() : window;
  91. if (hostWindow) {
  92. hostWindow.addEventListener('gamepadconnected', this._onGamepadConnectedEvent, false);
  93. hostWindow.addEventListener('gamepaddisconnected', this._onGamepadDisconnectedEvent, false);
  94. }
  95. }
  96. else {
  97. this._startMonitoringGamepads();
  98. }
  99. }
  100. }
  101. /**
  102. * The gamepads in the game pad manager
  103. */
  104. public get gamepads(): Gamepad[] {
  105. return this._babylonGamepads;
  106. }
  107. /**
  108. * Get the gamepad controllers based on type
  109. * @param type The type of gamepad controller
  110. * @returns Nullable gamepad
  111. */
  112. public getGamepadByType(type: number = Gamepad.XBOX): Nullable<Gamepad> {
  113. for (var gamepad of this._babylonGamepads) {
  114. if (gamepad && gamepad.type === type) {
  115. return gamepad;
  116. }
  117. }
  118. return null;
  119. }
  120. /**
  121. * Disposes the gamepad manager
  122. */
  123. public dispose() {
  124. if (this._gamepadEventSupported) {
  125. if (this._onGamepadConnectedEvent) {
  126. window.removeEventListener('gamepadconnected', this._onGamepadConnectedEvent);
  127. }
  128. if (this._onGamepadDisconnectedEvent) {
  129. window.removeEventListener('gamepaddisconnected', this._onGamepadDisconnectedEvent);
  130. }
  131. this._onGamepadConnectedEvent = null;
  132. this._onGamepadDisconnectedEvent = null;
  133. }
  134. this._babylonGamepads.forEach((gamepad) => {
  135. gamepad.dispose();
  136. });
  137. this.onGamepadConnectedObservable.clear();
  138. this.onGamepadDisconnectedObservable.clear();
  139. this._oneGamepadConnected = false;
  140. this._stopMonitoringGamepads();
  141. this._babylonGamepads = [];
  142. }
  143. private _addNewGamepad(gamepad: any): Gamepad {
  144. if (!this._oneGamepadConnected) {
  145. this._oneGamepadConnected = true;
  146. }
  147. var newGamepad;
  148. var dualShock: boolean = ((<string>gamepad.id).search("054c") !== -1);
  149. var xboxOne: boolean = ((<string>gamepad.id).search("Xbox One") !== -1);
  150. if (xboxOne || (<string>gamepad.id).search("Xbox 360") !== -1 || (<string>gamepad.id).search("xinput") !== -1) {
  151. newGamepad = new Xbox360Pad(gamepad.id, gamepad.index, gamepad, xboxOne);
  152. }
  153. else if (dualShock) {
  154. newGamepad = new DualShockPad(gamepad.id, gamepad.index, gamepad);
  155. }
  156. // if pose is supported, use the (WebVR) pose enabled controller
  157. else if (gamepad.pose) {
  158. newGamepad = PoseEnabledControllerHelper.InitiateController(gamepad);
  159. }
  160. else {
  161. newGamepad = new GenericPad(gamepad.id, gamepad.index, gamepad);
  162. }
  163. this._babylonGamepads[newGamepad.index] = newGamepad;
  164. return newGamepad;
  165. }
  166. private _startMonitoringGamepads() {
  167. if (!this._isMonitoring) {
  168. this._isMonitoring = true;
  169. //back-comp
  170. if (!this._scene) {
  171. this._checkGamepadsStatus();
  172. }
  173. }
  174. }
  175. private _stopMonitoringGamepads() {
  176. this._isMonitoring = false;
  177. }
  178. /** @hidden */
  179. public _checkGamepadsStatus() {
  180. // Hack to be compatible Chrome
  181. this._updateGamepadObjects();
  182. for (var i in this._babylonGamepads) {
  183. let gamepad = this._babylonGamepads[i];
  184. if (!gamepad || !gamepad.isConnected) {
  185. continue;
  186. }
  187. gamepad.update();
  188. }
  189. if (this._isMonitoring && !this._scene) {
  190. Engine.QueueNewFrame(() => { this._checkGamepadsStatus(); });
  191. }
  192. }
  193. // This function is called only on Chrome, which does not properly support
  194. // connection/disconnection events and forces you to recopy again the gamepad object
  195. private _updateGamepadObjects() {
  196. var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
  197. for (var i = 0; i < gamepads.length; i++) {
  198. let gamepad = gamepads[i];
  199. if (gamepad) {
  200. if (!this._babylonGamepads[gamepad.index]) {
  201. var newGamepad = this._addNewGamepad(gamepad);
  202. this.onGamepadConnectedObservable.notifyObservers(newGamepad);
  203. }
  204. else {
  205. // Forced to copy again this object for Chrome for unknown reason
  206. this._babylonGamepads[i].browserGamepad = gamepad;
  207. if (!this._babylonGamepads[i].isConnected) {
  208. this._babylonGamepads[i]._isConnected = true;
  209. this.onGamepadConnectedObservable.notifyObservers(this._babylonGamepads[i]);
  210. }
  211. }
  212. }
  213. }
  214. }
  215. }