followCameraKeyboardMoveInput.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. import { ICameraInput, CameraInputTypes } from "../../Cameras/cameraInputsManager";
  2. import { FollowCamera } from "../../Cameras/followCamera";
  3. import { serialize } from "../../Misc/decorators";
  4. import { Nullable } from "../../types";
  5. import { Observer } from "../../Misc/observable";
  6. import { Engine } from "../../Engines/engine";
  7. import { KeyboardInfo, KeyboardEventTypes } from "../../Events/keyboardEvents";
  8. import { Scene } from "../../scene";
  9. /**
  10. * Manage the keyboard inputs to control the movement of a follow camera.
  11. * @see https://doc.babylonjs.com/how_to/customizing_camera_inputs
  12. */
  13. export class FollowCameraKeyboardMoveInput implements ICameraInput<FollowCamera> {
  14. /**
  15. * Defines the camera the input is attached to.
  16. */
  17. public camera: FollowCamera;
  18. /**
  19. * Defines the list of key codes associated with the up action (increase heightOffset)
  20. */
  21. @serialize()
  22. public keysHeightOffsetIncr = [38];
  23. /**
  24. * Defines the list of key codes associated with the down action (decrease heightOffset)
  25. */
  26. @serialize()
  27. public keysHeightOffsetDecr = [40];
  28. /**
  29. * Defines whether the Alt modifier key is required to move up/down (alter heightOffset)
  30. */
  31. @serialize()
  32. public keysHeightOffsetModifierAlt: boolean = false;
  33. /**
  34. * Defines whether the Ctrl modifier key is required to move up/down (alter heightOffset)
  35. */
  36. @serialize()
  37. public keysHeightOffsetModifierCtrl: boolean = false;
  38. /**
  39. * Defines whether the Shift modifier key is required to move up/down (alter heightOffset)
  40. */
  41. @serialize()
  42. public keysHeightOffsetModifierShift: boolean = false;
  43. /**
  44. * Defines the list of key codes associated with the left action (increase rotationOffset)
  45. */
  46. @serialize()
  47. public keysRotationOffsetIncr = [37];
  48. /**
  49. * Defines the list of key codes associated with the right action (decrease rotationOffset)
  50. */
  51. @serialize()
  52. public keysRotationOffsetDecr = [39];
  53. /**
  54. * Defines whether the Alt modifier key is required to move left/right (alter rotationOffset)
  55. */
  56. @serialize()
  57. public keysRotationOffsetModifierAlt: boolean = false;
  58. /**
  59. * Defines whether the Ctrl modifier key is required to move left/right (alter rotationOffset)
  60. */
  61. @serialize()
  62. public keysRotationOffsetModifierCtrl: boolean = false;
  63. /**
  64. * Defines whether the Shift modifier key is required to move left/right (alter rotationOffset)
  65. */
  66. @serialize()
  67. public keysRotationOffsetModifierShift: boolean = false;
  68. /**
  69. * Defines the list of key codes associated with the zoom-in action (decrease radius)
  70. */
  71. @serialize()
  72. public keysRadiusIncr = [40];
  73. /**
  74. * Defines the list of key codes associated with the zoom-out action (increase radius)
  75. */
  76. @serialize()
  77. public keysRadiusDecr = [38];
  78. /**
  79. * Defines whether the Alt modifier key is required to zoom in/out (alter radius value)
  80. */
  81. @serialize()
  82. public keysRadiusModifierAlt: boolean = true;
  83. /**
  84. * Defines whether the Ctrl modifier key is required to zoom in/out (alter radius value)
  85. */
  86. @serialize()
  87. public keysRadiusModifierCtrl: boolean = false;
  88. /**
  89. * Defines whether the Shift modifier key is required to zoom in/out (alter radius value)
  90. */
  91. @serialize()
  92. public keysRadiusModifierShift: boolean = false;
  93. /**
  94. * Defines the rate of change of heightOffset.
  95. */
  96. @serialize()
  97. public heightSensibility: number = 1;
  98. /**
  99. * Defines the rate of change of rotationOffset.
  100. */
  101. @serialize()
  102. public rotationSensibility: number = 1;
  103. /**
  104. * Defines the rate of change of radius.
  105. */
  106. @serialize()
  107. public radiusSensibility: number = 1;
  108. private _keys = new Array<number>();
  109. private _ctrlPressed: boolean;
  110. private _altPressed: boolean;
  111. private _shiftPressed: boolean;
  112. private _onCanvasBlurObserver: Nullable<Observer<Engine>>;
  113. private _onKeyboardObserver: Nullable<Observer<KeyboardInfo>>;
  114. private _engine: Engine;
  115. private _scene: Scene;
  116. /**
  117. * Attach the input controls to a specific dom element to get the input from.
  118. * @param element Defines the element the controls should be listened from
  119. * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
  120. */
  121. public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
  122. if (this._onCanvasBlurObserver) {
  123. return;
  124. }
  125. this._scene = this.camera.getScene();
  126. this._engine = this._scene.getEngine();
  127. this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(() => {
  128. this._keys = [];
  129. });
  130. this._onKeyboardObserver = this._scene.onKeyboardObservable.add((info) => {
  131. let evt = info.event;
  132. if (!evt.metaKey) {
  133. if (info.type === KeyboardEventTypes.KEYDOWN) {
  134. this._ctrlPressed = evt.ctrlKey;
  135. this._altPressed = evt.altKey;
  136. this._shiftPressed = evt.shiftKey;
  137. if (this.keysHeightOffsetIncr.indexOf(evt.keyCode) !== -1 ||
  138. this.keysHeightOffsetDecr.indexOf(evt.keyCode) !== -1 ||
  139. this.keysRotationOffsetIncr.indexOf(evt.keyCode) !== -1 ||
  140. this.keysRotationOffsetDecr.indexOf(evt.keyCode) !== -1 ||
  141. this.keysRadiusIncr.indexOf(evt.keyCode) !== -1 ||
  142. this.keysRadiusDecr.indexOf(evt.keyCode) !== -1) {
  143. var index = this._keys.indexOf(evt.keyCode);
  144. if (index === -1) {
  145. this._keys.push(evt.keyCode);
  146. }
  147. if (evt.preventDefault) {
  148. if (!noPreventDefault) {
  149. evt.preventDefault();
  150. }
  151. }
  152. }
  153. } else {
  154. if (this.keysHeightOffsetIncr.indexOf(evt.keyCode) !== -1 ||
  155. this.keysHeightOffsetDecr.indexOf(evt.keyCode) !== -1 ||
  156. this.keysRotationOffsetIncr.indexOf(evt.keyCode) !== -1 ||
  157. this.keysRotationOffsetDecr.indexOf(evt.keyCode) !== -1 ||
  158. this.keysRadiusIncr.indexOf(evt.keyCode) !== -1 ||
  159. this.keysRadiusDecr.indexOf(evt.keyCode) !== -1) {
  160. var index = this._keys.indexOf(evt.keyCode);
  161. if (index >= 0) {
  162. this._keys.splice(index, 1);
  163. }
  164. if (evt.preventDefault) {
  165. if (!noPreventDefault) {
  166. evt.preventDefault();
  167. }
  168. }
  169. }
  170. }
  171. }
  172. });
  173. }
  174. /**
  175. * Detach the current controls from the specified dom element.
  176. * @param element Defines the element to stop listening the inputs from
  177. */
  178. public detachControl(element: Nullable<HTMLElement>) {
  179. if (this._scene) {
  180. if (this._onKeyboardObserver) {
  181. this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
  182. }
  183. if (this._onCanvasBlurObserver) {
  184. this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
  185. }
  186. this._onKeyboardObserver = null;
  187. this._onCanvasBlurObserver = null;
  188. }
  189. this._keys = [];
  190. }
  191. /**
  192. * Update the current camera state depending on the inputs that have been used this frame.
  193. * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
  194. */
  195. public checkInputs(): void {
  196. if (this._onKeyboardObserver) {
  197. this._keys.forEach((keyCode) => {
  198. if (this.keysHeightOffsetIncr.indexOf(keyCode) !== -1 &&
  199. this._modifierHeightOffset()) {
  200. this.camera.heightOffset += this.heightSensibility;
  201. } else if (this.keysHeightOffsetDecr.indexOf(keyCode) !== -1 &&
  202. this._modifierHeightOffset()) {
  203. this.camera.heightOffset -= this.heightSensibility;
  204. } else if (this.keysRotationOffsetIncr.indexOf(keyCode) !== -1 &&
  205. this._modifierRotationOffset()) {
  206. this.camera.rotationOffset += this.rotationSensibility;
  207. this.camera.rotationOffset %= 360;
  208. } else if (this.keysRotationOffsetDecr.indexOf(keyCode) !== -1 &&
  209. this._modifierRotationOffset()) {
  210. this.camera.rotationOffset -= this.rotationSensibility;
  211. this.camera.rotationOffset %= 360;
  212. } else if (this.keysRadiusIncr.indexOf(keyCode) !== -1 &&
  213. this._modifierRadius()) {
  214. this.camera.radius += this.radiusSensibility;
  215. } else if (this.keysRadiusDecr.indexOf(keyCode) !== -1 &&
  216. this._modifierRadius()) {
  217. this.camera.radius -= this.radiusSensibility;
  218. }
  219. });
  220. }
  221. }
  222. /**
  223. * Gets the class name of the current input.
  224. * @returns the class name
  225. */
  226. public getClassName(): string {
  227. return "FollowCameraKeyboardMoveInput";
  228. }
  229. /**
  230. * Get the friendly name associated with the input class.
  231. * @returns the input friendly name
  232. */
  233. public getSimpleName(): string {
  234. return "keyboard";
  235. }
  236. /**
  237. * Check if the pressed modifier keys (Alt/Ctrl/Shift) match those configured to
  238. * allow modification of the heightOffset value.
  239. */
  240. private _modifierHeightOffset(): boolean {
  241. return (this.keysHeightOffsetModifierAlt === this._altPressed &&
  242. this.keysHeightOffsetModifierCtrl === this._ctrlPressed &&
  243. this.keysHeightOffsetModifierShift === this._shiftPressed);
  244. }
  245. /**
  246. * Check if the pressed modifier keys (Alt/Ctrl/Shift) match those configured to
  247. * allow modification of the rotationOffset value.
  248. */
  249. private _modifierRotationOffset(): boolean {
  250. return (this.keysRotationOffsetModifierAlt === this._altPressed &&
  251. this.keysRotationOffsetModifierCtrl === this._ctrlPressed &&
  252. this.keysRotationOffsetModifierShift === this._shiftPressed);
  253. }
  254. /**
  255. * Check if the pressed modifier keys (Alt/Ctrl/Shift) match those configured to
  256. * allow modification of the radius value.
  257. */
  258. private _modifierRadius(): boolean {
  259. return (this.keysRadiusModifierAlt === this._altPressed &&
  260. this.keysRadiusModifierCtrl === this._ctrlPressed &&
  261. this.keysRadiusModifierShift === this._shiftPressed);
  262. }
  263. }
  264. (<any>CameraInputTypes)["FollowCameraKeyboardMoveInput"] = FollowCameraKeyboardMoveInput;