Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <!--

Start Game: SPACE

Move: LEFT / RIGHT
Jump: SPACE

GitHub: github.com/marcusstenbeck/plix

-->

<canvas id="game"></canvas>
              
            
!

CSS

              
                #game {
  position: absolute;
  width: 100%;
  height: 100%;
}
              
            
!

JS

              
                function Util() {}

    Util.intersectRect = function(r1, r2) {
        r1.left = r1.position.x - r1.components.physicsComponent.body.size.x/2;
        r1.top = r1.position.y - r1.components.physicsComponent.body.size.y/2;
        r1.right = r1.position.x + r1.components.physicsComponent.body.size.x/2;
        r1.bottom = r1.position.y + r1.components.physicsComponent.body.size.y/2;

        r2.left = r2.position.x - r2.components.physicsComponent.body.size.x/2;
        r2.top = r2.position.y - r2.components.physicsComponent.body.size.y/2;
        r2.right = r2.position.x + r2.components.physicsComponent.body.size.x/2;
        r2.bottom = r2.position.y + r2.components.physicsComponent.body.size.y/2;


        return !(r2.left > r1.right ||
            r2.right < r1.left ||
            r2.top > r1.bottom ||
            r2.bottom < r1.top);
    };

    Util.angleToPoint = function(from, to) {
        var distToPoint = {
            x: to.x - from.x,
            y: to.y - from.y
        };

        var angle = Math.atan(distToPoint.y/distToPoint.x);

        return angle + (distToPoint.x < 0 ? Math.PI : 0);
    };

    Util.keyMap = {
        37: 'LEFT',
        65: 'A',
        38: 'UP',
        39: 'RIGHT',
        68: 'D',
        40: 'DOWN',
        32: 'SPACE',
        87: 'W',
        83: 'S',
        75: 'K',
        76: 'L'
    };

    Util.keyForCode = function(keyCode) {
        return Util.keyMap[keyCode];
    };

function Body(params) {
		if(!params) params = {};

		this.mass = 1;
		this.pos = new Vec2();
		this.vel = new Vec2();
		this.acc = new Vec2();
		this.accumulatedForce = new Vec2();
		this.isSensor = !!params.isSensor;
		this.layer = typeof params.layer === 'number' ? params.layer : 0b01;

		this.type = params.type || Body.DYNAMIC;

		// TODO: Refactor out of here. Shape class?
		this.shape = {
			name: 'rectangle',
			width: 10,
			height: 10
		};

		this.tag = params.tag || '';
	}

	Body.prototype.getBounds = function() {
		return {
			left: this.pos.x - this.shape.width/2,
			right: this.pos.x + this.shape.width/2,
			top: this.pos.y + this.shape.height/2,
			bottom: this.pos.y - this.shape.height/2
		};
	};

	Body.prototype.applyForce = function(vecForce) {
		this.accumulatedForce.x += vecForce.x;
		this.accumulatedForce.y += vecForce.y;
	};

	Body.prototype.onCollision = function(otherBody) { /*  override this  */ };

	Body.DYNAMIC = 'dynamic';
	Body.KINEMATIC = 'kinematic';

function Vec2(x,y) {
		this.x = isNaN(x) ? 0 : x;
		this.y = isNaN(y) ? 0 : y;
	}

	Vec2.prototype.getLength = function() {
		return Math.sqrt(this.x*this.x + this.y*this.y);
	};

	Vec2.prototype.normalize = function() {
		var mag = this.getLength();
		this.x /= mag;
		this.y /= mag;
	};

function World() {
		this.bodies = [];
		this.forceFields = [];
		this.gravity = new Vec2();

		this.iterationCount = 0;

		this.callbackQueue = {
			aBodies: [],
			bBodies: [],
			collisionVectors: []
		};
	}

	World.prototype.MAX_ITERATIONS = 10;

	World.prototype.update = function(timestep) {
		if(!timestep) {
			console.warn('Bad timestep value', timestep);
			return;
		}

		// Make sure timestep never exceeds 10 ms
		var dt, timeleft, step;
		for(dt = 10, timeleft = timestep; timeleft > 0; timeleft -= dt) {
			step = timeleft < dt ? timeleft : dt;
			this._updateFixedTimeStep(step);
		}

		this.runCallbacks();
	};

	World.prototype._updateFixedTimeStep = function(timestep) {
		/**
		 *  Update positions, velocities, accelerations
		 */
		this._integrate(timestep);

		/**
		 *  Check for collisions
		 */
		var collisions = this._detectCollisions(this.bodies);

		/**
		 *  Resolve collisions
		 */
		this.iterationCount = 0;
		this._resolveCollisions(collisions);
	};

	World.prototype._integrate = function(timestep) {
		var body;

		var i, _len = this.bodies.length;
		for(i = 0; i < _len; i++) {
			body = this.bodies[i];

			/* TODO: Remove this?
			for(var j = 0; j < this.forceFields.length; j++) {
				var ff = this.forceFields[j];

				var dist = new Vec2(ff.pos.x - body.pos.x, ff.pos.y - body.pos.y);
				dist.normalize();

				body.accumulatedForce.x += dist.x * ff.magnitude / body.mass;
				body.accumulatedForce.y += dist.y * ff.magnitude / body.mass;
			}
			*/

			// Calculate acceleration
			switch(body.type) {
				case Body.DYNAMIC:
					body.acc.x = this.gravity.x + (body.accumulatedForce.x / body.mass);
					body.acc.y = this.gravity.y + (body.accumulatedForce.y / body.mass);
					break;
				case Body.KINEMATIC:
					body.acc.x = body.accumulatedForce.x / body.mass;
					body.acc.y = body.accumulatedForce.y / body.mass;
					break;
			}

			// Zero out accumulated force
			body.accumulatedForce.x = 0;
			body.accumulatedForce.y = 0;

			// Calculate velocity
			body.vel.x += body.acc.x * timestep;
			body.vel.y += body.acc.y * timestep;

			// Calculate position
			body.pos.x += body.vel.x * timestep;
			body.pos.y += body.vel.y * timestep;
		}
	};

	World.prototype._detectCollisions = function(bodies) {
		/**
		 *  AABB collision detection
		 */
		var collisions = [];

		var i, j, ba, bb, dh1, dh2, dv1, dv2, collisionVector, intersectionDepth, _len = bodies.length;
		for(i = 0; i < (_len - 1); i++) {
			ba = bodies[i].getBounds();

			for(j = i+1; j < _len; j++) {

				if(!(bodies[i].layer & bodies[j].layer)) {
					// The bodies do not share any layer
					continue;
				}

				bb = bodies[j].getBounds();

				dh1 = ba.right - bb.left;
				dh2 = bb.right - ba.left;
				dv1 = ba.top - bb.bottom;
				dv2 = bb.top - ba.bottom;

				if(dh1 <= 0 || dh2 <= 0 || dv1 <= 0 || dv2 <= 0) continue;  // no collision
				

				// ----- If we've come here, there has to be a collision ------ //


				intersectionDepth = {
					x: (dh1 < dh2 ? dh1 : dh2),
					y: (dv1 < dv2 ? dv1 : dv2)
				};

				// Determine collision axis
				if(intersectionDepth.x < intersectionDepth.y) {
					// horizontal collision
					collisionVector = new Vec2(bodies[i].pos.x - bodies[j].pos.x, 0);
				} else {
					// vertical collision
					collisionVector = new Vec2(0, bodies[i].pos.y - bodies[j].pos.y);
				}

				this.queueCallback(bodies[i], bodies[j], collisionVector);
				
				if(!!bodies[i].isSensor || !!bodies[j].isSensor) {
					// one of the bodies is a sensor, so there's no need to do anything else
					continue;
				}

				collisions.push([ bodies[i], bodies[j], collisionVector ]);
			}
		}

		return collisions;
	};

	World.prototype._resolveCollisions = function(collisions) {
		if(collisions.length === 0) return;

		if(this.iterationCount > this.MAX_ITERATIONS) {
			// Bail out!
			throw 'Too many iterations: ' +  this.iterationCount;
		}
		
		this.iterationCount += 1;
		
		var col;
		while(collisions.length > 0) {
			col = collisions.shift();

			// Resolve collision
			
			if(col[0].type == Body.DYNAMIC) {

				switch(col[1].type) {
					case Body.DYNAMIC:
						// dynamic - dynamic
						this._resolveDynamicDynamic(col[0], col[1], col[2]);  // col[2]: vectorAtoB
						break;

					case Body.KINEMATIC:
						// dynamic - kinematic
						this._resolveDynamicKinematic(col[0], col[1], col[2]);  // col[0]: dynamic, col[1]: kinematic, col[2]: vectorAtoB
						break;
				}
			} else if(col[0].type == Body.KINEMATIC) {
				switch(col[1].type) {
					case Body.DYNAMIC:
						// kinematic - dynamic

						// Right now the collisionVector is pointing in the opposite
						// direction of what _resolveDynamicKinematic() expects.
						// Reverse the direction of the vector
						col[2].x *= -1;
						col[2].y *= -1;

						this._resolveDynamicKinematic(col[1], col[0], col[2]);  // col[0]: kinematic, col[1]: dynamic, col[2]: vectorAtoB
						break;

					case Body.KINEMATIC:
						// kinematic - kinematic
						this._resolveKinematicKinematic(col[0], col[1], col[2]);
						break;
				}
			}
			delete col[0];
			delete col[1];
			delete col[2];
		}
		return this._resolveCollisions(this._detectCollisions(this.bodies));
	};

	World.prototype._resolveDynamicDynamic = function(dynamicBody1, dynamicBody2, vectorAtoB) {
		var stabilityHack = 0.000000001;

		var vecSolve = {
			x: (0.5 * (dynamicBody1.shape.width + dynamicBody2.shape.width) - Math.abs(vectorAtoB.x) + stabilityHack) * Math.sign(vectorAtoB.x),
			y: (0.5 * (dynamicBody1.shape.height + dynamicBody2.shape.height) - Math.abs(vectorAtoB.y) + stabilityHack) * Math.sign(vectorAtoB.y)
		};

		// Add solving vector
		dynamicBody1.pos.x += vecSolve.x;
		dynamicBody1.pos.y += vecSolve.y;
		dynamicBody2.pos.x -= vecSolve.x;
		dynamicBody2.pos.y -= vecSolve.y;

		var newVel1 = {
			x: (dynamicBody1.vel.x * (dynamicBody1.mass - dynamicBody2.mass) + (2 * dynamicBody2.mass * dynamicBody2.vel.x)) / (dynamicBody1.mass + dynamicBody2.mass),
			y: (dynamicBody1.vel.y * (dynamicBody1.mass - dynamicBody2.mass) + (2 * dynamicBody2.mass * dynamicBody2.vel.y)) / (dynamicBody1.mass + dynamicBody2.mass)
		};

		var newVel2 = {
			x: (dynamicBody2.vel.x * (dynamicBody1.mass - dynamicBody2.mass) + (2 * dynamicBody1.mass * dynamicBody1.vel.x)) / (dynamicBody1.mass + dynamicBody2.mass),
			y: (dynamicBody2.vel.y * (dynamicBody1.mass - dynamicBody2.mass) + (2 * dynamicBody1.mass * dynamicBody1.vel.y)) / (dynamicBody1.mass + dynamicBody2.mass)
		};

		dynamicBody1.vel.x = newVel1.x;
		dynamicBody1.vel.y = newVel1.y;
		dynamicBody2.vel.x = newVel2.x;
		dynamicBody2.vel.y = newVel2.y;
	};

	World.prototype._resolveDynamicKinematic = function(dynamicBody, kinematicBody, vectorAtoB) {

		var stabilityHack = 0.000000001;

		var vecSolve = {
			x: (0.5 * (dynamicBody.shape.width + kinematicBody.shape.width) - Math.abs(vectorAtoB.x) + stabilityHack) * Math.sign(vectorAtoB.x),
			y: (0.5 * (dynamicBody.shape.height + kinematicBody.shape.height) - Math.abs(vectorAtoB.y) + stabilityHack) * Math.sign(vectorAtoB.y)
		};

		// Add solving vector
		dynamicBody.pos.x += vecSolve.x;
		dynamicBody.pos.y += vecSolve.y;

		// Reverse velocity and a some artificial energy loss
		if(vectorAtoB.x !== 0) {
			if(Math.sign(vectorAtoB.x) === Math.sign(kinematicBody.vel.x)) {
				dynamicBody.vel.x = kinematicBody.vel.x;
			} else {
				dynamicBody.vel.x *= -0.98;
			}
		}

		if(vectorAtoB.y !== 0) {
			if(Math.sign(vectorAtoB.y) === Math.sign(kinematicBody.vel.y)) {
				dynamicBody.vel.y = kinematicBody.vel.y;
			} else {
				dynamicBody.vel.y *= -0.98;
			}
		}
	};

	World.prototype._resolveKinematicKinematic = function(body1, body2, vectorAtoB) {

		// TODO: This shows up a lot, DRY
		var stabilityHack = 0.000000001;

		// TODO: This shows up a lot, DRY
		var vecSolve = new Vec2(
			(0.5 * (body1.shape.width + body2.shape.width) - Math.abs(vectorAtoB.x) + stabilityHack) * Math.sign(vectorAtoB.x),
			(0.5 * (body1.shape.height + body2.shape.height) - Math.abs(vectorAtoB.y) + stabilityHack) * Math.sign(vectorAtoB.y)
		);

		var ratio;
		if(vectorAtoB.x !== 0 && vectorAtoB.y !== 0) {
			// TODO: Diagonal collision
		} else if(vectorAtoB.x === 0) {
			// TODO: Vertical collision
		} else if(vectorAtoB.y === 0) {
			// Horizontal collision
			if(body1.vel.x === 0) ratio = 0;
			else if(body2.vel.x === 0) ratio = 1;
			else {
				ratio = Math.abs(body1.vel.x) / (Math.abs(body1.vel.x) + Math.abs(body2.vel.x));
			}

			// "Reverse time"
			body1.pos.x += vecSolve.x * ratio;
			body1.pos.y += vecSolve.y * ratio;
			body2.pos.x -= vecSolve.x * (1 - ratio);
			body2.pos.y -= vecSolve.y * (1 - ratio);
		}
	};

	World.prototype.queueCallback = function(bodyA, bodyB, collisionVector) {

		for(var i = 0; i < this.callbackQueue.aBodies.length; i++) {
			if(this.callbackQueue.aBodies[i] === bodyA && this.callbackQueue.bBodies[i] === bodyB) {
				// pair already exists
				return;
			}
		}

		this.callbackQueue.aBodies.push(bodyA);
		this.callbackQueue.bBodies.push(bodyB);
		this.callbackQueue.collisionVectors.push(collisionVector);
	};

	World.prototype.runCallbacks = function(bodyA, bodyB, collisionVector) {
		var _len = this.callbackQueue.aBodies.length;

		if(_len === 0) return;

		var i;
		for(i = 0; i < _len; i++) {
			this.callbackQueue.aBodies[i].onCollision(this.callbackQueue.bBodies[i], this.callbackQueue.collisionVectors[i]);
			this.callbackQueue.bBodies[i].onCollision(this.callbackQueue.aBodies[i], this.callbackQueue.collisionVectors[i]);
		}

		this.callbackQueue.aBodies.length = 0;
		this.callbackQueue.bBodies.length = 0;
		this.callbackQueue.collisionVectors.length = 0;
	};


function Component(params) {
    	if(!params) params = {};

        this.entity = params.entity ? params.entity : undefined;
        if(!this) {
            console.warn('No entity to attach component to!');
        }
    }

    Component.prototype.setEntity = function(entity) {
        this.entity = entity;
    };

    Component.prototype.receiveMessage = function(message) {
        console.warn(this, 'receiveMessage() not implemented', message);
    };

/**
     *  Currently this is nothing more than an empty component
     *  that's used to determine whether an entity should be
     *  considered to be a camera.
     */

    function CameraComponent(params) {
        Component.call(this, params);
        if(!params) params = {};

        this.type = 'camera';
    }
    CameraComponent.prototype = Object.create(Component.prototype);

    CameraComponent.prototype.receiveMessage = function() {};


function KeyboardInputComponent(params) {
        Component.call(this, params);
        if(!params) params = {};

        this.type = 'input';

        this.keys = {};
    }
    KeyboardInputComponent.prototype = Object.create(Component.prototype);
    KeyboardInputComponent.prototype.constructor = Component;

    KeyboardInputComponent.prototype.receiveMessage = function(message) {
        message = message.split(':');

        // 'keyup' and 'keydown' are the messages we care about
        if(!(message[0] === 'keyup' || message[0] === 'keydown')) return;
        var state = message[0];

        // We're not interested in undefined keys
        if(message[1] === 'undefined') return;
        var key = message[1];

        // At this point we know that it's either
        // a keyup or a keydown event, and that the
        // key isn't undefined
        this.keys[key] = state === 'keydown' ? true : false;
    };

    KeyboardInputComponent.prototype.setEntity = function(entity) {
        this.entity = entity;
    };

/**
	 * A state. It's just data.
	 */
	function State(name) {
		this.name = name || Math.ceil(Math.random()*1000000);
		this.transitions = {};
		this.actions = {
			onEnter: [],
			onExit: []
		};
	}
	State.prototype.addTransition = function(eventName, targetState) {
		this.transitions[eventName] = targetState;
		return this;
	};
	State.prototype.onEnter = function(fn) {
		this.actions.onEnter.push(fn);
		return this;
	};
	State.prototype.onExit = function(fn) {
		this.actions.onExit.push(fn);
		return this;
	};

/**
	 * State machine. It changes states, and it runs the enter and exit actions in the states.
	 *
	 * param.startState {State} The starting state. Default: new State('init')
	 * param.states {[State]} An array of states
	 *
	 */
	function FSM(params) {
		if(!params) params = {};
		this.owner = params.owner || null;
		this.state = params.startState || new State('init');
		this.states = params.states || [this.state];

		// Run starting states entry actions
		this.enterState(this.state.name);
	}

	FSM.prototype.triggerEvent = function(eventName) {
		// Check if current state will exit on this event

		var nextStateName = this.state.transitions[eventName];

		// No transition? Then warn and gettouttahere!
		if(!nextStateName) {
			// console.warn('State', this.state.name, 'has no transition for event', eventName);
			return this;
		}

		this.enterState(nextStateName);

		return this;
	};
	
	FSM.prototype.createState = function(name) {
		var state = new State(name);
		this.states.push(state);
		return state;
	};
	
	FSM.prototype.deleteState = function(stateName) {
		if(typeof this.states[stateName] === 'object') delete this.states[stateName];
	};

	FSM.prototype.enterState = function(nextStateName) {
		if(this.state.name === nextStateName) return;

		for(var i = 0; i < this.states.length; i++) {
			if(this.states[i].name === nextStateName) {
				
				// perform the exit actions of current state
				for(var j = 0; j < this.states[i].actions.onExit.length; j++) {
					if(typeof this.states[i].actions.onExit[j] === 'function') this.states[i].actions.onExit[j](this.owner);
				}

				// Switch to next state
				this.state = this.states[i];

				// perform the entry actions of current state
				for(j = 0; j < this.states[i].actions.onEnter.length; j++) {
					if(typeof this.states[i].actions.onEnter[j] === 'function') this.states[i].actions.onEnter[j](this.owner);
				}

				return;
			}
		}

		// Couldn't find the state...
		console.error('State', this.state.name, 'could not transition to state', nextStateName, ': It does not exist');
		return this;
	};

function FsmComponent(params) {
        if(!params) params = {};
        this.type = 'fsm';

        // The state machine
        this._fsm = params.fsm || new FSM({owner: this.entity});
    }
    FsmComponent.prototype = Object.create(Component.prototype);

    FsmComponent.prototype.receiveMessage = function(message) {
        // Do nothing
    };

    FsmComponent.prototype.createState = function(stateName) {
        return this._fsm.createState(stateName);
    };

    FsmComponent.prototype.enterState = function(stateName) {
        // TODO: If currently in a state, then run it's onExit
        this._fsm.enterState(stateName);
        return this;
    };

    FsmComponent.prototype.setEntity = function(entity) {
        this.entity = entity;
        this._fsm.owner = this.entity;
    };

function PhysicsComponent(params) {
        Component.call(this, params);
        if(!params) params = {};

        // Component type
        this.type = 'physics';

        // Set up physics body
        this.body = new Body();
        this.body.pos = new Vec2(this.entity.transform.position.x, this.entity.transform.position.y);
        this.body.shape.width = params.width || 1;
        this.body.shape.height = params.height || 1;
        this.body.type = params.type || Body.DYNAMIC;
        this.body.tag = params.tag || '';
        this.body.mass = parseInt(params.mass) || 1;
        this.body.layer = typeof params.layer === 'number' ? params.layer : 0b01;

        if(!this.entity.scene._physicsWorld) {
            this.entity.scene._physicsWorld = new World();
        }

        this.entity.scene._physicsWorld.bodies.push(this.body);
    }
    PhysicsComponent.prototype = Object.create(Component.prototype);
    PhysicsComponent.prototype.constructor = Component;

    PhysicsComponent.prototype.receiveMessage = function(message) {
        switch(message) {
            case 'destroy':
                this.destroy();
                break;
        }
    };

    PhysicsComponent.prototype.setEntity = function(entity) {
        this.entity = entity;
    };

    PhysicsComponent.prototype.on = function(eventName, fn) {
        if(typeof fn === 'function') {
            var onEventName = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1);
            this.body[onEventName] = fn.bind(this);
        }
    };

    PhysicsComponent.prototype.destroy = function() {
        var index = this.entity.scene._physicsWorld.bodies.indexOf(this.body);

        if(index === -1) {
            throw 'Body not found in physics world.'    
        }

        // Remove the body from physics world from the scene list
        this.entity.scene._physicsWorld.bodies.splice(index, 1);

        delete this.body;
    };

function GraphicsComponent(params) {
        Component.call(this, params);
        if(!params) params = {};

        this.type = 'graphics';

        this.graphic = {
            type: 'shape',
            color: [0, 1, 0, 1],
            shapeData: {
                type: 'rectangle',
                width: 32,
                height: 32
            }
        };
    }
    GraphicsComponent.prototype = Object.create(Component.prototype);

    GraphicsComponent.prototype.receiveMessage = function() {};


function Entity(params) {
        if(!params) params = {};

        this.components = {};

        this.transform = {
            position: { x:0, y:0 },
            rotation: 0
        };

        this.components.graphics = new GraphicsComponent({ entity: this });

        this.scene = params.scene || null;
        this.data = {};
    }

    Entity.prototype.broadcastMessage = function(message) {
        var _this = this;
        Object.keys(this.components).forEach(function(key) {
            _this.components[key].receiveMessage(message);
        });
    };

    Entity.prototype.addComponent = function(component) {
        if(!component.type) {
            console.warn('No component type!!!', component);
            return;
        }

        // Set the component's entity to this
        component.setEntity(this);

        // Add component to components
        this.components[component.type] = component;
    };

    Entity.prototype.destroy = function() {
        this.broadcastMessage('destroy');

        var index = this.scene.entities.indexOf(this);

        // Remove the entity from the scene list
        this.scene.entities.splice(index, 1);
    };

function Scene() {
		this.entities = [];

		this.app = undefined;
	}

	Scene.prototype.getEntityByType = function(type) {
		for(var i = 0; i < this.entities.length; i++) {
			if(this.entities[i].type == type) return this.entities[i];
		}

		return;
	};

	Scene.prototype.attachEntity = function(ent) {
		this.entities.push(ent);
		ent.scene = this;
		return this;
	};

	Scene.prototype.update = function(tpf) {
		if(this._physicsWorld) this._physicsWorld.update(tpf);
			
		this.entities.forEach(function(entity) {
			if(typeof entity.script === 'function') entity.script(entity);
			if(entity.components.physics) {
				entity.transform.position.x = entity.components.physics.body.pos.x;
				entity.transform.position.y = entity.components.physics.body.pos.y;
			}
		});
	};

	Scene.prototype.broadcastMessage = function(message) {
        this.entities.forEach(function(ent) {
        	ent.broadcastMessage(message);
        });
    };

var PHYSICS_LAYER_ONE = 0b01;
    var PHYSICS_LAYER_TWO = 0b10;

    function PlatformGameFactory() {}

    PlatformGameFactory.createPlayer = function(scene, options) {

        var playerEntity = new Entity();
        scene.attachEntity(playerEntity);

        playerEntity.transform.position.x = options.x;
        playerEntity.transform.position.y = options.y;
        

        // Add physics component
        var pc = new PhysicsComponent({
                entity: playerEntity,
                type: Body.DYNAMIC,
                width: options.width,
                height: options.height,
                tag: 'player',
                layer: PHYSICS_LAYER_ONE
            });
        pc.on('collision', function(otherBody, collisionVector) {
            if(
                Math.abs(collisionVector.x) < 
                Math.abs(collisionVector.y) && 
                collisionVector.y < 0) {
                // Landed, so don't bounce!
                this.body.vel.y = 0;

                // Trigger grounded state
                playerEntity.components.fsm._fsm.triggerEvent('ground');
            }

            if(otherBody.tag === 'goal') {
                scene.app.nextLevel();
            }

            if(otherBody.tag === 'enemy') {
                scene.app.playerDied();
            }
        });
        playerEntity.addComponent(pc);


        // Add input component
        var ic = new KeyboardInputComponent({
            entity: playerEntity
        });
        playerEntity.addComponent(ic);

        // Create FSM component
        var fsm = new FsmComponent();
        playerEntity.addComponent(fsm);

        var sideMove = function(ent) {
            var xForce = 0;
            xForce += ent.components.input.keys[options.keys.left] ? -0.01 : 0;
            xForce += ent.components.input.keys[options.keys.right] ? 0.01 : 0;

            if(xForce !== 0) {
                ent.components.physics.body.applyForce({ x: xForce, y: 0 });
            }
            ent.components.physics.body.vel.x *= 0.8;
        };

        // Configure FSM
        fsm.createState('grounded')
            .onEnter(function(ent) {
                ent.script = function() {
                    sideMove(ent);

                    if(ent.components.input.keys[options.keys.jump]) {
                        // Add jumping force
                        ent.components.physics.body.applyForce({
                            x: 0,
                            y: -0.1
                        });

                        // Trigger jump event
                        fsm._fsm.triggerEvent('jump');
                    }
                };
            })
            .addTransition('jump', 'jumping');

        fsm.createState('jumping')
            .onEnter(function(ent) {
                ent.script = sideMove;
            })
            .addTransition('ground', 'grounded');
        
        // Start in the jumping state
        fsm.enterState('jumping');

        return playerEntity;
    };

    PlatformGameFactory.createWall = function(scene, options) {

        var wall = new Entity();
        scene.attachEntity(wall);

        wall.transform.position.x = options.x;
        wall.transform.position.y = options.y;

        var pc = new PhysicsComponent({
                tag: options.tag || 'wall',
                entity: wall,
                type: Body.KINEMATIC,
                width: options.width,
                height: options.height,
                layer: PHYSICS_LAYER_ONE | PHYSICS_LAYER_TWO
            });
        wall.addComponent(pc);

        return wall;
    };

    PlatformGameFactory.createEnemy = function(scene, options) {
        options = options || {};

        options.tag = 'enemy';

        var enemy = this.createWall(scene, options);

        enemy.components.graphics.graphic.color = [1, 0, 0, 1];

        return enemy;
    };

    PlatformGameFactory.createPickup = function(scene, options) {
        options = options || {};

        options.tag = 'pickup';
        options.width = options.width || 15;
        options.height = options.height || 15;

        function spawnFrag(scene, options) {
            options = options || {};

            var frag = new Entity();
            scene.attachEntity(frag);

            frag.transform.position.x = options.xPos;
            frag.transform.position.y = options.yPos;

            // Add physics component
            var pc = new PhysicsComponent({
                mass: 1,
                entity: frag,
                type: Body.DYNAMIC,
                width: options.width,
                height: options.height,
                tag: 'frag',
                layer: PHYSICS_LAYER_TWO
            });
            frag.addComponent(pc);

            var timeUntilDestroy = 1000 + 500*Math.random();
            setTimeout(function() {
                frag.destroy();
            }, timeUntilDestroy);

            // var scale = 0.0001;
            pc.body.applyForce(options.f);

            frag.components.graphics.graphic.color = [1, 1, 0, 1];
        }

        var pickup = this.createWall(scene, options);

        pickup.components.graphics.graphic.color = [1, 1, 0, 1];
        pickup.components.physics.body.isSensor = true;
        pickup.components.physics.on('collision', function(otherBody, collisionVector) {
            if(otherBody.tag === 'player') {
                console.log('yo i am a pickup and i got picked up');

                var w = this.body.shape.width/2;
                var h = this.body.shape.height/2;

                var collisionDirection = new Vec2(collisionVector.x, collisionVector.y);
                collisionDirection.normalize();
                collisionDirection.x *= -10;
                collisionDirection.y *= -10;
                var scale = 0.001;

                spawnFrag(this.entity.scene, { width: w, height: h, xPos: this.entity.transform.position.x + w/2, yPos: this.entity.transform.position.y + h/2, f: new Vec2(scale * (1 + collisionDirection.x), scale * (-40 + collisionDirection.y)) });
                spawnFrag(this.entity.scene, { width: w, height: h, xPos: this.entity.transform.position.x + w/2, yPos: this.entity.transform.position.y - h/2, f: new Vec2(scale * (0.5 + collisionDirection.x), scale * (-30 + collisionDirection.y)) });
                spawnFrag(this.entity.scene, { width: w, height: h, xPos: this.entity.transform.position.x - w/2, yPos: this.entity.transform.position.y + h/2, f: new Vec2(scale * (-2 + collisionDirection.x), scale * (-40 + collisionDirection.y)) });
                spawnFrag(this.entity.scene, { width: w, height: h, xPos: this.entity.transform.position.x - w/2, yPos: this.entity.transform.position.y - h/2, f: new Vec2(scale * (-0.5 + collisionDirection.x), scale * (-30 + collisionDirection.y)) });

                this.entity.destroy();
            }
        });
                   

        return pickup;
    };

    PlatformGameFactory.createLevel = function(levelNumber) {
        if(!levelNumber) return;

        switch(levelNumber) {
            case 1:
                return this.createLevel1();

            case 2:
                return this.createLevel2();

            case 3:
                return this.createLevel3();
        }
    };

    PlatformGameFactory.createLevel1 = function() {

        /**
         * Create main scene
         */
        var scene = new Scene('main');

        // Create player
        var player = PlatformGameFactory.createPlayer(scene, {
            x: -200,
            y: -80,
            width: 50,
            height: 70,
            keys: {
                left: 'LEFT',
                right: 'RIGHT',
                jump: 'SPACE'
            }
        });

        // We want the camera to follow the player
        PlatformGameFactory.createCamera(scene, {
            follow: player
        });

        // Create a floor
        PlatformGameFactory.createWall(scene, {
            x: 0,
            y: -20,
            width: 2400,
            height: 20
        });

        // Create the little floor obstacle
        PlatformGameFactory.createWall(scene, {
            x: 30,
            y: -45,
            width: 30,
            height: 30
        });

        PlatformGameFactory.createPickup(scene, {
            x: 200,
            y: -180
        });

        // Create a goal
        PlatformGameFactory.createWall(scene, {
            x: 500,
            y: -60,
            width: 30,
            height: 30,
            tag: 'goal'
        });

        // TODO: Be able to set gravity!!!
        if(scene._physicsWorld) {
            console.log('set gravity');
            scene._physicsWorld.gravity = new Vec2(0, 0.005);
        }


        return scene;
    };

    PlatformGameFactory.createLevel2 = function() {

        /**
         * Create main scene
         */
        var scene = new Scene('main');

        // Create player
        var player = PlatformGameFactory.createPlayer(scene, {
            x: -200,
            y: -80,
            width: 50,
            height: 70,
            keys: {
                left: 'LEFT',
                right: 'RIGHT',
                jump: 'SPACE'
            }
        });

        // We want the camera to follow the player
        PlatformGameFactory.createCamera(scene, {
            follow: player
        });

        // Create a floor
        PlatformGameFactory.createWall(scene, {
            x: 0,
            y: -20,
            width: 2400,
            height: 20
        });

        // Create the little floor obstacle
        PlatformGameFactory.createWall(scene, {
            x: 30,
            y: -45,
            width: 30,
            height: 30
        });

        // Create the little floor obstacle
        PlatformGameFactory.createWall(scene, {
            x: 30,
            y: -85,
            width: 30,
            height: 30
        });

        // Create a goal
        PlatformGameFactory.createWall(scene, {
            x: 500,
            y: -60,
            width: 30,
            height: 30,
            tag: 'goal'
        });

        // TODO: Be able to set gravity!!!
        if(scene._physicsWorld) {
            console.log('set gravity');
            scene._physicsWorld.gravity = new Vec2(0, 0.005);
        }


        return scene;
    };

    PlatformGameFactory.createLevel3 = function() {

        /**
         * Create main scene
         */
        var scene = new Scene('main');

      
              
            
!
999px

Console