AvatarController.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // designate component
  2. "atomic component";
  3. var glmatrix = require("gl-matrix");
  4. var quat = glmatrix.quat;
  5. var vec3 = glmatrix.vec3;
  6. module.exports = function(self) {
  7. var game = Atomic.game;
  8. var node = self.node;
  9. var onGround = true;
  10. var okToJump = true;
  11. var inAirTime = 0;
  12. var MOVE_FORCE = 1.8;
  13. var INAIR_MOVE_FORCE = 0.02;
  14. var BRAKE_FORCE = 0.2;
  15. var JUMP_FORCE = 7.0;
  16. var YAW_SENSITIVITY = 0.1;
  17. var INAIR_THRESHOLD_TIME = 0.1;
  18. var cameraMode = 0;
  19. var yaw = 0;
  20. var pitch = 0;
  21. var moveForward = false;
  22. var moveBackwards = false;
  23. var moveLeft = false;
  24. var moveRight = false;
  25. var mouseMoveX = 0.0;
  26. var mouseMoveY = 0.0;
  27. var button0 = false;
  28. var button1 = false;
  29. var lastButton0 = false;
  30. var lastButton1 = false;
  31. self.idle = true;
  32. self.start = function() {
  33. // Create rigidbody, and set non-zero mass so that the body becomes dynamic
  34. var body = node.createComponent("RigidBody");
  35. body.mass = 1.0;
  36. // Set zero angular factor so that physics doesn't turn the character on its own.
  37. // Instead we will control the character yaw manually
  38. body.angularFactor = [0, 0, 0];
  39. // Set the rigidbody to signal collision also when in rest, so that we get ground collisions properly
  40. body.collisionEventMode = Atomic.COLLISION_ALWAYS;
  41. // Set a capsule shape for collision
  42. var shape = node.createComponent("CollisionShape");
  43. shape.setCapsule(2, 4, [0, 2, 0]);
  44. }
  45. self.fixedUpdate = function(timestep) {
  46. var body = node.getComponent("RigidBody");
  47. // Update the in air timer. Reset if grounded
  48. if (!onGround)
  49. inAirTimer += timeStep;
  50. else
  51. inAirTimer = 0.0;
  52. // When character has been in air less than 1/10 second, it's still interpreted as being on ground
  53. var softGrounded = inAirTimer < INAIR_THRESHOLD_TIME;
  54. var rot = node.getRotation();
  55. var moveDir = [0, 0, 0];
  56. // Update movement & animation
  57. var velocity = body.getLinearVelocity();
  58. // Velocity on the XZ plane
  59. var planeVelocity = [velocity[0], 0.0, velocity[2]];
  60. if (cameraMode != 2) {
  61. if (moveForward) {
  62. vec3.add(moveDir, moveDir, [0, 0, 1])
  63. }
  64. if (moveBackwards) {
  65. vec3.add(moveDir, moveDir, [0, 0, -1])
  66. }
  67. if (moveLeft) {
  68. vec3.add(moveDir, moveDir, [-1, 0, 0])
  69. }
  70. if (moveRight) {
  71. vec3.add(moveDir, moveDir, [1, 0, 0])
  72. }
  73. }
  74. if (vec3.length(moveDir) > 0.0)
  75. vec3.normalize(moveDir, moveDir);
  76. vec3.transformQuat(moveDir, moveDir, [rot[1], rot[2], rot[3], rot[0]]);
  77. vec3.scale(moveDir, moveDir, (softGrounded ? MOVE_FORCE : INAIR_MOVE_FORCE));
  78. body.applyImpulse(moveDir);
  79. if (softGrounded) {
  80. // When on ground, apply a braking force to limit maximum ground velocity
  81. vec3.negate(planeVelocity, planeVelocity);
  82. vec3.scale(planeVelocity, planeVelocity, BRAKE_FORCE);
  83. body.applyImpulse(planeVelocity);
  84. // Jump. Must release jump control inbetween jumps
  85. if (button1) {
  86. if (okToJump) {
  87. var jumpforce = [0, 1, 0];
  88. vec3.scale(jumpforce, jumpforce, JUMP_FORCE);
  89. body.applyImpulse(jumpforce);
  90. okToJump = false;
  91. }
  92. } else
  93. okToJump = true;
  94. }
  95. if (softGrounded && vec3.length(moveDir) > 0.0)
  96. self.idle = false;
  97. else
  98. self.idle = true;
  99. // Reset grounded flag for next frame
  100. onGround = true;
  101. }
  102. function MoveCamera(timeStep) {
  103. // Movement speed as world units per second
  104. var MOVE_SPEED = 10.0;
  105. // Mouse sensitivity as degrees per pixel
  106. var MOUSE_SENSITIVITY = 0.1;
  107. yaw = yaw + MOUSE_SENSITIVITY * mouseMoveX;
  108. pitch = pitch + MOUSE_SENSITIVITY * mouseMoveY;
  109. if (pitch < -90)
  110. pitch = -90;
  111. if (pitch > 90)
  112. pitch = 90;
  113. // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
  114. var cameraNode = game.cameraNode;
  115. cameraNode.rotation = QuatFromEuler(pitch, yaw, 0.0);
  116. var speed = MOVE_SPEED * timeStep;
  117. if (moveForward)
  118. cameraNode.translate([0.0, 0.0, speed])
  119. if (moveBackwards)
  120. cameraNode.translate([0.0, 0.0, -speed])
  121. if (moveLeft)
  122. cameraNode.translate([-speed, 0.0, 0.0])
  123. if (moveRight)
  124. cameraNode.translate([speed, 0.0, 0.0])
  125. }
  126. function UpdateControls() {
  127. var input = game.input;
  128. moveForward = false;
  129. moveBackwards = false;
  130. moveLeft = false;
  131. moveRight = false;
  132. mouseMoveX = 0.0;
  133. mouseMoveY = 0.0;
  134. button0 = false;
  135. button1 = false;
  136. // Movement speed as world units per second
  137. var MOVE_SPEED = 20.0;
  138. // Mouse sensitivity as degrees per pixel
  139. var MOUSE_SENSITIVITY = 0.1;
  140. if (input.getKeyDown(Atomic.KEY_W))
  141. moveForward = true;
  142. if (input.getKeyDown(Atomic.KEY_S))
  143. moveBackwards = true;
  144. if (input.getKeyDown(Atomic.KEY_A))
  145. moveLeft = true;
  146. if (input.getKeyDown(Atomic.KEY_D))
  147. moveRight = true;
  148. if (input.getKeyPress(Atomic.KEY_F))
  149. button0 = true;
  150. if (input.getKeyPress(Atomic.KEY_SPACE))
  151. button1 = true;
  152. mouseMoveX = input.getMouseMoveX();
  153. mouseMoveY = input.getMouseMoveY();
  154. }
  155. self.update = function(timeStep) {
  156. UpdateControls();
  157. if (cameraMode != 2) {
  158. yaw += mouseMoveX * YAW_SENSITIVITY;
  159. pitch += mouseMoveY * YAW_SENSITIVITY;
  160. }
  161. if (pitch < -80)
  162. pitch = -80;
  163. if (pitch > 80)
  164. pitch = 80;
  165. if (button0) {
  166. cameraMode++;
  167. if (cameraMode == 3)
  168. cameraMode = 0;
  169. }
  170. }
  171. self.postUpdate = function(timestep) {
  172. // Get camera lookat dir from character yaw + pitch
  173. var rot = node.getRotation();
  174. dir = quat.create();
  175. quat.setAxisAngle(dir, [1, 0, 0], (pitch * Math.PI / 180.0));
  176. quat.multiply(dir, [rot[1], rot[2], rot[3], rot[0]], dir);
  177. var headNode = node.getChild("Head_Tip", true);
  178. var cameraNode = game.cameraNode;
  179. if (cameraMode == 1) {
  180. var headPos = headNode.getWorldPosition();
  181. var offset = [0.0, 0.15, 0.2];
  182. vec3.add(headPos, headPos, vec3.transformQuat(offset, offset, [rot[1], rot[2], rot[3], rot[0]]));
  183. cameraNode.setPosition(headPos);
  184. cameraNode.setRotation([dir[3], dir[0], dir[1], dir[2]]);
  185. quat.setAxisAngle(dir, [0, 1, 0], (yaw * Math.PI / 180.0));
  186. node.setRotation([dir[3], dir[0], dir[1], dir[2]]);
  187. }
  188. if (cameraMode == 0) {
  189. var aimPoint = node.getPosition();
  190. var aimOffset = [0, 1.7, 0];
  191. vec3.transformQuat(aimOffset, aimOffset, dir);
  192. vec3.add(aimPoint, aimPoint, aimOffset);
  193. var rayDir = vec3.create();
  194. vec3.transformQuat(rayDir, [0, 0, -1], dir);
  195. vec3.scale(rayDir, rayDir, 12);
  196. vec3.add(aimPoint, aimPoint, rayDir);
  197. cameraNode.setPosition(aimPoint);
  198. cameraNode.setRotation([dir[3], dir[0], dir[1], dir[2]]);
  199. quat.setAxisAngle(dir, [0, 1, 0], (yaw * Math.PI / 180.0));
  200. node.setRotation([dir[3], dir[0], dir[1], dir[2]]);
  201. }
  202. else
  203. MoveCamera(timestep);
  204. }
  205. }