Here I go with my first tutorial! I have thought about writing tutorials along time ago, But my English language wasn't as that good, But it got a lot better over the last few months so I hope this tutorial would be helpful for you.
In this series of tutorial we will be creating a basic box2d game then we will extend that to create a very organized OO box2d project using the help of design patterns! But we will leave that for another series.
The final result: Click any where on the stage, as the mouse go far away from the box, the applied impulse increases.
Steps :
In this series of tutorial we will be creating a basic box2d game then we will extend that to create a very organized OO box2d project using the help of design patterns! But we will leave that for another series.
The final result: Click any where on the stage, as the mouse go far away from the box, the applied impulse increases.
Steps :
- Download and setup box2d physics engine for as3
- Understanding some box2d basics
- Creating the world
- Creating the player
- Make things come alive! ( the fun part )
- Creating walls
- Adding mouse listener and applying Impulse to the player
- The conclusion
Download and setup box2d physics engine for as3
Create a new folder and put your flash file in. Click here and download the latest box2d version that is compatible with your flash version ( I am using box2d v2.1a ), Then unzip the box2d compressed file anywhere and navigate to "Box2DFlashAS3 2.1a\Source", you should find a Box2D folder in there , copy that and into your flash project folder paste it, my flash project folder looks like this :-
That's it!, now let's make sure everything is working. Open your fla file and from properties write a name for the class, I usually call it Main ( some developers called it with the name of the project ), then create your main class file "Main.as", and write this basic code in the Main.as file
Run your flash file and your should see "Working..." in the output window.
package { public class Main extends MovieClip { public function Main() { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } public function init(e:Event = null):void { trace("Working..."); } } }
Run your flash file and your should see "Working..." in the output window.
Understanding some box2d basics
Before we get into the code, we need first to understand how box2D works?, box2D is a physics engine that calculates all the positions and rotations for the objects you create through it.
e.g. if we needed to create a car with mass = 1000 kg that moves with a speed = 20 m/s, we will first need to create the world where our car should be, set its gravity and update the world every frame, Then we will create the body of the car, give it some physical properties and create a ground also give it some physical properties then we would apply an impulse on the car and it should move with a constant speed ( assuming no resistance ).
Calculating the Impulse :- Impulse = Mass * Velocity ( see wikipedia for more information ).
Impulse is a vector quantity ( which means it has x and y values ), The mass of the car is 1000 kg and we need the velocity to be equal to 20 m/s, we would need to apply an impulse = ( 20000 , 0 ), in another words 20000 in x direction and 0 in y direction.
How is that ?! If your stage size was 800 x 600 pixels, Box2D will assume each pixel = 1 meter.
Okay what is wrong with that ? the answer is simple, how tall are you? I am 1.7 meters so I would fill 1.7 pixels on the screen and that is just a dot! That is not good right..
There is a very simple approach to solve this. It is to work with pixels not meters and divide by a constant to convert to meters ( usually the constant equals 30 which means 30 pixels per meter ).
" Box2D doesn't display any graphics for you "
Yes it contains a debugdraw class that can be used to draw all the box2d objects you created, but this is usually used for testing, because in the real life you would need real graphics ( Sprites and MovieClips ) to represent objects in your game.
How to solve this? We will somehow make box2d objects contain reference for the sprites that act as the displays for this box2d objects. You have all the rights to not understand that at this level, but once we get into the code everything will be clear.
" Box2D object registration point is located at the center "
You should be familiar with registration points in flash, e.g. if we were to create a box in box2d and give it a start position of (200 ,300), it will be placed like this
Why is this a problem? It's not but we have to make sure that all our sprites have the registration point at the center ( more about that later ).
Also you should know that all box2D classes start with 'b2', e.g. b2Body, b2World, b2Shape, ... etc
We can now simplify the steps to Implement our game
e.g. if we needed to create a car with mass = 1000 kg that moves with a speed = 20 m/s, we will first need to create the world where our car should be, set its gravity and update the world every frame, Then we will create the body of the car, give it some physical properties and create a ground also give it some physical properties then we would apply an impulse on the car and it should move with a constant speed ( assuming no resistance ).
Calculating the Impulse :- Impulse = Mass * Velocity ( see wikipedia for more information ).
Impulse is a vector quantity ( which means it has x and y values ), The mass of the car is 1000 kg and we need the velocity to be equal to 20 m/s, we would need to apply an impulse = ( 20000 , 0 ), in another words 20000 in x direction and 0 in y direction.
Things to know About box2D
" Box2D works with meters not pixels "How is that ?! If your stage size was 800 x 600 pixels, Box2D will assume each pixel = 1 meter.
Okay what is wrong with that ? the answer is simple, how tall are you? I am 1.7 meters so I would fill 1.7 pixels on the screen and that is just a dot! That is not good right..
There is a very simple approach to solve this. It is to work with pixels not meters and divide by a constant to convert to meters ( usually the constant equals 30 which means 30 pixels per meter ).
" Box2D doesn't display any graphics for you "
Yes it contains a debugdraw class that can be used to draw all the box2d objects you created, but this is usually used for testing, because in the real life you would need real graphics ( Sprites and MovieClips ) to represent objects in your game.
How to solve this? We will somehow make box2d objects contain reference for the sprites that act as the displays for this box2d objects. You have all the rights to not understand that at this level, but once we get into the code everything will be clear.
" Box2D object registration point is located at the center "
You should be familiar with registration points in flash, e.g. if we were to create a box in box2d and give it a start position of (200 ,300), it will be placed like this
Why is this a problem? It's not but we have to make sure that all our sprites have the registration point at the center ( more about that later ).
Also you should know that all box2D classes start with 'b2', e.g. b2Body, b2World, b2Shape, ... etc
We can now simplify the steps to Implement our game
- Creating the world
- Creating the player ( car )
- Creating the ground
- Creating walls and obstacles.
- Update the world ( Make things come alive )
- Applying impulse on the player when mouse is clicked
Creating the world
To create the world we have to define a new object from the b2World class, and let's define it as a class variable to be able to access it anywhere in the project, also let's make a class constant PM which will be used to convert pixels to meters
We will be creating each step in a separate method, so create a method call it createWorld()
Box2D has a class to represent vectors called b2Vec2.
public static var world:b2World; public static const PM:uint = 30;
We will be creating each step in a separate method, so create a method call it createWorld()
public function createWorld():void { var gravity:b2Vec2 = new b2Vec2(0, 9.8); var sleep:Boolean = true; world = new b2World(gravity, sleep); }
Box2D has a class to represent vectors called b2Vec2.
The b2World constructor needs two parameters :
- gravity : the force that will act on all the box2d objects, forces are represented by vectors, in our example we created a normal gravity vector with a y direction equals to 9.8.
- sleep : when set to true the world will not simulate inactive bodies, so by setting it to true you increase the performance.
Creating the player ( car )
That's actually the messy part....
When creating physical objects in box2D we have to go through 5 steps :
When creating physical objects in box2D we have to go through 5 steps :
- Create body definition
- Create body from world using body definition
- Create shape
- Create fixture definition
- Pass the fixture definition to the CreateFixture method in the body object
As we have said before we will create each step in a separate method, so make a method that takes 4 arguments ( x, y, width, height ) and call it createPlayer which returns the b2body to be used later.
As you can see we first created bodyDef where we set the type ( dynamic ) and position ( remember to divide by the PM to convert pixels to meters ).
Then we created a body from the world using bodyDef.
Then we created a shape of type polygonShape, box2D has 3 types of shapes ( edge, circle and polygon ), In this tutorial we will use the polygon shape, b2PolygonShape has a method to easily create boxes called SetAsBox which takes two parameters ( half width and half height ), also don't forget to convert pixels to meters.
The fourth step which is creating the fixtureDef, it takes the shape and sets some physical properties, lets talk about each property we have set:
Don't forget to return the body for us to have full control over this object later.
That is it? No, we haven't created the sprite that will act as the display yet, so let's do that now, In the createPlayer method add this lines before creating the bodyDef
That is a very basic way of drawing a rectangle in a sprite, Note that we have set the registration point to the center by drawing around the center ( - width / 2 and - height / 2).
Okay we now created our box2d object and the display sprite, how to make a reference to the sprite in the box2d object? Fairly easy, just add this line after creating bodyDef object
public function createPlayer(_x:Number , _y:Number, _width:Number, _height:Number):b2Body { // Create body definition var bodyDef:b2BodyDef = new b2BodyDef(); bodyDef.type = b2Body.b2_dynamicBody; bodyDef.position.Set(_x / PM, _y / PM); // Create body from world using bodyDef var body:b2Body = world.CreateBody(bodyDef); // Create shape var shape:b2PolygonShape = new b2PolygonShape(); shape.SetAsBox(_width / 2 / PM, _height / 2 / PM); // Create fixtureDef giving shape var fixtureDef:b2FixtureDef = new b2FixtureDef(); fixtureDef.shape = shape; fixtureDef.restitution = 0.2; fixtureDef.friction = 1; fixtureDef.density = 0.5; // Pass the fixtureDef to the createFixture method in the body object body.CreateFixture(fixtureDef); return body; }
As you can see we first created bodyDef where we set the type ( dynamic ) and position ( remember to divide by the PM to convert pixels to meters ).
Then we created a body from the world using bodyDef.
Then we created a shape of type polygonShape, box2D has 3 types of shapes ( edge, circle and polygon ), In this tutorial we will use the polygon shape, b2PolygonShape has a method to easily create boxes called SetAsBox which takes two parameters ( half width and half height ), also don't forget to convert pixels to meters.
The fourth step which is creating the fixtureDef, it takes the shape and sets some physical properties, lets talk about each property we have set:
- Restitution : this video shows you the effect of increasing the restitution of bodies, for more information about the coefficient of restitution watch this video.
- Friction : by increasing this you increase the resistance between this object and any other object colliding with and therefore decreasing the time needed for this object to come to rest.
- Density : as you may know that mass = density * Volume, box2d compute the mass of an object from its density and its area multiplied by 4, Why 4? I don't know, maybe because it's convenient as the depth of the object. For our example here, the mass of this car will be equal to ( _height * _width * density * 4 ).
Don't forget to return the body for us to have full control over this object later.
That is it? No, we haven't created the sprite that will act as the display yet, so let's do that now, In the createPlayer method add this lines before creating the bodyDef
// Create car sprite var carSprite:Sprite = new Sprite(); carSprite.graphics.beginFill(0xafafaf, 1); carSprite.graphics.drawRect( -_width / 2, -_height / 2 , _width, _height); carSprite.graphics.endFill(); carSprite.x = _x; carSprite.y = _y; addChild(carSprite);
That is a very basic way of drawing a rectangle in a sprite, Note that we have set the registration point to the center by drawing around the center ( - width / 2 and - height / 2).
Okay we now created our box2d object and the display sprite, how to make a reference to the sprite in the box2d object? Fairly easy, just add this line after creating bodyDef object
bodyDef.userData = carSprite;
Part 1: All come together
Nothing Impressive has happened yet, just a box hanging there on the air , but as you will see in the first section of the second part where we make everything come alive ...
Part1 final code :-
public class Main extends MovieClip { public static var world:b2World; public static const PM:uint = 30; private var player:b2Body; public function Main() { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } public function init(e:Event = null):void { createWorld(); player = createPlayer(40, 50, 50, 10); } public function createWorld():void { var gravity:b2Vec2 = new b2Vec2(0, 9.8); var sleep:Boolean = true; world = new b2World(gravity, sleep); } public function createPlayer(_x:Number , _y:Number, _width:Number, _height:Number):b2Body { // Create car sprite var carSprite:Sprite = new Sprite(); carSprite.graphics.beginFill(0xafafaf, 1); carSprite.graphics.drawRect( -_width / 2, -_height / 2 , _width, _height); carSprite.graphics.endFill(); carSprite.x = _x; carSprite.y = _y; addChild(carSprite); // Create body definition var bodyDef:b2BodyDef = new b2BodyDef(); bodyDef.userData = carSprite; bodyDef.type = b2Body.b2_dynamicBody; bodyDef.position.Set(_x / PM, _y / PM); // Create body from world using bodyDef var body:b2Body = world.CreateBody(bodyDef); // Create shape var shape:b2PolygonShape = new b2PolygonShape(); shape.SetAsBox(_width / 2 / PM, _height / 2 / PM); // Create fixtureDef giving shape var fixtureDef:b2FixtureDef = new b2FixtureDef(); fixtureDef.shape = shape; fixtureDef.restitution = 0.2; fixtureDef.friction = 1; fixtureDef.density = 0.5; // Pass the fixtureDef to the createFixture method in the body object body.CreateFixture(fixtureDef); return body; } }
Thank you so much very useful post for begginer. I really appreciate that. Keep going please ! :)
ReplyDeleteIf i've put a movieclip in the stage, what's code that could work for it?
ReplyDelete