The Most Luxurious Blog

The latest thoughts, projects, and insight from the animals.
Combining Easel.js and Box2d in Canvas
One of the more difficult aspects of creating our Luxahoy HTML5 game was figuring out how to combine two very cool frameworks: Easel.js and Box2dWeb. In this tutorial we will go over the basics of "actor" objects so that you'll be able to translate box2d measurements to the x, y, and rotation parameters of your display object in easel.

Before starting this tutorial it is assumed that you have a basic knowledge of how box2d and easel both work. If you are new to the box2d world, please read through this awesome series of box2d orientation and tutorials from Seth Ladd. If you are new to easel, please check out the documentation so you can get an idea of how to add objects to the stage and understand the overall display hierarchy (hint: it shares several things in common with Flash coding).

Ok, now that that's out of the way, let's dig in. What we're going to create is create a simple demo that rains pink birds like so


View in browser - Download source code

Since we assume a basic understanding of box2d and easel we're going to skip the setup code and dive right in to combining the two frameworks. If you'd like a refresher on that simply download the demo zip or view the source on the demo page.

Now in Easel.js it's extremely simple to add an object to the stage at a specified position. Here's how we add birds to the screen from the ticker (loop) function

Note: all code below is non-functional excerpts from demo.js. For working code, download the zip.

1
2
3
4
5
6
7
var tick = function(dt, paused) {
	birdDelayCounter++;
	if(birdDelayCounter % 10 === 0) {  // delay so it doesn't spawn a bird on every frame
		birdDelayCounter = 0;
		birds.spawn();
	}
}
1
2
3
4
5
6
7
8
var spawn = function() {
	var birdBMP = new Bitmap("images/bird.png");
	birdBMP.x = Math.round(Math.random()*500);
	birdBMP.y = -30;
	birdBMP.regX = 25;   // important to set origin point to center of your bitmap
	birdBMP.regY = 25; 
	stage.addChild(birdBMP);
}
This should look pretty familiar. This code simply adds a bird to the stage in a random x position at the top of the canvas. The bitmap object itself is quite versatile, you can tween it's position, set it's alpha, skew it, etc. However, if you want to apply physics to your asset you'll have to send it to box2d.

regX and regY (origin point) are important when tying your easel object into box2d. This is because box2d objects have an origin point in the center as opposed to the top left like easel display objects.

Now that we have our images being added to the stage it's time to send 'em over to box2d for further awesomeness.

1
box2d.createBird(birdBMP);
1
2
3
4
5
6
7
8
9
10
11
12
13
var createBird = function(skin) {
	var birdFixture = new b2FixtureDef;
	birdFixture.density = 1;
	birdFixture.restitution = 0.6;
	birdFixture.shape = new b2CircleShape(24 / SCALE);   // half of bird.png width divided by world scale for right size
	var birdBodyDef = new b2BodyDef;
	birdBodyDef.type = b2Body.b2_dynamicBody;
	birdBodyDef.position.x = skin.x / SCALE;  // divide skin x and y by box2d scale to get right position
	birdBodyDef.position.y = skin.y / SCALE;
	var bird = world.CreateBody(birdBodyDef);
	bird.CreateFixture(birdFixture);
	bodies.push(bird);
}
The code above will create a box2d circle body at the same position and size as the bird bitmap object. Unfortunately though, the bird skin is still not mapped to the box2d object. Without debug on you would not see the box2d circle and the bird would remain stationary. So how do we tell the skin to follow the position of the box2d body?

...Actors! Actors are the meat and potatoes of having a visual box2d demo. Basically they have to run during the game loop and translate the box2d body metric positions back to pixel positions. Lets create one.

1
2
var actor = new actorObject(bird, skin);
bird.SetUserData(actor);  // set actor as userdata of body so we can get at it later if we need to
1
2
3
4
5
6
7
8
9
10
var actorObject = function(body, skin) {
	this.body = body;
	this.skin = skin;
	this.update = function() { // translate box2d positions to pixels
		this.skin.rotation = this.body.GetAngle() * (180 / Math.PI);
		this.skin.x = this.body.GetWorldCenter().x * SCALE;
		this.skin.y = this.body.GetWorldCenter().y * SCALE;
	}
	actors.push(this);
}
This is a basic example of an actor object, if you wanted to you could extend it with almost anything (ids, hit count, out of bounds function, etc). Now that an actor is defined for the box2d body we need to tell it to update during the physics loop.

1
2
3
4
// within physics loop before world.step
for(var i=0, l=actors.length; i<l; i++) {
     actors<i>.update();
}
The bitmap skin will now mirror whatever the box2d body does within the physics simulation.

If you want to remove the body AND it's skin together at some point you can add the body to an array to be removed. Bodies must be removed before each world.step.

1
2
3
4
5
6
7
8
9
10
11
// before step
for(var i=0, l=bodiesToRemove.length; i<l; i++) {
	removeActor(bodiesToRemove<i>.GetUserData());  // get the actor object in the user data of the body and send to removeActor function
	bodiesToRemove<i>.SetUserData(null);
	world.DestroyBody(bodiesToRemove<i>);
}
// after step
if(bodies.length > 30) {
     bodiesToRemove.push(bodies[0]);
     bodies.splice(0,1);
}
1
2
3
4
var removeActor = function(actor) {
	stage.removeChild(actor.skin);
	actors.splice(actors.indexOf(actor),1);
}
There you have it! There's a lot more powerful things you can do with an understanding of actors such as hit counts, box2d collision filter changes, out of bounds detection, etc. I hope to cover more in later tutorials.

View demo - Download source

If anyone has any optimization suggestions or other was to accomplish this please don't hesitate to leave comments.
1
Nice looking tutorial. One thing though: shouldn't all the <i>'s be 's ? (actors<i>, bodiesToRemove<i> etc)
by Arjen on Apr 17th 2012 at 3:00am
2
Hey Arjen, you're correct. For some reason my syntax highlighter converted them to <>. Strange
by Luxamillion on Apr 17th 2012 at 10:09am
3
Instead of setting the origin of easel object, you can also set the origin of box2D object by using `fixDef.shape.SetLocalPosition` or `fixDef.shape.SetAsOrientedBox`. It may be easier for people who is used to regular top-left coordinates.
by Son Tran on Apr 22nd 2012 at 10:29pm
4
independent frame rate? any suggestions for this case?
by Guile on Jan 9th 2013 at 9:43am
5
Thanks! this is really helpful, please make more!
by Danny on Jan 18th 2013 at 3:04am
6
I'm not sure what this means, but the zipped code wasn't running in Chrome although the demo on this page did work, so I diffed them. The one difference was that the line "Ticker.useRAF = true" was commented out in the working version. Making that change to the downloaded code got it working.

But chrome supports requestAnimationFrame so this should work, right? And from what I can figure out, it's something that I'd like to be using where it's available.
http://www.createjs.com/Docs/EaselJS/classes/Ticker.html#property_useRAF

Thanks for putting up this example, it's helping me figure things out, slowly!

by Dodoy on Feb 15th 2013 at 8:25pm
7
Thanks for reporting this Dodoy, yes chrome should be able to use RAF. Also, it's probably important to note that when I posted this example CreateJS was still in infancy - a lot of things have probably changed with it.
by Luxamillion on Feb 15th 2013 at 8:38pm
Leave Us A Comment


Spread The Love
Copied!
About
Luxurious Animals is a creative digital agency that has established itself as architects of innovative, elegant marketing media. Our work inspires, engages, and transforms audiences into passionate customers.
RSS
Categories
Archives
Contact
435 West 19th Street
New York, NY 10011
212.518.1920
info@luxanimals.com
Connect