gamepadManager.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import { Observable } from "../Misc/observable.js";
  2. import { IsWindowObjectExist } from "../Misc/domManagement.js";
  3. import { Xbox360Pad } from "./xboxGamepad.js";
  4. import { Gamepad, GenericPad } from "./gamepad.js";
  5. import { Engine } from "../Engines/engine.js";
  6. import { DualShockPad } from "./dualShockGamepad.js";
  7. import { Tools } from "../Misc/tools.js";
  8. /**
  9. * Manager for handling gamepads
  10. */
  11. export class GamepadManager {
  12. /**
  13. * Initializes the gamepad manager
  14. * @param _scene BabylonJS scene
  15. */
  16. constructor(_scene) {
  17. this._scene = _scene;
  18. this._babylonGamepads = [];
  19. this._oneGamepadConnected = false;
  20. /** @internal */
  21. this._isMonitoring = false;
  22. /**
  23. * observable to be triggered when the gamepad controller has been disconnected
  24. */
  25. this.onGamepadDisconnectedObservable = new Observable();
  26. if (!IsWindowObjectExist()) {
  27. this._gamepadEventSupported = false;
  28. }
  29. else {
  30. this._gamepadEventSupported = "GamepadEvent" in window;
  31. this._gamepadSupport = navigator && navigator.getGamepads;
  32. }
  33. this.onGamepadConnectedObservable = new Observable((observer) => {
  34. // This will be used to raise the onGamepadConnected for all gamepads ALREADY connected
  35. for (const i in this._babylonGamepads) {
  36. const gamepad = this._babylonGamepads[i];
  37. if (gamepad && gamepad._isConnected) {
  38. this.onGamepadConnectedObservable.notifyObserver(observer, gamepad);
  39. }
  40. }
  41. });
  42. this._onGamepadConnectedEvent = (evt) => {
  43. const gamepad = evt.gamepad;
  44. if (gamepad.index in this._babylonGamepads) {
  45. if (this._babylonGamepads[gamepad.index].isConnected) {
  46. return;
  47. }
  48. }
  49. let newGamepad;
  50. if (this._babylonGamepads[gamepad.index]) {
  51. newGamepad = this._babylonGamepads[gamepad.index];
  52. newGamepad.browserGamepad = gamepad;
  53. newGamepad._isConnected = true;
  54. }
  55. else {
  56. newGamepad = this._addNewGamepad(gamepad);
  57. }
  58. this.onGamepadConnectedObservable.notifyObservers(newGamepad);
  59. this._startMonitoringGamepads();
  60. };
  61. this._onGamepadDisconnectedEvent = (evt) => {
  62. const gamepad = evt.gamepad;
  63. // Remove the gamepad from the list of gamepads to monitor.
  64. for (const i in this._babylonGamepads) {
  65. if (this._babylonGamepads[i].index === gamepad.index) {
  66. const disconnectedGamepad = this._babylonGamepads[i];
  67. disconnectedGamepad._isConnected = false;
  68. this.onGamepadDisconnectedObservable.notifyObservers(disconnectedGamepad);
  69. disconnectedGamepad.dispose && disconnectedGamepad.dispose();
  70. break;
  71. }
  72. }
  73. };
  74. if (this._gamepadSupport) {
  75. //first add already-connected gamepads
  76. this._updateGamepadObjects();
  77. if (this._babylonGamepads.length) {
  78. this._startMonitoringGamepads();
  79. }
  80. // Checking if the gamepad connected event is supported (like in Firefox)
  81. if (this._gamepadEventSupported) {
  82. const hostWindow = this._scene ? this._scene.getEngine().getHostWindow() : window;
  83. if (hostWindow) {
  84. hostWindow.addEventListener("gamepadconnected", this._onGamepadConnectedEvent, false);
  85. hostWindow.addEventListener("gamepaddisconnected", this._onGamepadDisconnectedEvent, false);
  86. }
  87. }
  88. else {
  89. this._startMonitoringGamepads();
  90. }
  91. }
  92. }
  93. /**
  94. * The gamepads in the game pad manager
  95. */
  96. get gamepads() {
  97. return this._babylonGamepads;
  98. }
  99. /**
  100. * Get the gamepad controllers based on type
  101. * @param type The type of gamepad controller
  102. * @returns Nullable gamepad
  103. */
  104. getGamepadByType(type = Gamepad.XBOX) {
  105. for (const gamepad of this._babylonGamepads) {
  106. if (gamepad && gamepad.type === type) {
  107. return gamepad;
  108. }
  109. }
  110. return null;
  111. }
  112. /**
  113. * Disposes the gamepad manager
  114. */
  115. dispose() {
  116. if (this._gamepadEventSupported) {
  117. if (this._onGamepadConnectedEvent) {
  118. window.removeEventListener("gamepadconnected", this._onGamepadConnectedEvent);
  119. }
  120. if (this._onGamepadDisconnectedEvent) {
  121. window.removeEventListener("gamepaddisconnected", this._onGamepadDisconnectedEvent);
  122. }
  123. this._onGamepadConnectedEvent = null;
  124. this._onGamepadDisconnectedEvent = null;
  125. }
  126. this._babylonGamepads.forEach((gamepad) => {
  127. gamepad.dispose();
  128. });
  129. this.onGamepadConnectedObservable.clear();
  130. this.onGamepadDisconnectedObservable.clear();
  131. this._oneGamepadConnected = false;
  132. this._stopMonitoringGamepads();
  133. this._babylonGamepads = [];
  134. }
  135. _addNewGamepad(gamepad) {
  136. if (!this._oneGamepadConnected) {
  137. this._oneGamepadConnected = true;
  138. }
  139. let newGamepad;
  140. const dualShock = gamepad.id.search("054c") !== -1 && gamepad.id.search("0ce6") === -1;
  141. const xboxOne = gamepad.id.search("Xbox One") !== -1;
  142. if (xboxOne ||
  143. gamepad.id.search("Xbox 360") !== -1 ||
  144. gamepad.id.search("xinput") !== -1 ||
  145. (gamepad.id.search("045e") !== -1 && gamepad.id.search("Surface Dock") === -1)) {
  146. // make sure the Surface Dock Extender is not detected as an xbox controller
  147. newGamepad = new Xbox360Pad(gamepad.id, gamepad.index, gamepad, xboxOne);
  148. }
  149. else if (dualShock) {
  150. newGamepad = new DualShockPad(gamepad.id, gamepad.index, gamepad);
  151. }
  152. else {
  153. newGamepad = new GenericPad(gamepad.id, gamepad.index, gamepad);
  154. }
  155. this._babylonGamepads[newGamepad.index] = newGamepad;
  156. return newGamepad;
  157. }
  158. _startMonitoringGamepads() {
  159. if (!this._isMonitoring) {
  160. this._isMonitoring = true;
  161. //back-comp
  162. this._checkGamepadsStatus();
  163. }
  164. }
  165. _stopMonitoringGamepads() {
  166. this._isMonitoring = false;
  167. }
  168. /** @internal */
  169. _checkGamepadsStatus() {
  170. // Hack to be compatible Chrome
  171. this._updateGamepadObjects();
  172. for (const i in this._babylonGamepads) {
  173. const gamepad = this._babylonGamepads[i];
  174. if (!gamepad || !gamepad.isConnected) {
  175. continue;
  176. }
  177. try {
  178. gamepad.update();
  179. }
  180. catch {
  181. if (this._loggedErrors.indexOf(gamepad.index) === -1) {
  182. Tools.Warn(`Error updating gamepad ${gamepad.id}`);
  183. this._loggedErrors.push(gamepad.index);
  184. }
  185. }
  186. }
  187. if (this._isMonitoring) {
  188. Engine.QueueNewFrame(() => {
  189. this._checkGamepadsStatus();
  190. });
  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. _updateGamepadObjects() {
  196. const gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
  197. for (let i = 0; i < gamepads.length; i++) {
  198. const gamepad = gamepads[i];
  199. if (gamepad) {
  200. if (!this._babylonGamepads[gamepad.index]) {
  201. const 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. }
  216. //# sourceMappingURL=gamepadManager.js.map