/// /// /// import {vec3, quat} from 'gl-matrix'; "atomic component"; const MOVE_FORCE = 1.8; const BRAKE_FORCE = 0.2; const YAW_SENSITIVITY = 0.1; const PITCH_SENSITIVITY = 0.1; class AvatarController extends Atomic.JSComponent { // define inspector fields inspectorFields = { speed: 1.0, cameraDist: 12 } camera: Atomic.Camera; cameraNode: Atomic.Node; body: Atomic.RigidBody; yaw: number = 0; pitch: number = 0; mouseMoveX: number = 0; mouseMoveY: number = 0; moveForward: boolean = false; moveBackwards: boolean = false; moveLeft: boolean = false; moveRight: boolean = false; idle: boolean = true; speed: number = 1; cameraDist = 12; start() { this.camera = this.node.scene.getMainCamera(); this.cameraNode = this.camera.node; // Create rigidbody, and set non-zero mass so that the body becomes dynamic this.body = this.node.createComponent("RigidBody"); this.body.mass = 1.0; // Set zero angular factor so that physics doesn't turn the character on its own. // Instead we will control the character yaw manually this.body.angularFactor = [0, 0, 0]; // Set the rigidbody to signal collision also when in rest, so that we get ground collisions properly this.body.collisionEventMode = Atomic.COLLISION_ALWAYS; // Set a capsule shape for collision var shape = this.node.createComponent("CollisionShape"); shape.setCapsule(2, 4, [0, 2, 0]); } update(timeStep: number) { this.updateControls(); } fixedUpdate(timestep) { var rot = this.node.getRotation(); var moveDir = [0, 0, 0]; // Update movement & animation var velocity = this.body.getLinearVelocity(); // Velocity on the XZ plane var planeVelocity = [velocity[0], 0.0, velocity[2]]; if (this.moveForward) { vec3.add(moveDir, moveDir, [0, 0, 1]) } if (this.moveBackwards) { vec3.add(moveDir, moveDir, [0, 0, -1]) } if (this.moveLeft) { vec3.add(moveDir, moveDir, [-1, 0, 0]) } if (this.moveRight) { vec3.add(moveDir, moveDir, [1, 0, 0]) } if (vec3.length(moveDir) > 0.0) vec3.normalize(moveDir, moveDir); vec3.transformQuat(moveDir, moveDir, [rot[1], rot[2], rot[3], rot[0]]); vec3.scale(moveDir, moveDir, (MOVE_FORCE)); vec3.scale(moveDir, moveDir, this.speed); this.body.applyImpulse(moveDir); vec3.negate(planeVelocity, planeVelocity); vec3.scale(planeVelocity, planeVelocity, BRAKE_FORCE); this.body.applyImpulse(planeVelocity); if (vec3.length(moveDir) > 0.0) this.idle = false; else this.idle = true; } updateControls() { var input = Atomic.input; this.moveForward = false; this.moveBackwards = false; this.moveLeft = false; this.moveRight = false; this.mouseMoveX = 0.0; this.mouseMoveY = 0.0; // Movement speed as world units per second var MOVE_SPEED = 20.0; // Mouse sensitivity as degrees per pixel var MOUSE_SENSITIVITY = 0.1; if (input.getKeyDown(Atomic.KEY_W)) this.moveForward = true; if (input.getKeyDown(Atomic.KEY_S)) this.moveBackwards = true; if (input.getKeyDown(Atomic.KEY_A)) this.moveLeft = true; if (input.getKeyDown(Atomic.KEY_D)) this.moveRight = true; this.yaw += input.mouseMoveX * YAW_SENSITIVITY; this.pitch += input.mouseMoveY * PITCH_SENSITIVITY; if (this.pitch < -80) this.pitch = -80; if (this.pitch > 80) this.pitch = 80; } postUpdate(timestep) { // Get camera lookat dir from character yaw + pitch var rot = this.node.getRotation(); var dir = quat.create(); quat.setAxisAngle(dir, [1, 0, 0], (this.pitch * Math.PI / 180.0)); quat.multiply(dir, [rot[1], rot[2], rot[3], rot[0]], dir); var headNode = this.node.getChild("Head_Tip", true); var aimPoint = this.node.getWorldPosition(); var aimOffset = [0, 1.7, 0]; vec3.transformQuat(aimOffset, aimOffset, dir); vec3.add(aimPoint, aimPoint, aimOffset); var rayDir = vec3.create(); vec3.transformQuat(rayDir, [0, 0, -1], dir); vec3.scale(rayDir, rayDir, this.cameraDist); vec3.add(aimPoint, aimPoint, rayDir); this.cameraNode.setPosition(aimPoint); this.cameraNode.setRotation([dir[3], dir[0], dir[1], dir[2]]); quat.setAxisAngle(dir, [0, 1, 0], (this.yaw * Math.PI / 180.0)); this.node.setRotation([dir[3], dir[0], dir[1], dir[2]]); } } function QuatFromEuler(x, y, z) { var M_PI = 3.14159265358979323846264338327950288; var q = [0, 0, 0, 0]; // Order of rotations: Z first, then X, then Y (mimics typical FPS camera with gimbal lock at top/bottom) x *= (M_PI / 360); y *= (M_PI / 360); z *= (M_PI / 360); var sinX = Math.sin(x); var cosX = Math.cos(x); var sinY = Math.sin(y); var cosY = Math.cos(y); var sinZ = Math.sin(z); var cosZ = Math.cos(z); q[0] = cosY * cosX * cosZ + sinY * sinX * sinZ; q[1] = cosY * sinX * cosZ + sinY * cosX * sinZ; q[2] = sinY * cosX * cosZ - cosY * sinX * sinZ; q[3] = cosY * cosX * sinZ - sinY * sinX * cosZ; return q; } export = AvatarController;