AvatarController.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /// <reference path="../TypeScript/Atomic.d.ts"/>
  2. /// <reference path="../TypeScript/AtomicWork.d.ts" />
  3. /// <reference path="../Modules/gl-matrix.d.ts" />
  4. import {vec3, quat} from 'gl-matrix';
  5. "atomic component";
  6. const MOVE_FORCE = 1.8;
  7. const BRAKE_FORCE = 0.2;
  8. const YAW_SENSITIVITY = 0.1;
  9. const PITCH_SENSITIVITY = 0.1;
  10. class AvatarController extends Atomic.JSComponent {
  11. // define inspector fields
  12. inspectorFields = {
  13. speed: 1.0,
  14. cameraDist: 12
  15. }
  16. camera: Atomic.Camera;
  17. cameraNode: Atomic.Node;
  18. body: Atomic.RigidBody;
  19. yaw: number = 0;
  20. pitch: number = 0;
  21. mouseMoveX: number = 0;
  22. mouseMoveY: number = 0;
  23. moveForward: boolean = false;
  24. moveBackwards: boolean = false;
  25. moveLeft: boolean = false;
  26. moveRight: boolean = false;
  27. idle: boolean = true;
  28. speed: number = 1;
  29. cameraDist = 12;
  30. start() {
  31. this.camera = this.node.scene.getMainCamera();
  32. this.cameraNode = this.camera.node;
  33. // Create rigidbody, and set non-zero mass so that the body becomes dynamic
  34. this.body = <Atomic.RigidBody> this.node.createComponent("RigidBody");
  35. this.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. this.body.angularFactor = [0, 0, 0];
  39. // Set the rigidbody to signal collision also when in rest, so that we get ground collisions properly
  40. this.body.collisionEventMode = Atomic.COLLISION_ALWAYS;
  41. // Set a capsule shape for collision
  42. var shape = <Atomic.CollisionShape> this.node.createComponent("CollisionShape");
  43. shape.setCapsule(2, 4, [0, 2, 0]);
  44. }
  45. update(timeStep: number) {
  46. this.updateControls();
  47. }
  48. fixedUpdate(timestep) {
  49. var rot = this.node.getRotation();
  50. var moveDir = [0, 0, 0];
  51. // Update movement & animation
  52. var velocity = this.body.getLinearVelocity();
  53. // Velocity on the XZ plane
  54. var planeVelocity = [velocity[0], 0.0, velocity[2]];
  55. if (this.moveForward) {
  56. vec3.add(moveDir, moveDir, [0, 0, 1])
  57. }
  58. if (this.moveBackwards) {
  59. vec3.add(moveDir, moveDir, [0, 0, -1])
  60. }
  61. if (this.moveLeft) {
  62. vec3.add(moveDir, moveDir, [-1, 0, 0])
  63. }
  64. if (this.moveRight) {
  65. vec3.add(moveDir, moveDir, [1, 0, 0])
  66. }
  67. if (vec3.length(moveDir) > 0.0)
  68. vec3.normalize(moveDir, moveDir);
  69. vec3.transformQuat(moveDir, moveDir, [rot[1], rot[2], rot[3], rot[0]]);
  70. vec3.scale(moveDir, moveDir, (MOVE_FORCE));
  71. vec3.scale(moveDir, moveDir, this.speed);
  72. this.body.applyImpulse(moveDir);
  73. vec3.negate(planeVelocity, planeVelocity);
  74. vec3.scale(planeVelocity, planeVelocity, BRAKE_FORCE);
  75. this.body.applyImpulse(planeVelocity);
  76. if (vec3.length(moveDir) > 0.0)
  77. this.idle = false;
  78. else
  79. this.idle = true;
  80. }
  81. updateControls() {
  82. var input = Atomic.input;
  83. this.moveForward = false;
  84. this.moveBackwards = false;
  85. this.moveLeft = false;
  86. this.moveRight = false;
  87. this.mouseMoveX = 0.0;
  88. this.mouseMoveY = 0.0;
  89. // Movement speed as world units per second
  90. var MOVE_SPEED = 20.0;
  91. // Mouse sensitivity as degrees per pixel
  92. var MOUSE_SENSITIVITY = 0.1;
  93. if (input.getKeyDown(Atomic.KEY_W))
  94. this.moveForward = true;
  95. if (input.getKeyDown(Atomic.KEY_S))
  96. this.moveBackwards = true;
  97. if (input.getKeyDown(Atomic.KEY_A))
  98. this.moveLeft = true;
  99. if (input.getKeyDown(Atomic.KEY_D))
  100. this.moveRight = true;
  101. this.yaw += input.mouseMoveX * YAW_SENSITIVITY;
  102. this.pitch += input.mouseMoveY * PITCH_SENSITIVITY;
  103. if (this.pitch < -80)
  104. this.pitch = -80;
  105. if (this.pitch > 80)
  106. this.pitch = 80;
  107. }
  108. postUpdate(timestep) {
  109. // Get camera lookat dir from character yaw + pitch
  110. var rot = this.node.getRotation();
  111. var dir = quat.create();
  112. quat.setAxisAngle(dir, [1, 0, 0], (this.pitch * Math.PI / 180.0));
  113. quat.multiply(dir, [rot[1], rot[2], rot[3], rot[0]], dir);
  114. var headNode = this.node.getChild("Head_Tip", true);
  115. var aimPoint = <number[]> this.node.getWorldPosition();
  116. var aimOffset = [0, 1.7, 0];
  117. vec3.transformQuat(aimOffset, aimOffset, dir);
  118. vec3.add(aimPoint, aimPoint, aimOffset);
  119. var rayDir = vec3.create();
  120. vec3.transformQuat(rayDir, [0, 0, -1], dir);
  121. vec3.scale(rayDir, rayDir, this.cameraDist);
  122. vec3.add(aimPoint, aimPoint, rayDir);
  123. this.cameraNode.setPosition(aimPoint);
  124. this.cameraNode.setRotation([dir[3], dir[0], dir[1], dir[2]]);
  125. quat.setAxisAngle(dir, [0, 1, 0], (this.yaw * Math.PI / 180.0));
  126. this.node.setRotation([dir[3], dir[0], dir[1], dir[2]]);
  127. }
  128. }
  129. function QuatFromEuler(x, y, z) {
  130. var M_PI = 3.14159265358979323846264338327950288;
  131. var q = [0, 0, 0, 0];
  132. // Order of rotations: Z first, then X, then Y (mimics typical FPS camera with gimbal lock at top/bottom)
  133. x *= (M_PI / 360);
  134. y *= (M_PI / 360);
  135. z *= (M_PI / 360);
  136. var sinX = Math.sin(x);
  137. var cosX = Math.cos(x);
  138. var sinY = Math.sin(y);
  139. var cosY = Math.cos(y);
  140. var sinZ = Math.sin(z);
  141. var cosZ = Math.cos(z);
  142. q[0] = cosY * cosX * cosZ + sinY * sinX * sinZ;
  143. q[1] = cosY * sinX * cosZ + sinY * cosX * sinZ;
  144. q[2] = sinY * cosX * cosZ - cosY * sinX * sinZ;
  145. q[3] = cosY * cosX * sinZ - sinY * sinX * cosZ;
  146. return q;
  147. }
  148. export = AvatarController;