Final result of Box2D Chapter 1

I mention in my last post that Todd Kerpelman created a set of Box2D video tutorials (http://www.kerp.net/box2d/), I just finished up the first chapter and here is what I came up with…

As you can see it isn’t a whole lot, just a bunch of boxes falling down, but damn does it look good :) .

Here is the source code for the project

package
{
	import Box2D.Collision.b2AABB;
	import Box2D.Collision.Shapes.b2PolygonDef;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2DebugDraw;
	import Box2D.Dynamics.b2World;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;
	import fl.controls.Button;
	import flash.events.MouseEvent;

	/**
	 * Final result of Todd Kerpelman's chapter 1 box2d video tutorial
	 * (http://www.kerp.net/box2d/).
	 * The code here is pretty much line for line from Kerpelman's tutorial
	 * I added a small modification so the simulation may be restarted when
	 * the user clicks the restart button in the top left of the screen.
	 * @author Edgar Miranda
	 */
	public class HelloBoxWorld extends MovieClip
	{

		protected var _world:b2World;

		// Ratio of pixels to meters
		public static const RATIO:Number = 40;

		// Number of frames until we launch another crate
		private var _nextCrateIn: int;

		// Button to restart simulation
		public var restartButton: Button;

		public function HelloBoxWorld()
		{
			// 1. Set up the world
			setupWorld();
			// 2. Creating walls and floor
			createWallsAndFloor();

			setupDebugDraw();

			_nextCrateIn = 0;

			addEventListener(Event.ENTER_FRAME, newFrameEvent);

			// Put the restart button on the top most index
			// so it isn't covered by the falling crates
			addChild(restartButton);

			// Event listener in order to restart
			// simulation when the restart button is pressed
			restartButton.addEventListener(MouseEvent.CLICK, onRestartButtonClick)
		}

		private function onRestartButtonClick(e: MouseEvent): void {

			// Remember that the world counts as one body so you
			// want to remove bodies until there is only one body left
			while (_world.GetBodyCount() > 1) {
				_world.DestroyBody(_world.GetBodyList());
			}

			// recreate the walls again
			createWallsAndFloor();

			// set to default value
			_nextCrateIn = 0;
		}

		private function setupDebugDraw():void
		{
			var spriteToDrawOn:Sprite = new Sprite();
			addChild(spriteToDrawOn);

			var artistForHire:b2DebugDraw = new b2DebugDraw();
			artistForHire.m_sprite = spriteToDrawOn;
			artistForHire.m_drawScale = RATIO;
			artistForHire.SetFlags(b2DebugDraw.e_shapeBit);
			artistForHire.m_lineThickness = 2.0;
			artistForHire.m_fillAlpha = 0.6;

			_world.SetDebugDraw(artistForHire);
		}

		private function newFrameEvent(e:Event):void
		{
			_world.Step( 1.0 / 30, 10);

			// Every few frames, until a certain numer of crates have been added
			if (_nextCrateIn-- <= 0 && _world.m_bodyCount < 80) {

				// add a random create to the world
				addARandomCrate();
				_nextCrateIn = 1;
			}

			//trace("Falling crate is at", _fallingCrate.GetPosition().x, ",", _fallingCrate.GetPosition().y);
		}

		private function addARandomCrate():void
		{
			// Add a falling block to the world.
			var fallingCrateDef:b2PolygonDef = new b2PolygonDef();
			fallingCrateDef.SetAsBox(randomInt(5, 40) / RATIO, randomInt(5, 40) / RATIO);
			fallingCrateDef.friction = 0.8;
			fallingCrateDef.restitution = 0.3;
			fallingCrateDef.density = 0.7;

			// Set it to, 250, -30
			var fallingBodyDef:b2BodyDef = new b2BodyDef();
			fallingBodyDef.position.Set(randomInt(15, 530) / RATIO, randomInt(-100, -10) / RATIO);
			fallingBodyDef.angle = randomInt(0,360) * Math.PI / 180;

			var fallingCrate:b2Body = _world.CreateBody(fallingBodyDef);
			fallingCrate.CreateShape(fallingCrateDef);
			fallingCrate.SetMassFromShapes();

		}

		// Return a random number between lowVal and highVal, INCLUSIVE
		private function randomInt(lowVal:int, highVal:int): int
		{
			if (lowVal <= highVal) {
				return (lowVal + Math.floor(Math.random() * (highVal - lowVal + 1)));
			}else
			{
				throw(new Error("OMG! Wrong values passed to randomInt!!"));
			}
		}

		private function createWallsAndFloor():void
		{
			// Create the shape (polygon) definition
			var bigLongShapeDef: b2PolygonDef = new b2PolygonDef();
			bigLongShapeDef.vertexCount = 4;
			b2Vec2(bigLongShapeDef.vertices[0]).Set(0 / RATIO, 0 / RATIO);
			b2Vec2(bigLongShapeDef.vertices[1]).Set(550 / RATIO, 0 / RATIO);
			b2Vec2(bigLongShapeDef.vertices[2]).Set(550 / RATIO, 10 / RATIO);
			b2Vec2(bigLongShapeDef.vertices[3]).Set(0 / RATIO, 10 / RATIO);
			bigLongShapeDef.friction = 0.5;
			bigLongShapeDef.restitution = 0.3;
			bigLongShapeDef.density = 0.0;

			// Create the body definition
			var floorBodyDef:b2BodyDef = new b2BodyDef();
			floorBodyDef.position.Set(0 / RATIO, 390 / RATIO);

			// Create the body
			var floorBody:b2Body = _world.CreateBody(floorBodyDef);

			// Create the shape
			floorBody.CreateShape(bigLongShapeDef);
			floorBody.SetMassFromShapes();

			var tallSkinnyShape:b2PolygonDef = new b2PolygonDef();
			tallSkinnyShape.SetAsBox( 5 / RATIO, 195 / RATIO);
			tallSkinnyShape.friction = .5;
			tallSkinnyShape.restitution = 0.3;
			tallSkinnyShape.density = 0.0;

			var wallBodyDef:b2BodyDef = new b2BodyDef();
			wallBodyDef.position.Set(5 / RATIO, 195 / RATIO);

			var leftWall:b2Body = _world.CreateBody(wallBodyDef);
			leftWall.CreateShape(tallSkinnyShape);
			leftWall.SetMassFromShapes();

			wallBodyDef.position.Set(545 / RATIO, 195 / RATIO);
			var rightWall:b2Body = _world.CreateBody(wallBodyDef);
			rightWall.CreateShape(tallSkinnyShape);
			rightWall.SetMassFromShapes();

			//trace("My world now has", _world.m_bodyCount, "bodies in it! Wow!");
		}

		private function setupWorld():void
		{
			// 1. Set up the size of the universe
			var universeSize:b2AABB = new b2AABB();
			universeSize.lowerBound.Set( -3000 / RATIO, -3000 / RATIO);
			universeSize.upperBound.Set( 3000 / RATIO, 3000 / RATIO);			

			// 2. Define gravity
			var gravity: b2Vec2 = new b2Vec2(0, 9.8);

			// 3. Ignore sleeping objects
			var ignoreSleeping:Boolean = true;

			_world = new b2World(universeSize, gravity, ignoreSleeping);

			//trace("My world has", _world.GetBodyCount(), "bodies in it!");
		}

	}

}

You can get the Zip file containing all the project resources (source code, FLA, FlashDevelop project file) by clicking the link below.

Box2dChapter1_EdgarMirandadotnet.zip

My next goal is to finish up chapter 2, which is a bit longer then chapter 1 (50 lessons instead of 22). I’m currently on chapter 2 lesson 4, and I’m aiming to do around 5-10 lessons a day. I find it it’s easier to learn if I do about an hour or so of lessons each day instead of doing the whole batch in a day or two, there is also so much I can take of just watching video tutorials all day :) .

Look out for my final result for chapter 2!

This post was written by Edgar Miranda.

Currently a student at San Jose State University residing in the San Francisco Bay Area. When I'm not working on my next Flash game I enjoy reading up on new Flash technologies, browsing the web for interesting stories, learning a new song on the guitar, and occasionally playing some classic DotA on Warcraft 3.

More Posts by Edgar   Visit Edgar's Website

Liked this? Share it!

Subscribe to RSS feed Tweet this! StumbleUpon Reddit Digg This! Bookmark on Delicious Share on Facebook

Leave a Reply