AvatarController.js 10 KB

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