Source: addons/fps.js

  1. import register from "../util/register.js";
  2. import m_cam_fact from "../extern/camera.js";
  3. import m_ctl_fact from "../extern/controls.js";
  4. import m_phy_fact from "../extern/physics.js";
  5. import m_scs_fact from "../extern/scenes.js";
  6. import m_util_fact from "../extern/util.js";
  7. import m_main_fact from "../extern/main.js";
  8. import m_cont_fact from "../extern/container.js";
  9. import m_input_fact from "../extern/input.js";
  10. import m_screen_fact from "../extern/screen.js";
  11. import m_trans_fact from "../extern/transform.js";
  12. import m_const_fact from "../extern/constraints.js";
  13. import * as m_vec3 from "../libs/gl_matrix/vec3.js";
  14. import m_hmd_fact from "./hmd.js";
  15. import m_print_fact from "../intern/print.js";
  16. /**
  17. * Add-on for first person applications.
  18. * It makes creating FPS applications, binding controls and
  19. * controlling character movement easier.
  20. * @module fps
  21. * @local ChangeStateCallback
  22. * @local PlockCallback
  23. * @local CharMotionCallback
  24. */
  25. function FPS(ns, exports) {
  26. /**
  27. * Function which is called when the given ID state is changed.
  28. * @callback ChangeStateCallback
  29. * @param {number} old_state_id Previous state ID.
  30. * @param {number} new_state_id New state ID.
  31. */
  32. /**
  33. * Function which is called when pointerlock state was changed.
  34. * @callback PlockCallback
  35. * @param {HTMLElement} elem HTML element, which required pointerlock
  36. */
  37. /**
  38. * Function which is called when character changes his movement direction.
  39. * @callback CharMotionCallback
  40. * @param {number} forw_back Forward/backward direction (can be -1, 1 or 0).
  41. * @param {number} right_left Right/left direction (can be -1, 1 or 0).
  42. */
  43. /**
  44. * Callback for characters/camera rotation
  45. * @callback CharRotationCallback
  46. * @param {Object3D} character Character
  47. * @param {number} x rotation around X-axis in radians
  48. * @param {number} y rotation around Y-axis in radians
  49. */
  50. var m_cam = m_cam_fact(ns);
  51. var m_ctl = m_ctl_fact(ns);
  52. var m_phy = m_phy_fact(ns);
  53. var m_scs = m_scs_fact(ns);
  54. var m_util = m_util_fact(ns);
  55. var m_main = m_main_fact(ns);
  56. var m_cont = m_cont_fact(ns);
  57. var m_input = m_input_fact(ns);
  58. var m_screen = m_screen_fact(ns);
  59. var m_trans = m_trans_fact(ns);
  60. var m_const = m_const_fact(ns);
  61. var m_hmd = m_hmd_fact(ns);
  62. var m_print = m_print_fact(ns);
  63. var AT_PRESSED = 1;
  64. var AT_RELEASED = 2;
  65. var AT_CONTINUOUS = 3;
  66. var CS_STAY = 0;
  67. var CS_WALK = 1;
  68. var CS_RUN = 2;
  69. var CS_FLY = 3;
  70. var CS_CLIMB = 4;
  71. var MOBILE_FORWARD_BTN_ID = "B4W_DEFAULT_BTN_1";
  72. var MOBILE_BACKWARD_BTN_ID = "B4W_DEFAULT_BTN_2";
  73. var FORWARD_SVG = 'url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyBpZD0ic3ZnMzQwMCIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTQuMTExbW0iIHdpZHRoPSIxNC4xMTFtbSIgdmVyc2lvbj0iMS4xIiB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHZpZXdCb3g9IjAgMCA0OS45OTk5OTggNDkuOTk5OTk5Ij4gPG1ldGFkYXRhIGlkPSJtZXRhZGF0YTM0MDUiPjxyZGY6UkRGPjxjYzpXb3JrIHJkZjphYm91dD0iIj48ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD48ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+PGRjOnRpdGxlLz48L2NjOldvcms+PC9yZGY6UkRGPiA8L21ldGFkYXRhPiA8ZyBpZD0ibGF5ZXIxIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0xMDAyLjQpIj48cmVjdCBpZD0icmVjdDI0MTI2IiBvcGFjaXR5PSIwLjU0MyIgZmlsbC1vcGFjaXR5PSIuODM2MTEiIGhlaWdodD0iNTAiIHdpZHRoPSI1MCIgeT0iMTAwMi40IiB4PSIwIiBmaWxsPSIjZmZmIi8+PHBhdGggaWQ9InBhdGgyNDEyOCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZD0ibTM5LjE0MiAxMDMzLjUtMTQuMTQyLTE0LjE0Mi0xNC4xNDIgMTQuMTQyIiBzdHJva2U9IiM2ZTZlNmUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIiBmaWxsPSJub25lIi8+IDwvZz48L3N2Zz4=);';
  74. var DRAG_TOUCH_DELTA_MULT = 4;
  75. var DRAG_MOUSE_DELTA_MULT = 2;
  76. var CAM_SMOOTH_CHARACTER_COEFF = 0.2
  77. var AXIS_THRESHOLD = 0.05;
  78. var ROT_STEP = 2.5;
  79. var MIN_VERT_ANG = (-Math.PI + 0.1) / 2;
  80. var MAX_VERT_ANG = (Math.PI - 0.1) / 2;
  81. var MULT_SCALE = 200000;
  82. var GMPD_BTNS_OFFSET = 300;
  83. var GMPD_AXIS_OFFSET = 326;
  84. var _move_delta = new Float32Array(2);
  85. var _smooth_factor = 1;
  86. var _fps_camera_mult = 0.0004;
  87. var _manifold_counter = 0;
  88. var _character_sm = null;
  89. var _plock_sensor = null;
  90. var _plock_cb = {
  91. enable_cb : null,
  92. disable_cb : null
  93. };
  94. var _rotation_cb = function() {};
  95. var _curr_gamepad_id = 0;
  96. var _state_counter = 4;
  97. var _is_freezed = false;
  98. var _vec2_tmp = new Float32Array(2);
  99. var _vec3_tmp = new Float32Array(3);
  100. var _vec3_tmp2 = new Float32Array(3);
  101. function default_rotation_cb(character, rot_x, rot_y) {
  102. var camera = m_scs.get_active_camera();
  103. m_cam.rotate_camera(camera, rot_x, rot_y);
  104. var angles = m_cam.get_camera_angles_char(camera, _vec2_tmp);
  105. m_phy.set_character_rotation_h(character, angles[0]);
  106. m_phy.set_character_vert_move_dir_angle(character, angles[1]);
  107. }
  108. function smooth_cb(obj, id, pulse, rot_callback) {
  109. if (Math.abs(_move_delta[0]) > 0.01 || Math.abs(_move_delta[1]) > 0.01) {
  110. var elapsed = m_ctl.get_sensor_value(obj, id, 0);
  111. var rot_x = m_util.smooth(_move_delta[0], 0, elapsed, smooth_coeff());
  112. var rot_y = m_util.smooth(_move_delta[1], 0, elapsed, smooth_coeff());
  113. _move_delta[0] -= rot_x;
  114. _move_delta[1] -= rot_y;
  115. rot_callback(obj, -rot_x * _fps_camera_mult, -rot_y * _fps_camera_mult);
  116. }
  117. }
  118. function set_smooth_factor(value) {
  119. _smooth_factor = value;
  120. }
  121. function smooth_coeff() {
  122. return CAM_SMOOTH_CHARACTER_COEFF * _smooth_factor;
  123. }
  124. function create_mobile_controls(character, parent_elem) {
  125. var forward_btn = document.createElement("div");
  126. forward_btn.style.cssText =
  127. "position: absolute;" +
  128. "left: 10px;" +
  129. "bottom: 70px;" +
  130. "width: 53px;" +
  131. "height: 53px;" +
  132. "background-image: " + FORWARD_SVG;
  133. forward_btn.setAttribute("id", MOBILE_FORWARD_BTN_ID);
  134. var backwards_btn = document.createElement("div");
  135. backwards_btn.style.cssText =
  136. "position: absolute;" +
  137. "left: 10px;" +
  138. "bottom: 10px;" +
  139. "width: 53px;" +
  140. "height: 53px;" +
  141. "background-image: " + FORWARD_SVG +
  142. "transform: rotate(180deg);" +
  143. "transform-origin: center;";
  144. backwards_btn.setAttribute("id", MOBILE_BACKWARD_BTN_ID);
  145. parent_elem.appendChild(forward_btn);
  146. parent_elem.appendChild(backwards_btn);
  147. }
  148. function check_pointerlock(elem) {
  149. var request_plock = elem.requestPointerLock ||
  150. elem.webkitRequestPointerLock || elem.mozRequestPointerLock;
  151. return typeof request_plock === "function";
  152. }
  153. function rotate_cam_by_axis(obj, camobj, id, elapsed) {
  154. var h_axis = m_ctl.get_sensor_value(obj, id, 1);
  155. var v_axis = m_ctl.get_sensor_value(obj, id, 2);
  156. var rot_step_value = elapsed * ROT_STEP;
  157. var vert_axis_val = Math.abs(v_axis) < AXIS_THRESHOLD ? 0 : v_axis;
  158. var vert_ang = - vert_axis_val * rot_step_value;
  159. var hor_axis_val = Math.abs(h_axis) < AXIS_THRESHOLD ? 0 : h_axis;
  160. var hor_ang = - hor_axis_val * rot_step_value;
  161. vert_ang = m_util.clamp(vert_ang, MIN_VERT_ANG, MAX_VERT_ANG);
  162. m_cam.rotate_camera(camobj, hor_ang, vert_ang);
  163. var cam_angls = m_cam.get_camera_angles(camobj, _vec2_tmp);
  164. m_phy.set_character_rotation_h(obj, cam_angls[0] + Math.PI);
  165. }
  166. function disable_rotation(elem, character) {
  167. if (m_ctl.check_sensor_manifolds(character, "FPS_PLOCK"))
  168. m_ctl.remove_sensor_manifold(character, "FPS_PLOCK");
  169. if (m_ctl.check_sensor_manifolds(null, "FPS_ACTIVATE_PLOCK"))
  170. m_ctl.remove_sensor_manifold(null, "FPS_ACTIVATE_PLOCK");
  171. if (m_ctl.check_sensor_manifolds(character, "FPS_CAM_ROT"))
  172. m_ctl.remove_sensor_manifold(character, "FPS_CAM_ROT");
  173. if (m_ctl.check_sensor_manifolds(null, "FPS_DRAG_PRESS"))
  174. m_ctl.remove_sensor_manifold(null, "FPS_DRAG_PRESS");
  175. if (m_ctl.check_sensor_manifolds(null, "FPS_DRAG_MOVE"))
  176. m_ctl.remove_sensor_manifold(null, "FPS_DRAG_MOVE");
  177. if (m_ctl.check_sensor_manifolds(character, "FPS_SMOOTH_DRAG"))
  178. m_ctl.remove_sensor_manifold(character, "FPS_SMOOTH_DRAG");
  179. }
  180. function enable_rotation(elem, character) {
  181. if (check_pointerlock(elem) && !m_main.detect_mobile()) {
  182. var plock_mouse_sen = m_ctl.create_plock_mouse_sensor(elem);
  183. var plock_sen = _plock_sensor ? _plock_sensor : m_ctl.create_plock_sensor(elem);
  184. _plock_sensor = plock_sen;
  185. var fps_plock_logic_func = function(s) {
  186. return s[0] && s[1];
  187. }
  188. var fps_plock_sensors_cb = function(obj, id, pulse) {
  189. if (pulse > 0) {
  190. var payload = m_ctl.get_sensor_payload(obj, id, 0);
  191. _rotation_cb(obj, -payload.coords[0] * _fps_camera_mult,
  192. -payload.coords[1] * _fps_camera_mult);
  193. }
  194. }
  195. m_ctl.create_sensor_manifold(character, "FPS_PLOCK",
  196. m_ctl.CT_CONTINUOUS, [plock_mouse_sen, plock_sen],
  197. fps_plock_logic_func, fps_plock_sensors_cb);
  198. var fps_act_logic_func = function(s) {
  199. return s[0];
  200. }
  201. var fps_act_sensor_cb = function(obj, id, pulse) {
  202. if (pulse > 0) {
  203. if (_plock_cb.enable_cb)
  204. _plock_cb.enable_cb(elem);
  205. } else
  206. if (_plock_cb.disable_cb)
  207. _plock_cb.disable_cb(elem);
  208. }
  209. m_ctl.create_sensor_manifold(null, "FPS_ACTIVATE_PLOCK",
  210. m_ctl.CT_TRIGGER, [plock_sen],
  211. fps_act_logic_func, fps_act_sensor_cb);
  212. } else {
  213. if (m_main.detect_mobile()) {
  214. var move_sen = m_ctl.create_touch_move_sensor("XY", elem);
  215. var click_sen = m_ctl.create_touch_click_sensor(elem);
  216. var drag_mult = DRAG_TOUCH_DELTA_MULT;
  217. } else {
  218. var move_sen = m_ctl.create_mouse_move_sensor("XY", elem);
  219. var click_sen = m_ctl.create_mouse_click_sensor(elem);
  220. var drag_mult = DRAG_MOUSE_DELTA_MULT;
  221. var camera = m_scs.get_active_camera();
  222. var cam_rot_sensor_cb = function(obj, id, pulse) {
  223. if (pulse > 0) {
  224. var elapsed = m_ctl.get_sensor_value(obj, id, 0);
  225. rotate_cam_by_axis(obj, camera, id, elapsed);
  226. }
  227. }
  228. var logic_func = function(s) {
  229. return Math.abs(s[1]) > AXIS_THRESHOLD || Math.abs(s[2]) > AXIS_THRESHOLD;
  230. };
  231. var e_s = m_ctl.create_elapsed_sensor();
  232. var h_axis = m_ctl.create_gamepad_axis_sensor(m_input.GMPD_AXIS_2);
  233. var v_axis = m_ctl.create_gamepad_axis_sensor(m_input.GMPD_AXIS_3);
  234. m_ctl.create_sensor_manifold(character, "FPS_CAM_ROT", m_ctl.CT_CONTINUOUS,
  235. [e_s, h_axis, v_axis], logic_func, cam_rot_sensor_cb);
  236. }
  237. var move_x = 0;
  238. var move_y = 0;
  239. var press_logic_func = function(s) {
  240. return s[0];
  241. }
  242. var press_sensor_cb = function(obj, id, pulse) {
  243. if (pulse == 1) {
  244. var payload = m_ctl.get_sensor_payload(obj, id, 0);
  245. var coords = payload.coords;
  246. move_x = coords[0];
  247. move_y = coords[1];
  248. }
  249. }
  250. m_ctl.create_sensor_manifold(null, "FPS_DRAG_PRESS",
  251. m_ctl.CT_SHOT, [click_sen],
  252. press_logic_func, press_sensor_cb);
  253. var move_logic_func = function(s) {
  254. return s[0] && s[1];
  255. }
  256. var move_sensor_cb = function(obj, id, pulse) {
  257. if (pulse == 1) {
  258. var payload = m_ctl.get_sensor_payload(obj, id, 0);
  259. var coords = payload.coords;
  260. _move_delta[0] += (coords[0] - move_x) * drag_mult;
  261. _move_delta[1] += (coords[1] - move_y) * drag_mult;
  262. move_x = coords[0];
  263. move_y = coords[1];
  264. }
  265. }
  266. m_ctl.create_sensor_manifold(null, "FPS_DRAG_MOVE",
  267. m_ctl.CT_CONTINUOUS, [move_sen, click_sen],
  268. move_logic_func, move_sensor_cb);
  269. var elapsed = m_ctl.create_elapsed_sensor();
  270. m_ctl.create_sensor_manifold(character, "FPS_SMOOTH_DRAG", m_ctl.CT_CONTINUOUS,
  271. [elapsed], null, smooth_cb, _rotation_cb);
  272. }
  273. }
  274. function set_characters_camera(character, lock_camera) {
  275. var cam_obj = m_scs.get_active_camera();
  276. if (lock_camera) {
  277. var cam_trans = m_trans.get_translation(cam_obj, _vec3_tmp);
  278. var char_trans = m_trans.get_translation(character, _vec3_tmp2);
  279. m_vec3.subtract(cam_trans, char_trans, cam_trans);
  280. m_const.append_stiff_trans(cam_obj, character, cam_trans);
  281. }
  282. var angles = m_cam.get_camera_angles_char(cam_obj, _vec2_tmp);
  283. m_phy.set_character_rotation_h(character, angles[0]);
  284. m_phy.set_character_vert_move_dir_angle(character, angles[1]);
  285. }
  286. var _enable_vr_cb = null;
  287. var _disable_vr_cb = null;
  288. // TODO: remove next function
  289. exports.append_switch_vr_cbs = function(enable_cb, disable_cb) {
  290. _enable_vr_cb = enable_cb;
  291. _disable_vr_cb = _disable_vr_cb;
  292. }
  293. function register_hmd(elem, character) {
  294. m_input.add_click_listener(elem, function() {
  295. m_screen.request_fullscreen_hmd(elem,
  296. function() {
  297. disable_rotation(elem, character);
  298. // camera rotation is enabled with HMD
  299. m_hmd.enable_hmd(m_hmd.HMD_ALL_AXES_MOUSE_NONE);
  300. create_character_vr_rotate_sensor(character);
  301. if (_enable_vr_cb)
  302. _enable_vr_cb();
  303. },
  304. function() {
  305. remove_character_vr_rotate_sensor(character);
  306. m_hmd.disable_hmd();
  307. enable_rotation(elem, character);
  308. if (_disable_vr_cb)
  309. _disable_vr_cb();
  310. }
  311. );
  312. });
  313. }
  314. function create_character_vr_rotate_sensor(character) {
  315. var e_s = m_ctl.create_elapsed_sensor();
  316. var camobj = m_scs.get_active_camera();
  317. var sensor_cb = function(obj, id, pulse) {
  318. var hor_angle = m_cam.get_camera_angles_char(camobj, _vec2_tmp)[0];
  319. m_phy.set_character_rotation_h(obj, hor_angle /*+ Math.PI*/);
  320. }
  321. m_ctl.create_sensor_manifold(character, "FPS_CHARECTER_VR_ROT", m_ctl.CT_CONTINUOUS,
  322. [e_s], null, sensor_cb);
  323. }
  324. function remove_character_vr_rotate_sensor(character) {
  325. m_ctl.remove_sensor_manifold(character, "FPS_CHARECTER_VR_ROT");
  326. }
  327. function check_vr_support() {
  328. var support = m_hmd.check_browser_support() && !m_main.detect_mobile();
  329. var device = m_input.get_device_by_type_element(m_input.DEVICE_HMD);
  330. if (device)
  331. support = support && m_input.get_value_param(device, m_input.HMD_WEBVR_TYPE) &
  332. m_input.HMD_WEBVR1;
  333. return support;
  334. }
  335. function enable_camera_rotation(elem, character) {
  336. if (check_vr_support()) {
  337. register_hmd(elem, character);
  338. }
  339. enable_rotation(elem, character);
  340. }
  341. function enable_movements(elem, character, motion_cb, settings) {
  342. if (m_main.detect_mobile()) {
  343. var parent_elem = elem.parentElement ? elem.parentElement : elem;
  344. create_mobile_controls(character, parent_elem);
  345. }
  346. var sm = init_character_states(character);
  347. var move_state = {
  348. left: 0,
  349. right: 0,
  350. forw: 0,
  351. back: 0
  352. }
  353. var movestyle = -1;
  354. var move_dir_cd = settings.move_dir_cd;
  355. var check_character_look = function() {
  356. if (_is_freezed) {
  357. move_state.left = 0;
  358. move_state.right = 0;
  359. move_state.forw = 0;
  360. move_state.back = 0;
  361. return false;
  362. }
  363. return true;
  364. }
  365. var set_char_state = function(for_back, right_left) {
  366. var curr_state = get_state_machine_state(sm).id;
  367. if (!for_back && !right_left && curr_state != CS_CLIMB)
  368. state_machine_switch_state(sm, CS_STAY);
  369. else if (curr_state == CS_STAY) {
  370. if (movestyle > 0)
  371. state_machine_switch_state(sm, CS_RUN);
  372. else
  373. state_machine_switch_state(sm, CS_WALK);
  374. }
  375. }
  376. var move_forward_cb = function(value) {
  377. if (!check_character_look())
  378. return;
  379. var for_back_pr = move_state.forw + move_state.back;
  380. move_state.forw = value;
  381. var for_back = move_state.forw + move_state.back;
  382. var right_left = move_state.left + move_state.right;
  383. m_phy.set_character_move_dir(character, for_back, right_left);
  384. if (motion_cb)
  385. motion_cb(for_back, right_left);
  386. set_char_state(for_back, right_left);
  387. if (move_dir_cd && for_back_pr != for_back)
  388. move_dir_cd(for_back, right_left, 0);
  389. }
  390. var move_backward_cb = function(value) {
  391. if (!check_character_look())
  392. return;
  393. value = value > 0.0 ? 1.0 : value;
  394. value = value < 0.0 ? -1.0 : value;
  395. var for_back_pr = move_state.forw + move_state.back;
  396. move_state.back = -value;
  397. var for_back = move_state.forw + move_state.back;
  398. var right_left = move_state.left + move_state.right;
  399. m_phy.set_character_move_dir(character, for_back, right_left);
  400. if (motion_cb)
  401. motion_cb(for_back, right_left);
  402. set_char_state(for_back, right_left);
  403. if (move_dir_cd && for_back_pr != for_back)
  404. move_dir_cd(for_back, right_left, 0);
  405. }
  406. var move_right_cb = function(value) {
  407. if (!check_character_look())
  408. return;
  409. var curr_state = get_state_machine_state(sm).id;
  410. if (curr_state == CS_CLIMB)
  411. value = 0;
  412. value = value > 0.0 ? 1.0 : value;
  413. value = value < 0.0 ? -1.0 : value;
  414. var right_left_pr = move_state.left + move_state.right;
  415. move_state.right = -value;
  416. var for_back = move_state.forw + move_state.back;
  417. var right_left = move_state.left + move_state.right;
  418. m_phy.set_character_move_dir(character, for_back, right_left);
  419. if (motion_cb)
  420. motion_cb(for_back, right_left);
  421. set_char_state(for_back, right_left);
  422. if (move_dir_cd && right_left_pr != right_left)
  423. move_dir_cd(for_back, right_left, 0);
  424. }
  425. var move_left_cb = function(value) {
  426. if (!check_character_look())
  427. return;
  428. var curr_state = get_state_machine_state(sm).id;
  429. if (curr_state == CS_CLIMB)
  430. value = 0;
  431. var right_left_pr = move_state.left + move_state.right;
  432. move_state.left = value;
  433. var for_back = move_state.forw + move_state.back;
  434. var right_left = move_state.left + move_state.right;
  435. m_phy.set_character_move_dir(character, for_back, right_left);
  436. if (motion_cb)
  437. motion_cb(for_back, right_left);
  438. set_char_state(for_back, right_left);
  439. if (move_dir_cd && right_left_pr != right_left)
  440. move_dir_cd(for_back, right_left, 0);
  441. }
  442. var jump_cb = function(value) {
  443. if (!check_character_look())
  444. return;
  445. if (value > 0.0) {
  446. m_phy.character_jump(character);
  447. if (move_dir_cd)
  448. move_dir_cd(move_state.forw + move_state.back,
  449. move_state.left + move_state.right, 1);
  450. }
  451. }
  452. var change_movestyle = function(value) {
  453. movestyle = value;
  454. if (Math.abs(move_state.forw + move_state.back) +
  455. Math.abs(move_state.left + move_state.right)) {
  456. if (value > 0.0)
  457. state_machine_switch_state(sm, CS_RUN);
  458. else
  459. state_machine_switch_state(sm, CS_WALK);
  460. if (move_dir_cd)
  461. move_dir_cd(move_state.forw + move_state.back,
  462. move_state.left + move_state.right, 0);
  463. } else
  464. state_machine_switch_state(sm, CS_STAY);
  465. }
  466. var change_flystyle = function(value) {
  467. if (value > 0.0) {
  468. var state = get_state_machine_state(_character_sm).id;
  469. if (state == CS_WALK || state == CS_STAY)
  470. state_machine_switch_state(sm, CS_FLY);
  471. else
  472. state_machine_switch_state(sm, CS_WALK);
  473. }
  474. }
  475. var forward_sens_arr = settings.forward_sens_arr;
  476. var backward_sens_arr = settings.backward_sens_arr;
  477. var right_sens_arr = settings.right_sens_arr;
  478. var left_sens_arr = settings.left_sens_arr;
  479. var jump_sens_arr = settings.jump_sens_arr;
  480. var run_sens_arr = settings.run_sens_arr;
  481. var fly_sens_arr = settings.fly_sens_arr;
  482. bind_action(AT_PRESSED, forward_sens_arr, move_forward_cb);
  483. bind_action(AT_PRESSED, backward_sens_arr, move_backward_cb);
  484. bind_action(AT_PRESSED, right_sens_arr, move_right_cb);
  485. bind_action(AT_PRESSED, left_sens_arr, move_left_cb);
  486. bind_action(AT_PRESSED, jump_sens_arr, jump_cb);
  487. bind_action(AT_CONTINUOUS, [m_input.GMPD_AXIS_0], move_right_cb);
  488. bind_action(AT_CONTINUOUS, [m_input.GMPD_AXIS_1], move_backward_cb);
  489. bind_action(AT_PRESSED, run_sens_arr, change_movestyle);
  490. bind_action(AT_PRESSED, fly_sens_arr, change_flystyle);
  491. }
  492. function get_state_machine() {
  493. if (!_character_sm)
  494. _character_sm = {
  495. nodes: [],
  496. current_node: null,
  497. lock: false,
  498. last_state: null
  499. };
  500. return _character_sm;
  501. }
  502. function state_machine_add_state(state_machine, id, allowed_ids, call_switch,
  503. call_before_switch, call_after_switch) {
  504. state_machine.nodes.push({
  505. id: id,
  506. allowed_ids: allowed_ids,
  507. call_switch: call_switch,
  508. call_before_switch: call_before_switch,
  509. call_after_switch: call_after_switch
  510. });
  511. }
  512. function check_state_machine_validation(state_machine) {
  513. var names = [];
  514. for (var i = 0; i < state_machine.nodes; i++) {
  515. var id = state_machine.nodes[i].id;
  516. if (names.indexOf(id) >= 0)
  517. return false;
  518. else
  519. names.push(id);
  520. }
  521. for (var i = 0; i < state_machine.nodes; i++) {
  522. var node = state_machine.nodes[i];
  523. for (var j = 0; j < node.allowed_ids.length; j++) {
  524. if (names.indexOf(node.allowed_ids[j]) < 0)
  525. return false;
  526. }
  527. }
  528. return true;
  529. }
  530. function get_state_machine_node(state_machine, node_id) {
  531. var node = null;
  532. for (var i = 0; i < state_machine.nodes.length; i++) {
  533. if (state_machine.nodes[i].id == node_id) {
  534. node = state_machine.nodes[i];
  535. break;
  536. }
  537. }
  538. return node;
  539. }
  540. function set_state_machine_start_node(state_machine, node_id) {
  541. var node = get_state_machine_node(state_machine, node_id);
  542. state_machine.current_node = node;
  543. return node;
  544. }
  545. function get_state_machine_state(state_machine) {
  546. return state_machine.current_node;
  547. }
  548. function set_state_machine_node_after_cb(state_machine, state_id, callback) {
  549. var node = get_state_machine_node(state_machine, state_id);
  550. node.call_after_switch = callback;
  551. }
  552. function set_state_machine_node_before_cb(state_machine, state_id, callback) {
  553. var node = get_state_machine_node(state_machine, state_id);
  554. node.call_before_switch = callback;
  555. }
  556. function state_machine_switch_state(state_machine, new_state_id) {
  557. state_machine.last_state = new_state_id;
  558. if (state_machine.lock) {
  559. return false;
  560. }
  561. var cur_state = get_state_machine_state(state_machine);
  562. var old_state_id = cur_state.id;
  563. if (cur_state.allowed_ids.indexOf(new_state_id) >= 0) {
  564. var before = true;
  565. if (cur_state.call_before_switch) {
  566. before = cur_state.call_before_switch(old_state_id, new_state_id)
  567. }
  568. if (before) {
  569. set_state_machine_start_node(state_machine, new_state_id);
  570. if (cur_state.call_switch)
  571. cur_state.call_switch(old_state_id, new_state_id);
  572. if (cur_state.call_after_switch)
  573. cur_state.call_after_switch(old_state_id, new_state_id);
  574. return true
  575. }
  576. }
  577. return false
  578. }
  579. function init_character_states(character) {
  580. var sm = get_state_machine();
  581. var change_state_cb = function(old_state_id, new_state_id) {
  582. switch(new_state_id) {
  583. case CS_STAY:
  584. case CS_WALK:
  585. m_phy.set_character_move_type(character, m_phy.CM_WALK);
  586. break;
  587. case CS_RUN:
  588. m_phy.set_character_move_type(character, m_phy.CM_RUN);
  589. break;
  590. case CS_FLY:
  591. m_phy.set_character_move_type(character, m_phy.CM_FLY);
  592. break;
  593. case CS_CLIMB:
  594. m_phy.set_character_move_type(character, m_phy.CM_CLIMB);
  595. break;
  596. };
  597. }
  598. state_machine_add_state(sm, CS_STAY, [CS_WALK, CS_FLY, CS_RUN, CS_CLIMB], change_state_cb,
  599. null, null);
  600. state_machine_add_state(sm, CS_WALK, [CS_RUN, CS_FLY, CS_CLIMB, CS_STAY], change_state_cb,
  601. null, null);
  602. state_machine_add_state(sm, CS_RUN, [CS_WALK, CS_FLY, CS_CLIMB, CS_STAY], change_state_cb,
  603. null, null);
  604. state_machine_add_state(sm, CS_FLY, [CS_WALK], change_state_cb,
  605. null, null);
  606. state_machine_add_state(sm, CS_CLIMB, [CS_WALK, CS_RUN, CS_STAY], change_state_cb,
  607. null, null);
  608. check_state_machine_validation(sm);
  609. set_state_machine_start_node(sm, CS_STAY);
  610. return _character_sm;
  611. }
  612. function remove_devices_controls() {
  613. for (var i = 0; i < _manifold_counter; i++)
  614. m_ctl.remove_sensor_manifold(null, "FPS_USER_CONTROL_ACTION_"
  615. + i.toString());
  616. }
  617. function remove_plock_controls(character, elem) {
  618. if (check_pointerlock(elem) && !m_main.detect_mobile()) {
  619. if (_plock_cb.disable_cb)
  620. _plock_cb.disable_cb(elem);
  621. m_ctl.remove_sensor_manifold(character, "FPS_PLOCK");
  622. m_ctl.remove_sensor_manifold(null, "FPS_ACTIVATE_PLOCK");
  623. }
  624. }
  625. function remove_drag_controls(character, elem) {
  626. if (!check_pointerlock(elem) || m_main.detect_mobile()) {
  627. m_ctl.remove_sensor_manifold(character, "FPS_CAM_ROT");
  628. m_ctl.remove_sensor_manifold(character, "FPS_DRAG_PRESS");
  629. m_ctl.remove_sensor_manifold(character, "FPS_DRAG_MOVE");
  630. m_ctl.remove_sensor_manifold(character, "FPS_SMOOTH_DRAG");
  631. }
  632. }
  633. function remove_hmd_controls(character) {
  634. if (check_vr_support())
  635. m_ctl.remove_sensor_manifold(character, "FPS_CHARECTER_VR_ROT");
  636. }
  637. function remove_mobile_controls(character, elem) {
  638. if (m_main.detect_mobile()) {
  639. var forward_btn = document.getElementById(MOBILE_FORWARD_BTN_ID);
  640. var backward_btn = document.getElementById(MOBILE_BACKWARD_BTN_ID);
  641. if (forward_btn)
  642. elem.removeChild(forward_btn);
  643. if (backward_btn)
  644. elem.removeChild(backward_btn);
  645. }
  646. }
  647. function set_curr_gamepad_id(new_id) {
  648. _curr_gamepad_id = new_id;
  649. }
  650. /**
  651. * Character state defining that character is staying.
  652. * @const {CharacterState} module:fps.CS_STAY
  653. */
  654. exports.CS_STAY = CS_STAY;
  655. /**
  656. * Character state defining that character is in walk-mode.
  657. * @const {CharacterState} module:fps.CS_WALK
  658. */
  659. exports.CS_WALK = CS_WALK;
  660. /**
  661. * Character state defining that character is in run-mode.
  662. * @const {CharacterState} module:fps.CS_RUN
  663. */
  664. exports.CS_RUN = CS_RUN;
  665. /**
  666. * Character state defining that character is in fly-mode.
  667. * @const {CharacterState} module:fps.CS_FLY
  668. */
  669. exports.CS_FLY = CS_FLY;
  670. /**
  671. * Character state defining that character is in climb-mode.
  672. * @const {CharacterState} module:fps.CS_CLIMB
  673. */
  674. exports.CS_CLIMB = CS_CLIMB;
  675. /**
  676. * An input type detecting a discrete user action, e.g. button press.
  677. * @const module:fps.AT_PRESSED
  678. */
  679. exports.AT_PRESSED = AT_PRESSED;
  680. /**
  681. * An input type detecting a discrete user action, e.g. button release.
  682. * @const module:fps.AT_RELEASED
  683. */
  684. exports.AT_RELEASED = AT_RELEASED;
  685. /**
  686. * An input type detecting a continuous user action,
  687. * e.g. a held down button, mouse movement, gamepad stick tilt etc.
  688. * @const module:fps.AT_CONTINUOUS
  689. */
  690. exports.AT_CONTINUOUS = AT_CONTINUOUS;
  691. /**
  692. * Bind action callback to user controls. This allows to set and define an additional
  693. * action to an event and set the conditions to when and how the event happens.
  694. * @method module:fps.bind_action
  695. * @param {number} action_type Type of action
  696. * @param {Array} action_controls Array of sensor types
  697. * @param {Function} action_cb Function which applies logic
  698. * @example var m_ctl = require("controls");
  699. * var m_fps = require("fps");
  700. * var m_input = require("input");
  701. *
  702. * var action_cb = function(value) {
  703. * console.log(value);
  704. * };
  705. *
  706. * // bind custom callback to be executed after pressing the W key or one of the gamepad
  707. * // buttons or after clicking/touching the html element with the "forward_button_id" id
  708. * m_fps.bind_action(m_fps.AT_PRESSED, [m_ctl.KEY_W, m_input.GMPD_BUTTON_12,
  709. * "forward_button_id"], action_cb);
  710. */
  711. exports.bind_action = bind_action;
  712. function bind_action(action_type, action_controls, action_cb) {
  713. if (!action_controls.length)
  714. return;
  715. var sensors = [];
  716. for (var j = 0; j < action_controls.length; j++) {
  717. if (typeof action_controls[j] == "number") {
  718. if (action_controls[j] < GMPD_BTNS_OFFSET)
  719. sensors.push(m_ctl.create_keyboard_sensor(action_controls[j]));
  720. else if (action_controls[j] < GMPD_AXIS_OFFSET) {
  721. sensors.push(m_ctl.create_gamepad_btn_sensor(action_controls[j],
  722. _curr_gamepad_id));
  723. } else
  724. sensors.push(m_ctl.create_gamepad_axis_sensor(action_controls[j]));
  725. } else if (typeof action_controls[j] == "string") {
  726. var control_element = document.getElementById(action_controls[j]);
  727. if (control_element) {
  728. sensors.push(m_ctl.create_touch_click_sensor(control_element));
  729. } else
  730. m_print.error("Couldn't find element with " +
  731. action_controls[j] + " ID.");
  732. }
  733. }
  734. var logic_func = function(s) {
  735. return false;
  736. }
  737. var type = m_ctl.CT_SHOT;
  738. var manifold_cb = function(obj, id, pulse, sens_num) {}
  739. switch(action_type) {
  740. case AT_PRESSED:
  741. type = m_ctl.CT_TRIGGER;
  742. logic_func = function(s) {
  743. for (var i = 0; i < s.length; i++)
  744. if (s[i])
  745. return true;
  746. return false;
  747. }
  748. manifold_cb = function(obj, id, pulse, sens_num) {
  749. if (pulse > 0)
  750. action_cb(1.0);
  751. else
  752. action_cb(0.0);
  753. }
  754. break;
  755. case AT_RELEASED:
  756. type = m_ctl.CT_TRIGGER;
  757. logic_func = function(s) {
  758. for (var i = 0; i < s.length; i++)
  759. if (s[i])
  760. return true;
  761. return false;
  762. }
  763. manifold_cb = function(obj, id, pulse, sens_num) {
  764. if (pulse < 0)
  765. action_cb(1.0);
  766. else
  767. action_cb(0.0);
  768. }
  769. break;
  770. case AT_CONTINUOUS:
  771. type = m_ctl.CT_CONTINUOUS;
  772. logic_func = function(s) {
  773. for (var i = 0; i < s.length; i++)
  774. if (Math.abs(s[i]) > AXIS_THRESHOLD)
  775. return true;
  776. return false;
  777. }
  778. manifold_cb = function(obj, id, pulse, sens_num) {
  779. if (pulse > 0) {
  780. for (var i = 0; i < sens_num; i++) {
  781. var value = m_ctl.get_sensor_value(obj, id, i);
  782. if (Math.abs(value) > AXIS_THRESHOLD) {
  783. action_cb(value);
  784. return;
  785. }
  786. }
  787. } else
  788. action_cb(0.0);
  789. }
  790. break;
  791. };
  792. var action_id = "FPS_USER_CONTROL_ACTION_" + _manifold_counter.toString();
  793. var sensors_number = sensors.length;
  794. m_ctl.create_sensor_manifold(null, action_id, type,
  795. sensors, logic_func, manifold_cb, sensors_number);
  796. _manifold_counter++;
  797. }
  798. /**
  799. * Enable FPS controls. This sets keyboard, gamepad and mouse controls.
  800. * VR is also plug-and-play ready.
  801. * @method module:fps.enable_fps_controls
  802. * @param {Object} [options={}] Initialization options.
  803. * @param {Object3D} [options.character=The result of the {@link module:scenes.get_first_character|get_first_character()} method call] Character.
  804. * @param {HTMLElement} [options.element=The result of the {@link module:container.get_canvas|get_canvas()} method call] HTML element to add event listeners to.
  805. * @param {CharMotionCallback} [options.motion_cb=null] Motion callback function
  806. * @param {number} [options.gamepad_id=0] Connected gamepad ID.
  807. * @param {number[]} [options.forward_sens=[{@link module:controls.KEY_W|KEY_W}, {@link module:input.GMPD_BUTTON_12|GMPD_BUTTON_12}]] Array of sensor types used for forward motion.
  808. * @param {number[]} [options.backward_sens=[{@link module:controls.KEY_S|KEY_S}, {@link module:input.GMPD_BUTTON_13|GMPD_BUTTON_13}]] Array of sensor types used for backward motion.
  809. * @param {number[]} [options.right_sens=[{@link module:controls.KEY_D|KEY_D}, {@link module:input.GMPD_BUTTON_15|GMPD_BUTTON_15}]] Array of sensor types used for right motion.
  810. * @param {number[]} [options.left_sens=[{@link module:controls.KEY_A|KEY_A}, {@link module:input.GMPD_BUTTON_14|GMPD_BUTTON_14}]] Array of sensor types used for left motion.
  811. * @param {number[]} [options.jump_sens=[{@link module:controls.KEY_SPACE|KEY_SPACE}, {@link module:input.GMPD_BUTTON_1|GMPD_BUTTON_1}]] Array of sensor types used for jumping.
  812. * @param {number[]} [options.fly_sens=[{@link module:controls.KEY_SHIFT|KEY_SHIFT}, {@link module:input.GMPD_BUTTON_7|GMPD_BUTTON_7}]] Array of sensor types used for flying.
  813. * @param {CharRotationCallback} [options.rotation_cb] Callback for camera rotation. If not specified, the default one will be used.
  814. * @param {boolean} [options.lock_camera=false] Parent camera to the character
  815. * @cc_externs character element gamepad_id
  816. * @cc_externs forward_sens backward_sens right_sens left_sens
  817. * @cc_externs report_init_failure pause_invisible key_pause_enabled
  818. * @cc_externs jump_sens fly_sens rotation_cb lock_camera
  819. * @cc_externs move_dir_cd motion_cb
  820. * @example var m_fps = require("fps");
  821. *
  822. * var character = m_scene.get_first_character();
  823. *
  824. * var move_cb = function(forw_back, right_left) {
  825. * console.log(forw_back, right_left);
  826. * }
  827. *
  828. * m_fps.enable_fps_controls(character, null, move_cb);
  829. */
  830. exports.enable_fps_controls = function(options) {
  831. options = options || {lock_camera : true};
  832. var character = options.character || m_scs.get_first_character();
  833. if (!character)
  834. return;
  835. var elem = options.element || m_cont.get_canvas();
  836. var motion_cb = options.motion_cb || null;
  837. set_curr_gamepad_id(options.gamepad_id || 0);
  838. var forward_sens_arr = options.forward_sens || [m_ctl.KEY_W, m_input.GMPD_BUTTON_12];
  839. var backward_sens_arr = options.backward_sens || [m_ctl.KEY_S, m_input.GMPD_BUTTON_13];
  840. var right_sens_arr = options.right_sens || [m_ctl.KEY_D, m_input.GMPD_BUTTON_15];
  841. var left_sens_arr = options.left_sens || [m_ctl.KEY_A, m_input.GMPD_BUTTON_14];
  842. var jump_sens_arr = options.jump_sens || [m_ctl.KEY_SPACE, m_input.GMPD_BUTTON_1];
  843. var run_sens_arr = options.run_sens || [m_ctl.KEY_SHIFT, m_input.GMPD_BUTTON_7];
  844. var fly_sens_arr = options.fly_sens || [];
  845. if (m_main.detect_mobile()) {
  846. forward_sens_arr.push(MOBILE_FORWARD_BTN_ID);
  847. backward_sens_arr.push(MOBILE_BACKWARD_BTN_ID);
  848. }
  849. var move_dir_cd = options.move_dir_cd || null;
  850. var configs = {
  851. forward_sens_arr : forward_sens_arr,
  852. backward_sens_arr : backward_sens_arr,
  853. right_sens_arr : right_sens_arr,
  854. left_sens_arr : left_sens_arr,
  855. jump_sens_arr : jump_sens_arr,
  856. run_sens_arr : run_sens_arr,
  857. fly_sens_arr : fly_sens_arr,
  858. move_dir_cd: move_dir_cd
  859. };
  860. _rotation_cb = options.rotation_cb || default_rotation_cb;
  861. var lock_camera = typeof options.lock_camera != "undefined" ? options.lock_camera : true;
  862. set_characters_camera(character, lock_camera);
  863. enable_camera_rotation(elem, character);
  864. enable_movements(elem, character, motion_cb, configs);
  865. }
  866. /**
  867. * Disable FPS controls.
  868. * @method module:fps.disable_fps_controls
  869. * @param {Object3D} [character=The result of the {@link module:scenes.get_first_character|get_first_character()} method call] Character
  870. * @param {HTMLElement} [elem=Canvas container element] HTML element to add event listeners to
  871. * @example var m_fps = require("fps");
  872. *
  873. * m_fps.disable_fps_controls();
  874. */
  875. exports.disable_fps_controls = function(character, elem) {
  876. character = character || m_scs.get_first_character();
  877. elem = elem || m_cont.get_container();
  878. if (!character)
  879. return;
  880. remove_devices_controls();
  881. remove_plock_controls(character, elem);
  882. remove_drag_controls(character, elem);
  883. remove_hmd_controls(character);
  884. remove_mobile_controls(character, elem);
  885. }
  886. /**
  887. * Set character state changing callback function.
  888. * @method module:fps.set_state_change_cb
  889. * @param {number} state_id State ID
  890. * @param {ChangeStateCallback} callback Callback function
  891. * @example var m_fps = require("fps");
  892. *
  893. * var state_changing_cb = function(old_state_id, new_state_id) {
  894. * console.log(old_state_id, new_state_id);
  895. * }
  896. *
  897. * m_fps.set_state_change_cb(m_fps.CS_WALK, state_changing_cb);
  898. */
  899. exports.set_state_change_cb = function(state_id, callback) {
  900. var sm = _character_sm;
  901. set_state_machine_node_after_cb(sm, state_id, callback);
  902. }
  903. /**
  904. * Set character's camera smooth behavior
  905. * @method module:fps.set_cam_smooth_factor
  906. * @param {number} value Smooth factor
  907. * @example var m_fps = require("fps");
  908. *
  909. * m_fps.set_cam_smooth_factor(0.2);
  910. */
  911. exports.set_cam_smooth_factor = function(value) {
  912. m_ctl.set_plock_smooth_factor(value);
  913. set_smooth_factor(value);
  914. }
  915. /**
  916. * Set character's camera smooth behavior
  917. * @method module:fps.get_cam_smooth_factor
  918. * @example var m_fps = require("fps");
  919. *
  920. * var smooth_factor = m_fps.get_cam_smooth_factor();
  921. */
  922. exports.get_cam_smooth_factor = function() {
  923. return _smooth_factor;
  924. }
  925. /**
  926. * Set character's camera mouse sensitivity
  927. * @method module:fps.set_cam_sensitivity
  928. * @param {number} value Sensitivity
  929. * @example var m_fps = require("fps");
  930. *
  931. * m_fps.set_cam_sensitivity(80);
  932. */
  933. exports.set_cam_sensitivity = set_cam_sensitivity;
  934. function set_cam_sensitivity(value) {
  935. _fps_camera_mult = value / MULT_SCALE;
  936. }
  937. /**
  938. * Get character's camera mouse sensitivity
  939. * @method module:fps.get_cam_sensitivity
  940. * @example var m_fps = require("fps");
  941. *
  942. * var sens = m_fps.get_cam_sensitivity();
  943. */
  944. exports.get_cam_sensitivity = get_cam_sensitivity;
  945. function get_cam_sensitivity() {
  946. return _fps_camera_mult * MULT_SCALE;
  947. }
  948. /**
  949. * Set pointerlock callback function, which is called when pointerlock is enabled
  950. * @method module:fps.set_plock_enable_cb
  951. * @param {PlockCallback} callback Callback function
  952. * @example var m_fps = require("fps");
  953. * var cb = function(element) {
  954. * console.log("pointerlock is enabled");
  955. * }
  956. *
  957. * m_fps.set_plock_enable_cb(cb);
  958. */
  959. exports.set_plock_enable_cb = function(callback) {
  960. _plock_cb.enable_cb = callback;
  961. }
  962. /**
  963. * Set pointerlock callback function, which is called when pointerlock is disabled
  964. * @method module:fps.set_plock_disable_cb
  965. * @param {PlockCallback} callback Callback function
  966. * @example var m_fps = require("fps");
  967. *
  968. * var cb = function(element) {
  969. * console.log("pointerlock is disabled");
  970. * }
  971. *
  972. * m_fps.set_plock_disable_cb(cb);
  973. */
  974. exports.set_plock_disable_cb = function(callback) {
  975. _plock_cb.disable_cb = callback;
  976. }
  977. /**
  978. * Get character's current state.
  979. * @method module:fps.get_character_state
  980. * @returns {CharacterState} Character's current state
  981. * @example var m_fps = require("fps");
  982. *
  983. * var curr_state = m_fps.get_character_state();
  984. *
  985. * if (curr_state == m_fps.CS_RUN)
  986. * console.log("Character is running");
  987. */
  988. exports.get_character_state = function() {
  989. return get_state_machine_state(_character_sm).id;
  990. }
  991. /**
  992. * Add new character's state.
  993. * @method module:fps.add_new_state
  994. * @returns {CharacterState} Character's new state
  995. * @example var m_fps = require("fps");
  996. *
  997. * var new_state = m_fps.add_new_state();
  998. */
  999. exports.add_new_state = function() {
  1000. return _state_counter++;
  1001. }
  1002. /**
  1003. * Add new character's state to its state machine.
  1004. * @method module:fps.add_state
  1005. * @param {CharacterState} new_state Character's new state
  1006. * @param {CharacterState[]} enabled_transitions Enabled transitions to another states
  1007. * @param {ChangeStateCallback} change_state_cb Callback function
  1008. * @example var m_fps = require("fps");
  1009. *
  1010. * var new_state = m_fps.add_new_state();
  1011. * var state_changing_cb = function(old_state_id, new_state_id) {
  1012. * console.log(old_state_id, new_state_id);
  1013. * }
  1014. * m_fps.add_state(new_state, [m_fps.CS_WALK, m_fps.CS_RUN], state_changing_cb);
  1015. */
  1016. exports.add_state = function(new_state, enabled_transitions, change_state_cb) {
  1017. var sm = get_state_machine();
  1018. state_machine_add_state(sm, new_state, enabled_transitions, change_state_cb,
  1019. null, null);
  1020. if (!check_state_machine_validation(sm))
  1021. m_print.error("Incorrect state machine.");
  1022. }
  1023. /**
  1024. * Switch character's state.
  1025. * @method module:fps.switch_state
  1026. * @param {CharacterState} state Character's state
  1027. * @example var m_fps = require("fps");
  1028. *
  1029. * m_fps.switch_state(m_fps.CS_WALK);
  1030. */
  1031. exports.switch_state = function(state) {
  1032. var sm = get_state_machine();
  1033. state_machine_switch_state(sm, state);
  1034. }
  1035. /**
  1036. * Lock character's state changing.
  1037. * @method module:fps.lock_character
  1038. * @example var m_fps = require("fps");
  1039. *
  1040. * m_fps.lock_character();
  1041. */
  1042. exports.lock_character = function() {
  1043. var sm = get_state_machine();
  1044. sm.lock = true;
  1045. }
  1046. /**
  1047. * Unlock character's state changing.
  1048. * @method module:fps.unlock_character
  1049. * @example var m_fps = require("fps");
  1050. *
  1051. * m_fps.unlock_character();
  1052. */
  1053. exports.unlock_character = function() {
  1054. var sm = get_state_machine();
  1055. sm.lock = false;
  1056. }
  1057. /**
  1058. * Check if character's state changing is locked.
  1059. * @method module:fps.is_character_locked
  1060. * @example var m_fps = require("fps");
  1061. *
  1062. * if (m_fps.is_character_locked())
  1063. * console.log("character is locked");
  1064. */
  1065. exports.is_character_locked = function() {
  1066. var sm = get_state_machine();
  1067. return sm.lock;
  1068. }
  1069. /**
  1070. * Set character rotation callback.
  1071. * @method module:fps.set_rotation_cb
  1072. * @param {CharRotationCallback} rotation_cb Character's rotation callback
  1073. * @example var m_fps = require("fps");
  1074. *
  1075. * m_fps.set_rotation_cb(function(char, rot_x, rot_y) {});
  1076. */
  1077. exports.set_rotation_cb = function(rotation_cb) {
  1078. if (!rotation_cb)
  1079. _rotation_cb = default_rotation_cb;
  1080. else
  1081. _rotation_cb = rotation_cb;
  1082. }
  1083. /**
  1084. * Freeze character's movements.
  1085. * @method module:fps.freeze_movements
  1086. * @example var m_fps = require("fps");
  1087. *
  1088. * m_fps.freeze_movements();
  1089. */
  1090. exports.freeze_movements = function() {
  1091. _is_freezed = true;
  1092. }
  1093. /**
  1094. * Unfreeze character's movements.
  1095. * @method module:fps.unfreeze_movements
  1096. * @example var m_fps = require("fps");
  1097. *
  1098. * m_fps.unfreeze_movements();
  1099. */
  1100. exports.unfreeze_movements = function() {
  1101. _is_freezed = false;
  1102. }
  1103. };
  1104. var fps_factory = register("fps", FPS);
  1105. export default fps_factory;