followCameraKeyboardMoveInput.ts 11 KB

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