// designate component "atomic component"; "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; //import gl-matrix library //https://github.com/toji/gl-matrix for more information var gl_matrix_1 = require("gl-matrix"); //define constans var MOVE_FORCE = 1.8; var INAIR_MOVE_FORCE = 0.02; var BRAKE_FORCE = 0.2; var JUMP_FORCE = 7.0; var YAW_SENSITIVITY = 0.1; var INAIR_THRESHOLD_TIME = 0.1; //define a component AvatarController var AvatarController = (function (_super) { __extends(AvatarController, _super); function AvatarController() { _super.apply(this, arguments); //define an inspectorFields to make variables visible in editor this.inspectorFields = { //needs default value to make editor understand type of that value speed: 1.0 }; this.speed = 1.0; this.onGround = true; this.okToJump = true; this.inAirTime = 0.0; this.softGrounded = true; this.cameraMode = 0; this.yaw = 0; this.pitch = 0; this.moveForward = false; this.moveBackwards = false; this.moveLeft = false; this.moveRight = false; this.mouseMoveX = 0.0; this.mouseMoveY = 0.0; this.button0 = false; this.button1 = false; this.lastButton0 = false; this.lastButton1 = false; this.idle = true; } AvatarController.prototype.start = function () { //get main camera and set its node to cameraNode var camera = this.node.scene.getMainCamera(); this.cameraNode = camera.node; // Create rigidbody, and set non-zero mass so that the body becomes dynamic var body = this.node.createComponent("RigidBody"); 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 body.angularFactor = [0, 0, 0]; // Set the rigidbody to signal collision also when in rest, so that we get ground collisions properly body.collisionEventMode = Atomic.COLLISION_ALWAYS; // Set a capsule shape for collision var shape = this.node.createComponent("CollisionShape"); shape.setCapsule(2, 4, [0, 2, 0]); }; AvatarController.prototype.fixedUpdate = function (timeStep) { //get a RigidBody component from the current node var body = this.node.getComponent("RigidBody"); // Update the in air timer. Reset if grounded if (!this.onGround) { this.inAirTime += timeStep; } else { this.inAirTime = 0.0; } // When character has been in air less than 1/10 second, it's still interpreted as being on ground var softGrounded = this.inAirTime < INAIR_THRESHOLD_TIME; // Get rotation of the current node var rot = this.node.getRotation(); var moveDir = [0, 0, 0]; // Update movement & animation var velocity = body.getLinearVelocity(); // Velocity on the XZ plane var planeVelocity = [velocity[0], 0.0, velocity[2]]; if (this.cameraMode != 2) { if (this.moveForward) { gl_matrix_1.vec3.add(moveDir, moveDir, [0, 0, 1]); } if (this.moveBackwards) { gl_matrix_1.vec3.add(moveDir, moveDir, [0, 0, -1]); } if (this.moveLeft) { gl_matrix_1.vec3.add(moveDir, moveDir, [-1, 0, 0]); } if (this.moveRight) { gl_matrix_1.vec3.add(moveDir, moveDir, [1, 0, 0]); } } if (gl_matrix_1.vec3.length(moveDir) > 0.0) { gl_matrix_1.vec3.normalize(moveDir, moveDir); } gl_matrix_1.vec3.transformQuat(moveDir, moveDir, [rot[1], rot[2], rot[3], rot[0]]); gl_matrix_1.vec3.scale(moveDir, moveDir, (softGrounded ? MOVE_FORCE : INAIR_MOVE_FORCE)); if (this.softGrounded) { gl_matrix_1.vec3.scale(moveDir, moveDir, this.speed); } body.applyImpulse(moveDir); if (this.softGrounded) { // When on ground, apply a braking force to limit maximum ground velocity gl_matrix_1.vec3.negate(planeVelocity, planeVelocity); gl_matrix_1.vec3.scale(planeVelocity, planeVelocity, BRAKE_FORCE); body.applyImpulse(planeVelocity); // Jump. Must release jump control inbetween jumps if (this.button1) { if (this.okToJump) { var jumpforce = [0, 1, 0]; gl_matrix_1.vec3.scale(jumpforce, jumpforce, JUMP_FORCE); //Apply impulse to the body body.applyImpulse(jumpforce); this.okToJump = false; } } else { this.okToJump = true; } } if (this.softGrounded && gl_matrix_1.vec3.length(moveDir) > 0.0) { this.idle = false; } else { this.idle = true; } // Reset grounded flag for next frame this.onGround = true; }; AvatarController.prototype.MoveCamera = function (timeStep) { // Movement speed as world units per second var MOVE_SPEED = 10.0; // Mouse sensitivity as degrees per pixel var MOUSE_SENSITIVITY = 0.1; this.yaw = this.yaw + MOUSE_SENSITIVITY * this.mouseMoveX; this.pitch = this.pitch + MOUSE_SENSITIVITY * this.mouseMoveY; if (this.pitch < -90) { this.pitch = -90; } if (this.pitch > 90) { this.pitch = 90; } // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero this.cameraNode.rotation = QuatFromEuler(this.pitch, this.yaw, 0.0); var speed = MOVE_SPEED * timeStep; //translate camera on the amount of speed value if (this.moveForward) { this.cameraNode.translate([0.0, 0.0, this.speed]); } if (this.moveBackwards) { this.cameraNode.translate([0.0, 0.0, -this.speed]); } if (this.moveLeft) { this.cameraNode.translate([-speed, 0.0, 0.0]); } if (this.moveRight) { this.cameraNode.translate([speed, 0.0, 0.0]); } }; AvatarController.prototype.UpdateControls = function () { var input = Atomic.input; this.moveForward = false; this.moveBackwards = false; this.moveLeft = false; this.moveRight = false; this.mouseMoveX = 0.0; this.mouseMoveY = 0.0; this.button0 = false; this.button1 = false; // Movement speed as world units per second // let MOVE_SPEED = 20.0; -- unused // Mouse sensitivity as degrees per pixel // let MOUSE_SENSITIVITY = 0.1; -- unused //check input if (input.getKeyDown(Atomic.KEY_W) || input.getKeyDown(Atomic.KEY_UP)) { this.moveForward = true; } if (input.getKeyDown(Atomic.KEY_S) || input.getKeyDown(Atomic.KEY_DOWN)) { this.moveBackwards = true; } if (input.getKeyDown(Atomic.KEY_A) || input.getKeyDown(Atomic.KEY_LEFT)) { this.moveLeft = true; } if (input.getKeyDown(Atomic.KEY_D) || input.getKeyDown(Atomic.KEY_RIGHT)) { this.moveRight = true; } if (input.getKeyPress(Atomic.KEY_F)) { this.button0 = true; } if (input.getKeyPress(Atomic.KEY_SPACE)) { this.button1 = true; } //if we are on mobile if (Atomic.platform == "Android" || Atomic.platform == "iOS") { //iterate through each TouchState, if it doesn't touch any widgets, use it as a `mouse` for (var i = 0; i < Atomic.input.getNumTouches(); i++) { var touchState = Atomic.input.getTouch(i); if (touchState.touchedWidget == null) { var delta = touchState.delta; this.mouseMoveX = delta[0]; this.mouseMoveY = delta[1]; } } } else { // update mouse coordinates this.mouseMoveX = input.getMouseMoveX(); this.mouseMoveY = input.getMouseMoveY(); } }; AvatarController.prototype.update = function (timeStep) { this.UpdateControls(); //if it's a free view if (this.cameraMode != 2) { this.yaw += this.mouseMoveX * YAW_SENSITIVITY; this.pitch += this.mouseMoveY * YAW_SENSITIVITY; } if (this.pitch < -80) { this.pitch = -80; } if (this.pitch > 80) { this.pitch = 80; } if (this.button0) { this.cameraMode++; if (this.cameraMode == 3) { this.cameraMode = 0; } } }; //that function called right after update function AvatarController.prototype.postUpdate = function (timestep) { // Get camera lookat dir from character yaw + pitch var rot = this.node.getRotation(); //create quaternion var dir = gl_matrix_1.quat.create(); //set X value gl_matrix_1.quat.setAxisAngle(dir, [1, 0, 0], (this.pitch * Math.PI / 180.0)); gl_matrix_1.quat.multiply(dir, [rot[1], rot[2], rot[3], rot[0]], dir); var headNode = this.node.getChild("Head_Tip", true); //if it's a FPS view if (this.cameraMode == 1) { var headPos = headNode.getWorldPosition(); var offset = [0.0, 0.15, 0.2]; gl_matrix_1.vec3.add(headPos, headPos, gl_matrix_1.vec3.transformQuat(offset, offset, [rot[1], rot[2], rot[3], rot[0]])); this.cameraNode.setPosition(headPos); this.cameraNode.setRotation([dir[3], dir[0], dir[1], dir[2]]); gl_matrix_1.quat.setAxisAngle(dir, [0, 1, 0], (this.yaw * Math.PI / 180.0)); this.node.setRotation([dir[3], dir[0], dir[1], dir[2]]); } //if it's a third person view if (this.cameraMode == 0) { var aimPoint = this.node.getWorldPosition(); var aimOffset = [0, 1.7, 0]; gl_matrix_1.vec3.transformQuat(aimOffset, aimOffset, dir); gl_matrix_1.vec3.add(aimPoint, aimPoint, aimOffset); var rayDir = gl_matrix_1.vec3.create(); gl_matrix_1.vec3.transformQuat(rayDir, [0, 0, -1], dir); gl_matrix_1.vec3.scale(rayDir, rayDir, 8); gl_matrix_1.vec3.add(aimPoint, aimPoint, rayDir); this.cameraNode.setPosition(aimPoint); this.cameraNode.setRotation([dir[3], dir[0], dir[1], dir[2]]); gl_matrix_1.quat.setAxisAngle(dir, [0, 1, 0], (this.yaw * Math.PI / 180.0)); this.node.setRotation([dir[3], dir[0], dir[1], dir[2]]); } else { this.MoveCamera(timestep); } }; return AvatarController; }(Atomic.JSComponent)); function QuatFromEuler(x, y, z) { var M_PI = 3.14159265358979323846264338327950288; var q = [0, 0, 0, 0]; 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; } module.exports = AvatarController;