AvatarController.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // designate component
  2. "atomic component";
  3. "use strict";
  4. var __extends = (this && this.__extends) || function (d, b) {
  5. for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
  6. function __() { this.constructor = d; }
  7. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  8. };
  9. //import gl-matrix library
  10. //https://github.com/toji/gl-matrix for more information
  11. var gl_matrix_1 = require("gl-matrix");
  12. //define constans
  13. var MOVE_FORCE = 1.8;
  14. var INAIR_MOVE_FORCE = 0.02;
  15. var BRAKE_FORCE = 0.2;
  16. var JUMP_FORCE = 7.0;
  17. var YAW_SENSITIVITY = 0.1;
  18. var INAIR_THRESHOLD_TIME = 0.1;
  19. //define a component AvatarController
  20. var AvatarController = (function (_super) {
  21. __extends(AvatarController, _super);
  22. function AvatarController() {
  23. _super.apply(this, arguments);
  24. //define an inspectorFields to make variables visible in editor
  25. this.inspectorFields = {
  26. //needs default value to make editor understand type of that value
  27. speed: 1.0
  28. };
  29. this.speed = 1.0;
  30. this.onGround = true;
  31. this.okToJump = true;
  32. this.inAirTime = 0.0;
  33. this.softGrounded = true;
  34. this.cameraMode = 0;
  35. this.yaw = 0;
  36. this.pitch = 0;
  37. this.moveForward = false;
  38. this.moveBackwards = false;
  39. this.moveLeft = false;
  40. this.moveRight = false;
  41. this.mouseMoveX = 0.0;
  42. this.mouseMoveY = 0.0;
  43. this.button0 = false;
  44. this.button1 = false;
  45. this.lastButton0 = false;
  46. this.lastButton1 = false;
  47. this.idle = true;
  48. }
  49. AvatarController.prototype.start = function () {
  50. //get main camera and set its node to cameraNode
  51. var camera = this.node.scene.getMainCamera();
  52. this.cameraNode = camera.node;
  53. // Create rigidbody, and set non-zero mass so that the body becomes dynamic
  54. var body = this.node.createComponent("RigidBody");
  55. body.mass = 1.0;
  56. // Set zero angular factor so that physics doesn't turn the character on its own.
  57. // Instead we will control the character yaw manually
  58. body.angularFactor = [0, 0, 0];
  59. // Set the rigidbody to signal collision also when in rest, so that we get ground collisions properly
  60. body.collisionEventMode = Atomic.COLLISION_ALWAYS;
  61. // Set a capsule shape for collision
  62. var shape = this.node.createComponent("CollisionShape");
  63. shape.setCapsule(2, 4, [0, 2, 0]);
  64. };
  65. AvatarController.prototype.fixedUpdate = function (timeStep) {
  66. //get a RigidBody component from the current node
  67. var body = this.node.getComponent("RigidBody");
  68. // Update the in air timer. Reset if grounded
  69. if (!this.onGround) {
  70. this.inAirTime += timeStep;
  71. }
  72. else {
  73. this.inAirTime = 0.0;
  74. }
  75. // When character has been in air less than 1/10 second, it's still interpreted as being on ground
  76. var softGrounded = this.inAirTime < INAIR_THRESHOLD_TIME;
  77. // Get rotation of the current node
  78. var rot = this.node.getRotation();
  79. var moveDir = [0, 0, 0];
  80. // Update movement & animation
  81. var velocity = body.getLinearVelocity();
  82. // Velocity on the XZ plane
  83. var planeVelocity = [velocity[0], 0.0, velocity[2]];
  84. if (this.cameraMode != 2) {
  85. if (this.moveForward) {
  86. gl_matrix_1.vec3.add(moveDir, moveDir, [0, 0, 1]);
  87. }
  88. if (this.moveBackwards) {
  89. gl_matrix_1.vec3.add(moveDir, moveDir, [0, 0, -1]);
  90. }
  91. if (this.moveLeft) {
  92. gl_matrix_1.vec3.add(moveDir, moveDir, [-1, 0, 0]);
  93. }
  94. if (this.moveRight) {
  95. gl_matrix_1.vec3.add(moveDir, moveDir, [1, 0, 0]);
  96. }
  97. }
  98. if (gl_matrix_1.vec3.length(moveDir) > 0.0) {
  99. gl_matrix_1.vec3.normalize(moveDir, moveDir);
  100. }
  101. gl_matrix_1.vec3.transformQuat(moveDir, moveDir, [rot[1], rot[2], rot[3], rot[0]]);
  102. gl_matrix_1.vec3.scale(moveDir, moveDir, (softGrounded ? MOVE_FORCE : INAIR_MOVE_FORCE));
  103. if (this.softGrounded) {
  104. gl_matrix_1.vec3.scale(moveDir, moveDir, this.speed);
  105. }
  106. body.applyImpulse(moveDir);
  107. if (this.softGrounded) {
  108. // When on ground, apply a braking force to limit maximum ground velocity
  109. gl_matrix_1.vec3.negate(planeVelocity, planeVelocity);
  110. gl_matrix_1.vec3.scale(planeVelocity, planeVelocity, BRAKE_FORCE);
  111. body.applyImpulse(planeVelocity);
  112. // Jump. Must release jump control inbetween jumps
  113. if (this.button1) {
  114. if (this.okToJump) {
  115. var jumpforce = [0, 1, 0];
  116. gl_matrix_1.vec3.scale(jumpforce, jumpforce, JUMP_FORCE);
  117. //Apply impulse to the body
  118. body.applyImpulse(jumpforce);
  119. this.okToJump = false;
  120. }
  121. }
  122. else {
  123. this.okToJump = true;
  124. }
  125. }
  126. if (this.softGrounded && gl_matrix_1.vec3.length(moveDir) > 0.0) {
  127. this.idle = false;
  128. }
  129. else {
  130. this.idle = true;
  131. }
  132. // Reset grounded flag for next frame
  133. this.onGround = true;
  134. };
  135. AvatarController.prototype.MoveCamera = function (timeStep) {
  136. // Movement speed as world units per second
  137. var MOVE_SPEED = 10.0;
  138. // Mouse sensitivity as degrees per pixel
  139. var MOUSE_SENSITIVITY = 0.1;
  140. this.yaw = this.yaw + MOUSE_SENSITIVITY * this.mouseMoveX;
  141. this.pitch = this.pitch + MOUSE_SENSITIVITY * this.mouseMoveY;
  142. if (this.pitch < -90) {
  143. this.pitch = -90;
  144. }
  145. if (this.pitch > 90) {
  146. this.pitch = 90;
  147. }
  148. // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
  149. this.cameraNode.rotation = QuatFromEuler(this.pitch, this.yaw, 0.0);
  150. var speed = MOVE_SPEED * timeStep;
  151. //translate camera on the amount of speed value
  152. if (this.moveForward) {
  153. this.cameraNode.translate([0.0, 0.0, this.speed]);
  154. }
  155. if (this.moveBackwards) {
  156. this.cameraNode.translate([0.0, 0.0, -this.speed]);
  157. }
  158. if (this.moveLeft) {
  159. this.cameraNode.translate([-speed, 0.0, 0.0]);
  160. }
  161. if (this.moveRight) {
  162. this.cameraNode.translate([speed, 0.0, 0.0]);
  163. }
  164. };
  165. AvatarController.prototype.UpdateControls = function () {
  166. var input = Atomic.input;
  167. this.moveForward = false;
  168. this.moveBackwards = false;
  169. this.moveLeft = false;
  170. this.moveRight = false;
  171. this.mouseMoveX = 0.0;
  172. this.mouseMoveY = 0.0;
  173. this.button0 = false;
  174. this.button1 = false;
  175. // Movement speed as world units per second
  176. // let MOVE_SPEED = 20.0; -- unused
  177. // Mouse sensitivity as degrees per pixel
  178. // let MOUSE_SENSITIVITY = 0.1; -- unused
  179. //check input
  180. if (input.getKeyDown(Atomic.KEY_W) || input.getKeyDown(Atomic.KEY_UP)) {
  181. this.moveForward = true;
  182. }
  183. if (input.getKeyDown(Atomic.KEY_S) || input.getKeyDown(Atomic.KEY_DOWN)) {
  184. this.moveBackwards = true;
  185. }
  186. if (input.getKeyDown(Atomic.KEY_A) || input.getKeyDown(Atomic.KEY_LEFT)) {
  187. this.moveLeft = true;
  188. }
  189. if (input.getKeyDown(Atomic.KEY_D) || input.getKeyDown(Atomic.KEY_RIGHT)) {
  190. this.moveRight = true;
  191. }
  192. if (input.getKeyPress(Atomic.KEY_F)) {
  193. this.button0 = true;
  194. }
  195. if (input.getKeyPress(Atomic.KEY_SPACE)) {
  196. this.button1 = true;
  197. }
  198. //if we are on mobile
  199. if (Atomic.platform == "Android" || Atomic.platform == "iOS") {
  200. //iterate through each TouchState, if it doesn't touch any widgets, use it as a `mouse`
  201. for (var i = 0; i < Atomic.input.getNumTouches(); i++) {
  202. var touchState = Atomic.input.getTouch(i);
  203. if (touchState.touchedWidget == null) {
  204. var delta = touchState.delta;
  205. this.mouseMoveX = delta[0];
  206. this.mouseMoveY = delta[1];
  207. }
  208. }
  209. }
  210. else {
  211. // update mouse coordinates
  212. this.mouseMoveX = input.getMouseMoveX();
  213. this.mouseMoveY = input.getMouseMoveY();
  214. }
  215. };
  216. AvatarController.prototype.update = function (timeStep) {
  217. this.UpdateControls();
  218. //if it's a free view
  219. if (this.cameraMode != 2) {
  220. this.yaw += this.mouseMoveX * YAW_SENSITIVITY;
  221. this.pitch += this.mouseMoveY * YAW_SENSITIVITY;
  222. }
  223. if (this.pitch < -80) {
  224. this.pitch = -80;
  225. }
  226. if (this.pitch > 80) {
  227. this.pitch = 80;
  228. }
  229. if (this.button0) {
  230. this.cameraMode++;
  231. if (this.cameraMode == 3) {
  232. this.cameraMode = 0;
  233. }
  234. }
  235. };
  236. //that function called right after update function
  237. AvatarController.prototype.postUpdate = function (timestep) {
  238. // Get camera lookat dir from character yaw + pitch
  239. var rot = this.node.getRotation();
  240. //create quaternion
  241. var dir = gl_matrix_1.quat.create();
  242. //set X value
  243. gl_matrix_1.quat.setAxisAngle(dir, [1, 0, 0], (this.pitch * Math.PI / 180.0));
  244. gl_matrix_1.quat.multiply(dir, [rot[1], rot[2], rot[3], rot[0]], dir);
  245. var headNode = this.node.getChild("Head_Tip", true);
  246. //if it's a FPS view
  247. if (this.cameraMode == 1) {
  248. var headPos = headNode.getWorldPosition();
  249. var offset = [0.0, 0.15, 0.2];
  250. gl_matrix_1.vec3.add(headPos, headPos, gl_matrix_1.vec3.transformQuat(offset, offset, [rot[1], rot[2], rot[3], rot[0]]));
  251. this.cameraNode.setPosition(headPos);
  252. this.cameraNode.setRotation([dir[3], dir[0], dir[1], dir[2]]);
  253. gl_matrix_1.quat.setAxisAngle(dir, [0, 1, 0], (this.yaw * Math.PI / 180.0));
  254. this.node.setRotation([dir[3], dir[0], dir[1], dir[2]]);
  255. }
  256. //if it's a third person view
  257. if (this.cameraMode == 0) {
  258. var aimPoint = this.node.getWorldPosition();
  259. var aimOffset = [0, 1.7, 0];
  260. gl_matrix_1.vec3.transformQuat(aimOffset, aimOffset, dir);
  261. gl_matrix_1.vec3.add(aimPoint, aimPoint, aimOffset);
  262. var rayDir = gl_matrix_1.vec3.create();
  263. gl_matrix_1.vec3.transformQuat(rayDir, [0, 0, -1], dir);
  264. gl_matrix_1.vec3.scale(rayDir, rayDir, 8);
  265. gl_matrix_1.vec3.add(aimPoint, aimPoint, rayDir);
  266. this.cameraNode.setPosition(aimPoint);
  267. this.cameraNode.setRotation([dir[3], dir[0], dir[1], dir[2]]);
  268. gl_matrix_1.quat.setAxisAngle(dir, [0, 1, 0], (this.yaw * Math.PI / 180.0));
  269. this.node.setRotation([dir[3], dir[0], dir[1], dir[2]]);
  270. }
  271. else {
  272. this.MoveCamera(timestep);
  273. }
  274. };
  275. return AvatarController;
  276. }(Atomic.JSComponent));
  277. function QuatFromEuler(x, y, z) {
  278. var M_PI = 3.14159265358979323846264338327950288;
  279. var q = [0, 0, 0, 0];
  280. x *= (M_PI / 360);
  281. y *= (M_PI / 360);
  282. z *= (M_PI / 360);
  283. var sinX = Math.sin(x);
  284. var cosX = Math.cos(x);
  285. var sinY = Math.sin(y);
  286. var cosY = Math.cos(y);
  287. var sinZ = Math.sin(z);
  288. var cosZ = Math.cos(z);
  289. q[0] = cosY * cosX * cosZ + sinY * sinX * sinZ;
  290. q[1] = cosY * sinX * cosZ + sinY * cosX * sinZ;
  291. q[2] = sinY * cosX * cosZ - cosY * sinX * sinZ;
  292. q[3] = cosY * cosX * sinZ - sinY * sinX * cosZ;
  293. return q;
  294. }
  295. module.exports = AvatarController;