Você está na página 1de 28

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

ActionScript.org Flash, Flex and ActionScript Resources - http://www.actionscript.org/resources Getting Started with Box2D http://www.actionscript.org/resources/articles/1087/1/Getting-Started-with-Box2D/Page1.html By Jean Andr Mas Jean Andr Mas Published on May 5, 2011 Graphic designer A complete article about Box2D features converted since 2004 to coding. Box2D overview I play around You can download the source files here. with C++, OpenGL, Java, Javascript, AND Actionscript. Box2D is a C++ 2D physical world simulation created by Erin Catto and My website: ASWC ported to many other languages. The Actionscript 3 version we will be using in this tutorial is adapted from the original Box2D by BorisTheBrave and can be found here. We will be using the 2.1a version which is the latest one at this date. What is Box2D? Box2D simulates a virtual 2D world by using complex Mathematical formulas and applying the results to virtual objects. Thats right, everything is virtual in box2D. Box2D is not a DisplayObject that you display on the screen. Box2D does not display graphics on the screen either. In fact Box2D has no direct relation with whatever you will display on the screen. How do I use Box2D? To use Box2D with your next game or application, you create a Box2D world, create some Box2D bodies/objects then use the values (position, rotation) from these bodies/objects to position and rotate your graphics in your application. So one part of your application will be managing Box2D, another part of your application will be using Box2D values to actually display and position graphics on the screen. How easy is it to work with Box2D? Lets just say that beginners will have a real hard time working with Box2D: . Box2D documentation is not really developed. Documentation doesnt come with examples. Most methods/properties have no description of what they are supposed to be doing. . Box2D doesnt throw errors. In the best cases if you do a mistake working with Box2D, what you are trying to do wont work. In the worst cases it will crash Flash altogether. . Box2D doesnt dispatch events. Box2D uses a system of class callbacks. Is it up to you to wrap that up into a class dispatching events. . Box2D framework and used patterns are a bit confusing. Getting or setting one value in Box2D can be made via public member variables, getters/setters or even methods. You better check the documentation regularly to know which one to use. . Box2D doesnt use pixels but meters as measure unit. If you create a 100100 object in Box2D you are creating a 100 meter x 100 meter object. Pretty much the size of a building. Box2D is slower and less accurate with bigger object so you will need to scale down all the value you pass to Box2D and then scale them up when you retrieve them. We usually handle

1 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

this with a public static const scale value. . Box2D uses radians exclusively for angle measurement. Flash doesnt use radians for rotating objects so here again to set an angle in Box2D you will need to convert to radian first and when retrieving angles you will need to convert back to degrees. Also to create any kind of object in Box2D you often have to write quite a few lines of code. This process can be very repetitive and not quite suited for efficient development. That is why a good OOP knowledge will help you on wrapping up most repetitive code into classes/static methods. Now that we know more about Box2D, it is time to write some code. Lets start by creating a functional Box2D world. Creating a Box2D World This article is meant for advanced and expert coder so I will not show how to import classes or write functions or classes. You are assumed to know all this already. We will be working with a simple document class for this tutorial. Assuming you downloaded the latest Box2D (2.1a), lets get started. Creating our world:
[as]world = new b2World(new b2Vec2(0, 10), true);[/as]

The b2Vec2 vector is setting the gravity system. You can of course experiment with different gravity system. Now we need to update our world on a constant basis so lets just use a Timer for that. I set the timer to 33ms which is close to 30fps.
[as]private function updateWorld(e:TimerEvent):void { world.Step(1 / 30, 10, 10); world.ClearForces(); }[/as]

And here we go. We do have a Box2D world working properly. Pretty easy right? Note the step method, first value is the time step, that is how much time has passed in between each step. Second value is the velocity iteration, and third is the position iteration. Box2D loops roughly through all objects and calculate their position and velocity. Those two iteration values determine how many time box2D will perform those calculation. The more iteration, the more accurate will be the results but also of course the more CPU Box2D will be using. Yes, that means with lower values box2D might miss some collision or position incorrectly some objects. You should call ClearForces() after each time step as required by Box2D. Of course its not that exciting since theres nothing to see but our 2D world simulation is actually already working. To prove it heres a little demo, just click anywhere and see the Box2D circle falling.

2 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

Experiment with different gravity values, different time steps to get a good understanding of those. What you are actually seeing here is the box2D debug mode which is the next subject . Working in Debug Mode with Box2D Even tough Box2D doesnt display anything on the screen it has a debug mode that you can use to see what Box2D is actually doing. Very useful of course. Now lets see how to create a debug mode. Setting the debug mode:
[as]var debug_draw:b2DebugDraw = new b2DebugDraw(); var debug_sprite:Sprite = new Sprite(); addChild(debug_sprite); debug_draw.SetSprite(debug_sprite); debug_draw.SetFillAlpha(0.3); debug_draw.SetDrawScale(SCALE); debug_draw.SetFlags(b2DebugDraw.e_shapeBit); world.SetDebugDraw(debug_draw);[/as]

You need to pass a Sprite to the b2DebugDraw object and of course add this Sprite on the screen so you can see the Box2D objects. You can debug at a different scale but usually you pass the same scale value. Note that all the examples in this article will be referencing a public static const called SCALE and set to a value of 30. Also the setFlags() method allows you to see different box2D features. You can set alpha for the fill and the lines. Feel free to experiment. Finally lets update our debug mode as well:
[as]private function updateWorld(e:TimerEvent):void

3 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

{ world.Step(1 / 30, 10, 10); world.DrawDebugData(); world.ClearForces(); }[/as]

Thats it, now everything we create in Box2D will be shown in our debug Sprite. Here again that was pretty easy. Now to see something we of course need to create something so lets see that now. Creating a Box2D Body In the little demo earlier you saw a few rectangles and circles. Rectangles were not moving (static) and the circles were moving (dynamic). Those object in box2D are of type b2Body. A b2Body is created by the b2World not by you directly but by using the b2World method CreateBody(). A b2Body is made up of different type of b2Shape and b2Fixture. Shapes create the shape of the body while fixture are made from the shape and used in collision detection. A body can be made of one or many shapes. Creating a body can require quite a few lines of code so thats where you want to reduce this process by creating static method or functions. Anyway lets see how to create a simple rectangle body first: We start as often in Box2D with a definition class which simply provides a bunch of default values:
[as]var my_body:b2BodyDef = new b2BodyDef(); my_body.position.Set(100 / SCALE, 200 / SCALE); my_body.angle = 15 * math.PI / 180 ; my_body.type = b2Body.b2_dynamicBody;[/as]

we set the position of our body with the Box2D equivalent of 100 pixel on the x axis and 200 pixel on the y axis. We then give an angle in radians of course. Finally we set the type of our body. This can be static (not moving), dynamic (respond to forces) and kinematic (special type of body with unique velocity and no response to forces). Note that you can change a b2Body type at any time. You could create a static b2Body and change it to dynamic later on by calling mybody.SetType() An important concept to not miss here is the body coordinates that we set explicitly. You will later be able to get the coordinates of that body by using the method mybody.GetPosition(). This will give you the coordinate of that body regardless of its shape and mass while calling mybody.GetWorldCenter() will give you the coordinate of the center of mass which might not be the same.
[as]var my_box:b2PolygonShape = new b2PolygonShape(); my_box.SetAsBox(100 / 2 / SCALE, 100 / 2 / SCALE);[/as]

There are 3 type of shape you can use to build your bodies. The b2PolygonShape allows you to create easily rectangles and complex polygon using an array of vertices, the b2CircleShape creates circles, and the b2EdgeShape creates lines type of object. This last shape type is used less often and has some limitation concerning its interaction with other objects. Use it partially.
[as]var my_fixture:b2FixtureDef = new b2FixtureDef(); my_fixture.density = 1; my_fixture.friction = 1; my_fixture.restitution = 0.5; my_fixture.shape=my_box;[/as]

We create the fixture and pass the shape we defined and applied a few values for mass, friction and restitution. The mass value is used for computing the mass of the shape (a value of 0 creates a static body!). The friction value is used in calculating the friction between two objects, and the restitution value is used for computing the bounciness of objects.

4 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

Finally:
[as]var world_body:b2Body=world.CreateBody(my_body); world_body.CreateFixture(my_fixture);[/as]

We create a b2body by passing the body definition in the CreateBody method of the b2World class. Then we create the fixture by using the b2body method CreateFixture and passing our fixture in it. Quite a complex operation you would say to create a little rectangle. Well to be honest this is actually the short version. We could have also passed to our fixture a b2FilterData object (used to define groups of collision). Now we only created a body with one fixture which we could call a simple body. We could as well create a body with multiple fixture which we could then call a complex body. Lets see this in action (click anywhere):

You can at any time add new fixtures to a body or remove them. Now to display real graphics you simply need to keep a reference to the body you create and use their values to update your graphic position. For this simple tutorial Im just going to store my references in a Dictionary. Note that b2Body instances have a SetUserData() method where you can store any type of objects. You can use that to store a reference to your graphic. There are hundreds of way to link a b2Body reference to the graphic it represents so Ill let you find your preferred way. I personally use specific set of classes for this task. Click anywhere.

5 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

now its time to see how Box2D handles collision and more important how we can find out when two bodies collide. Handling a Box2D Collision Collision detection is done by extending the b2ContactListener class, overriding all its methods and passing it to the SetContactListener() method of the b2World. This is just a system of class callback so dont expect the dispatch of an event here. Up to you to implement the IEventDispatcher interface and have your callback class dispatch events. One important thing to notice here is that the BeginContact() method is called only once when the contact begins and the EndContact() method is called obviously when the contact stop colliding. To resume, youll know when two contact start touching and when they stop touching but youll have to assume what happens in between. For example if you want to know if two contacts are still touching you need to check if a EndContact() has been called for them. If not then you can assume they are still touching. Lets get started by creating our class. We simply extend the b2ContactListener class and override all its methods. Lets call our class ContactCallback. Then we need to pass it to our world:
[as]world.SetContactListener(new ContactCallback())[/as]

And voila, its already working. If you put some traces in each ContactCallback methods youll see that they are all called each time a collision happens. The BeginContact() method and the EndContact() method are giving us a b2Contact object. With this object we can have access to two fixtures, the two fixtures that collided. Remember that a b2Body is made up with fixtures, one or many. So instead of telling you which b2Body collided, the b2Contact tells you which fixture collided. To know which b2Body was involved in the collision you can retrieve a reference to the fixture:
12/23/2011 11:43 AM

6 of 28

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

[as]var firstfixture:b2Fixture = contact.GetFixtureA(); var firstbody:b2Body = firstfixture.GetBody();[/as]

That simple. You need of course to keep a reference to your b2Body and why not to your fixtures as well in order to take appropriate action. A b2Contact has also a GetFixtureB() method so you can reference the second fixture. Lets put this in action with two simple examples. Lets trigger a sound when a basketball touches the floor. Basically I keep a reference of the ground body and I create a ContactCallback instance for which I keep a reference:
[AS]private var contacttarget:b2Body; private var contactcallback:ContactCallback;[/AS]

I modify a bit that class to accept a target public variable and a callback function to which I will pass the b2Body that collided with the ground. (I dont use callbacks usually but I use it here to quickly illustrate the implementation) In that callback function I do:
[as]if (bodyreferences[body] is Ball) { sound.play(); }[/as]

So simple.

Now lets destroy all crosses when they touch the ground. That means get rid of the graphic but also get rid of the b2Body of course which is not as easy as you might think. You actually cannot destroy a b2Body from within a BeginContact() or EndContact(). Instead you need to qualify those b2Body for

7 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

destruction and destroy them after the world step. I just put them in an array here: Here is what happens in our callback function:
[as]var mc:MovieClip = bodyreferences[body] as MovieClip; if (mc.numChildren > 0) { if (mc.getChildAt(0) is Wood || mc.getChildAt(0) is Tube) { destroyedbody.push(body); mc.visible = false; } }[/as]

Now after the world step I destroy them safely:


[as]world.Step(1 / 30, 10, 10); world.ClearForces(); while (destroyedbody.length > 0) { var bodytodestroy:b2Body = destroyedbody.shift(); if (bodytodestroy) { world.DestroyBody(bodytodestroy); } }[/as]

note that theres also a b2DestructionListener that you can implement the same way. This is used to deal properly with the remaining objects after destroying a b2Body. Now you probably know how to implement that callback class. Now its time to check controllers and see what they can do and how to use

8 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

them. Using a Box2D Controller Box2D lets you easily apply a wide range of force, gravity, impulse to a b2body. Its actually very simple as you can see in this following example. Simply click on any object to make it jump:

The code for this is actually very straight forward. I simply get a reference of the b2Body referenced by the graphic I click on and then in my click event handler:
[as]var body:b2Body = graphicreferences[e.currentTarget]; if (body) { body.ApplyImpulse(new b2Vec2(0 , - 7), body.GetWorldCenter()); }[/as]

and this is really it. Controllers let you apply a wide range of different forces too but on as many b2Body as you want. Lets say you create a platform game and on one level there is a area where the gravity is different, or why not an area filled with water or again an area very windy. Instead of trying to apply different values to all b2Body, you simply register them with a controller and let the magic happen. We wont try all Controller here but only show a couple just so we know how to use them. A popular and spectacular one if the b2BuoyancyController which lets you simulate fluids. Lets try this. As usual click first to see the objects start falling. In the center, theres a simple graphic representing a sink full of water. If the falling objects enter in that area, they are added to the b2BuoyancyController which simulates a

9 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

fluid. For that purpose I set the basketball to be a bit less dense while the little crate were set to be heavier. That way you can really see the difference between the two when they enter the fluid area. If a graphic floating in the sink leaves the sink area for some reason, its referenced b2Body is removed from the controller. Go ahead and try it.

Some relevant code:


[as]fuildcontroller = new b2BuoyancyController(); fuildcontroller.normal.Set(0,-1); fuildcontroller.offset = - 280 / SCALE; fuildcontroller.density = 3; fuildcontroller.angularDrag = 2; fuildcontroller.linearDrag = 5;[/as]

We set the normal (which is a b2Vec2) to opposite on the y axis. We then set an offset to position the start of that fluid (280 pixels from the top). We also set a density for the fluid, the higher the value the thicker will be the fluid simulated. Water should probably be 1 or 2 but here I set it to 3. The angularDrag and linearDrag value influences the reaction and movements of the objects so feel free to experiment with them. Finally we set our Controller:
[as]world.AddController(fuildcontroller);[/as]

And thats it. Now all you have to do is add b2Body instances to it simply like that:
[as]fuildcontroller.AddBody(mybody);[/as]

And of course you can remove them as easily:


[as]fuildcontroller.RemoveBody(mybody);[/as]

10 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

Important: Do not add the same b2Body twice! Weird results will occur. Now for our last example we will look into the b2ConstantAccelController. This simple Controller applies an acceleration to your b2Body (via a b2Vec2). This is the perfect Controller for simulating wind. Lets see it in action. When objects pass through the orange stripe, they are added to the b2ConstantAccelController and of course once they are out of that zone they are removed. To really show the effect I set a very strong wind. Click anywhere:

Relevant code:
[as]windcontroler = new b2ConstantAccelController(); windcontroler.A = new b2Vec2(15, 0); world.AddController(windcontroler);[/as]

Setting a wind in Box2D with 3 lines of code. That cant get better than that. Now its time to look into Box2D joints. Creating a Box2D weld joint Creating a b2Joint requires sometime quite a lot of repetitive code so just like when creating a b2Body you might want to wrap that up into classes or static methods in order to cut down the amount of code you write. You can of course combine b2Joint together to achieve a complex system of mechanic. Be aware that creating a non logical situation can cause a Flash Player crash so be sure you know what you are doing. B2Joint can also break if you apply to them the right amount of force. Most joint creation use a common pattern: . A b2Body reference.
12/23/2011 11:43 AM

11 of 28

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

. The b2Body target of the joint. . the position where the joint will be attached. The b2Body reference is the b2Body where you want to attach the joint. The target b2Body is the body the joint will influence. The position is an absolute position (x,y) that will indicate where you want to attach the joint in relation to the reference joint. Even though we use absolute position here it will end up being a relative positioning to the reference b2Body. As I said this is true for most joint creation so that also means not all of them use that pattern. Dont worry, we will look at all of them. So lets start with a simple b2Joint, the b2WeldJoint. This joint simply glue two b2Body together. You could use this b2Joint to construct a complex object and upon the right condition you would destroy the b2Joint so the complex object fall apart. Be aware that that joint is quite sensitive to force constraint so dont use it if you want two b2Body to be very strongly glued together. In this next example I built a little bridge and simply let objects fall upon it. As the weight increases on the bridge you will start to see it move and even bend. That is because a lot of constraint is imposed on the weld joint themselves. Finally when the last object falls I destroy all joints and let the poor bridge crash down gracefully. Click anywhere to start the demo:

The bridge was made up of 5 graphic/b2Body and 4 joints and created by calling a createWeldJoint() method. Check it out in the document class but here is the relevant part:
[as]var jointdef:b2WeldJointDef = new b2WeldJointDef(); jointdef.Initialize(bridgebox, bridgeboxtwo, new b2Vec2(350 / SCALE, 350 / SCALE)); var joint:b2WeldJoint = world.CreateJoint(jointdef as b2JointDef) as b2WeldJoint;[/as]

First we created a b2WeldJointDef (every single joint type has a Def type of class we use for creating

12 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

them) just like we do when creating a b2Body. Then we call Initialize() (here again we will do this on most joint type). We pass the two b2Body we want to weld together and an anchor point (b2Vec2). Most joint definition class have an Initialize() method we need to call. Finally we create our joint by calling world.CreateJoint() and our joint is up and working. As I said this one is quite simple but theres really so many interesting thing that can be created with it. Experiment on your own and lets move onto another simple one. Creating a Box2D distance joint A b2DistanceJoint simply keeps two b2Body at a certain distance from each other at all time but thats the only freedom restriction those b2Body have. This joint can be used as part of a complex object where you are using many type of joint or for creating chains for example. You can apply many joint type to the same b2Body or group of b2Body so the b2DistanceJoint can really take a good place in your complex object creation. You imagination is the only limit. Click to start:

A very simple example but really the b2DistanceJoint can help you create very complex mechanics and objects. Not much code needed either:
[as]var distance:b2DistanceJointDef = new b2DistanceJointDef(); distance.Initialize(boxtwo, boxthree, new b2Vec2(55 / SCALE, -50 / SCALE) , new b2Vec2(65 / SCALE, -50 / SCALE) ) var disatncejoint:b2DistanceJoint = world.CreateJoint(distance as b2JointDef) as b2DistanceJoint;[/as]

You pass the two b2Body you want to link and then two b2Vec2, one representing the first anchor point attached to the first b2Body and the second representing the second anchor point attached to the second b2Body. Lets move now onto more complex type of joint.

13 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

Creating a Box2D friction joint A b2FrictionJoint lets you apply friction to a b2Body. This can be useful for simulating friction between two pieces of machinery or simply for representing the friction occurring when an object rolls over different type of ground. In this next example all falling basketball have the same size and weight but the ones on the left have a b2FrictionJoint applied to them so you can see the difference in speed and bounciness. Note that once you create a b2FrictionJoint you can of course change on the fly its effect by adjusting its maxForce and maxTorque property. Like if you were creating a mini golf type of game, you could set the maxForce to 0 when passing over ice and to 8 or 9 when passing on grass. Anyway click anywhere to see this joint in action:

With such a high level of friction the basketballs on the left have the tendency to stick easily to other object. You can notice also the speed difference. Here is the relevant code:
[as]var jointdef:b2FrictionJointDef = new b2FrictionJointDef(); jointdef.localAnchorA.SetZero(); jointdef.localAnchorB.SetZero(); jointdef.bodyA = body; jointdef.bodyB = contacttarget; jointdef.maxForce = 6; jointdef.maxTorque = 5; jointdef.collideConnected = true; world.CreateJoint(jointdef as b2JointDef);[/as]

We set the two anchor point to zero so the friction doesnt have a direction. We then set the first body as the target of the friction and the second body as the ground (in this example). Then we set some force and torque values and make sure the two bodies can still collide with each other. Lets now take a look at the

14 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

b2MouseJoint. Creating a Box2D mouse joint The name b2MouseJoint can be a little confusing. At first we might think its a joint meant to be only used with the mouse. Well of course we can implement easily drag functionality with this joint but what this joint really does has virtually nothing to do with mouse interaction. The b2MouseJoint allows you to set a destination position for a b2Body, and this b2Body will try to get there while still interacting with the Box2D world and be subject to any forces that apply. That means that if the path of that b2Body is going through a wall then the b2Body will be stuck behind the wall and wont reach its destination. If there are other b2Body on the dragged b2Body path then this b2Body will try to push them aside. The reason this has nothing to do with a mouse is because setting the b2Body destination is done simply by passing a pair of value which could easily be done by code or by pressing keys on your keyboard. Of course passing as value the mouse position makes the b2Body try to follow the mouse. I guess my point here is, dont limit this joint to only mouse interaction since it can do so much more. Theres an important point to notice here, b2body have a method called SetPosition() with which you can easily set the position of your b2Body. So why cant we use that to drag our b2Body? The SetPosition() method sets arbitrary the position of the b2Body regardless of the box2D world interaction, forces, and other b2Body. For that reason using SetPosition() can produce unwanted results like setting a dynamic b2Body inside a static b2Body. Box2D wont like that and will probably stop handling your b2Body correctly or even in some cases crash the Flash Player. So use the SetPosition() method wisely and if what you really want if a dragging around a b2Body while still having this body interacting with the Box2D world just like any other b2Body, then b2MouseJoint is the joint you need. Just a simple example now. A bunch of b2Body, click and hold the mouse and to drag a body, release the mouse and to release the b2Body:

15 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

The relevant code of course:


[as]var mousedef:b2MouseJointDef = new b2MouseJointDef(); mousedef.bodyA = world.GetGroundBody(); mousedef.bodyB = currentbody; var bodypos:b2Vec2 = currentbody.GetWorldCenter(); mousedef.target.Set(bodypos.x, bodypos.y); mousedef.collideConnected = true; mousedef.maxForce = 300 * currentbody.GetMass(); mousejoint = world.CreateJoint(mousedef) as b2MouseJoint;[/as]

The first b2Body must be one of reference like a ground that you create or like here the convenient world.GetGroundBody() method. The second b2Body is the one you want to drag around. Then we set the b2Body starting position by simply calling currentbody.GetWorldCenter() and passing it as starting position. We want the b2Body to still collide with the ground so we set this to true. Finally we apply a maxForce which here is pretty strong. You want to set this strong enough so the b2mouseJoint can move around heavy objects easily. The smaller the value, the harder it will be for the b2Body to reach its destination which can create interesting effects of course. In the example I of course destroy the b2MouseJoint when you release the mouse. Finally in the update method we add this after the world.step():
[as]var currentmousepos:b2Vec2 = new b2Vec2(stage.mouseX / SCALE, stage.mouseY / SCALE); mousejoint.SetTarget(currentmousepos)[/as]

We simply pass the value pair of the current mouse position which sets the destination for our b2Body. We could as easily substitute this by a value pair controlled by the arrow key on your keyboard or even by random values generated by code.
12/23/2011 11:43 AM

16 of 28

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

To prove my earlier point here is an example where we use the b2MouseJoint without a mouse! Use your arrow keys on your keyboard to move the crosshair and watch the b2Body follow it:

Another really useful joint (they all are anyway) is the b2RevoluteJoint. Let see that now. Creating a Box2D revolute joint Making things turn! With this joint you can make your b2Body turn around an axis but thats not all. You can make them turn freely, make them turn on a range of degrees, and even apply a motor to power the turning! Thats basically everything you need to simulate any turning element from a complex car gear system to a simple fan. You could easily create a car brake system by adding a b2Frictionjoint in the mix for example. Also if you want to create a car game then this is the number one joint you need to look into. So lets take a look at our first demo. We simply set a platform turning freely around an axis. Simple enough. Click anywhere to get started:

17 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

Relevant code:
[as]var platform:b2Body = DrawBox(250, 250, 250, 10, true, 0, 1, 1, 1); var revolute:b2RevoluteJointDef = new b2RevoluteJointDef(); revolute.Initialize(world.GetGroundBody(), platform, platform.GetWorldCenter()); var joint:b2RevoluteJoint = world.CreateJoint(revolute as b2JointDef) as b2RevoluteJoint;[/as]

We create our platform b2Body and we initialize our joint definition with the world.GetGroundBody() as the b2Body of reference. The second b2Body (platform) is the one we want this b2RevoluteJoint to apply to. The most important part is the last one, platform.GetWorldCenter(), since this sets the axis around which the platform will turn. Here I simply retrieve the platform center of mass. This b2Vec2 could actually be set to other values obviously to create a type of swing for example. Since our platform is set at x:250, y:250 we could simply pass a b2Vec2 at 250, 150 which will set the axis 100 pixel above: [as]revolute.Initialize(world.GetGroundBody(), platform, new b2Vec2( 250 / SCALE, 150 / SCALE));[/as] and the result:

18 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

Now lets try to limit the degree range:


[as]revolute.lowerAngle = -30 * Math.PI / 180; revolute.upperAngle = 30 * Math.PI / 180; revolute.enableLimit = true;[/as]

We set the lowerAngle to 30 degrees (converted to radians), the upperAngle to 30 degrees and finally we enable the angle limit. Now our platform can only move in between -30 and 30 degrees:

19 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

Now lets try to use a motor:


[as]revolute.enableMotor = true; revolute.maxMotorTorque = 10; revolute.motorSpeed = 2;[/as]

We first enable the motor, we then set a small amount of torque and finally give the joint some speed. Now to stop a joint like that from turning or to change its speed you need to keep a reference to the created joint. With a b2RevoluteJoint you can use a method like SetMotorSpeed() and simply set it to zero to stop the motor. Here is our demo:

20 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

Because we have a very low amount of torque, other object can stop or modify the way the platform turns very easily. The platform could even stop moving if there are too many object in front of it. Lets try again with a bigger amount of torque to see the difference:

21 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

We set the torque to 2500 and now even though the speed is the same it seems that no object can really alter the platform speed or even stop it anymore. Alright we covered everything with this joint but just for the fun of it lets finish with a car example. Use the arrow key on your keyboard.

22 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

We build a simple car body with a few b2Body and then attach two b2Body at each extremity. We then set two b2RevoluteJoint and keep a reference of them so we can set the motor speed on the fly. Also in this example we set the two inclined ground on the side with a friction of 0 which pretty much simulates icy ground. Use the left and right arrow to play with it.
[as]var revolute:b2RevoluteJointDef = new b2RevoluteJointDef(); revolute.Initialize(suspensionholder, weelone, new b2Vec2(267 / SCALE, 415 / SCALE)); revolute.collideConnected = false; revolute.enableMotor = true; revolute.motorSpeed = 0; revolute.maxMotorTorque = 100; motorone = world.CreateJoint(revolute as b2JointDef) as b2RevoluteJoint;[/as]

In a keyboardEvent listener:
[as]motorone.SetMotorSpeed(10);[/as]

Thats it for this b2RevoluteJoint. Creating a Box2D line and prismatic joint So far I kept all type of joint into its own section but here the b2LineJoint (new to 2.1) and b2PrismaticJoint are so similar that I thought it would be better to keep them together. They are so similar in fact that you create them exactly the same way and with the same type of values. So whats the difference? Well with the b2LineJoint the b2Body target of the joint can still move freely on its own while with the b2PrismaticJoint the b2Body target is tied to its joint and the joint axis. Well take a look shortly so we can see the difference but first let me tell you what they do. Those two joint allow a b2Body to translate along an angle. You can also set a translation range
12/23/2011 11:43 AM

23 of 28

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

(minimum, maximum) and finally you can power them with a motor. Think about the moving platform in the old 2D Super Mario games. You can set them either vertical or horizontal or at any angle you want. Perfect for creating elevators, carriers, suspension system, pumps and so on. In this next demo I set a b2lineJoint on the left and a b2PrismaticJoint on the right. The goal being to reach the center top platform with the buggy. Click on the green arrows to make the two joint go up or down. You can control the buggy with the arrow keys on your keyboard:

If you tried to make both joint carry the buggy Im guessing you experienced a little problem. With the b2Prismaticjoint the platform stay still while going up just like an elevator but with the b2LineJoint the platform starts rotating and its nearly impossible to keep the buggy on it. Both joint translate a b2Body along an axis but with the b2PrismaticJoint the b2Body is tied to the joint and so cannot rotate or interact with the Box2D world like a free b2Body would. With the b2LineJoint the b2Body is still free to rotate and interact with the Box2D world. Relevant code (remember b2LineJoint and b2PrismaticJoint are created the same way):
[as]var linedef:b2LineJointDef = new b2LineJointDef(); linedef.Initialize(axis, lineplatform, lineplatform.GetPosition(), new b2Vec2(0, 1)); linedef.enableLimit = true; linedef.enableMotor = true; linedef.lowerTranslation = 0; linedef.maxMotorForce = 100; linedef.motorSpeed = 0; linedef.upperTranslation = 270 / SCALE; linejoint = world.CreateJoint(linedef as b2JointDef) as b2LineJoint;[/as]

The initialize method is particular here. I created a special axis b2Body and passed it as the reference

24 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

b2Body (I could have used world.getGroundBody()). Next I pass the platform b2Body which is the one the buggy is supposed to stand on. I then set the anchor to the coordinate of that b2Body. Finally last parameter is a vector (notice we dont use / SCALE here) and will set the angle at which the translation occurs. You have seen most other values before. We enable range limit, enable motor, set the lower range to 0 (starting point), the upper range to 270 (reaching the ground). A torque of 100 so we can carry some weight. We simply set the joint motor speed when clicking on the green arrows. An operation as simple as that :
[as]linejoint.SetMotorSpeed( -1); prismjoint.SetMotorSpeed(-1);[/as]

Negative value to go up and positive to go down. Now to make a platform go horizontal you would pass a vector like this: new b2Vec2(1, 0) Also to make a platform go back and forth (there are about dozens of ways to do it) you could set a number variable to keep track of the platform position and once the platform stops moving it is time to reverse the speed:
[as]linedef.motorSpeed = 1;//start with some speed jointposition = 0;//keep track of platform position[/as]

Then in the update method after the world.step():


[as]if (jointposition == linejoint.GetJointTranslation())//not moving? { linejoint.SetMotorSpeed(linejoint.GetMotorSpeed() * -1);//reverse the speed } else { jointposition = linejoint.GetJointTranslation();//it's moving so store the position }[/as]

Thats it about those two joints now lets play with the b2PulleyJoint joint. Creating a Box2D pulley joint What the pulley joint does is simple, you have two b2Body (lets say body1 and body2) and two anchor points. When you pull down one body1 body2 goes up, when you pull down body2 body1 goes up. How you pull them down or move them is really up to you. Add a b2MouseJoint or increase the weight of one b2Body by placing other b2Body in it, or increase the mass or whatever other system you can think about. In our following example we try to move up our buggy (the keyboard control are turned off this time) by increasing the weight of the b2Body on the left. We increase or decrease its weight by clicking on the green arrows in the middle. When the b2Body weight starts to be bigger than the car weight and the platform weight together, the buggy starts to move up.

25 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

Relevant code:
[as]var pulleydef:b2PulleyJointDef = new b2PulleyJointDef(); var bodyoneanchor:b2Vec2 = new b2Vec2(100 / SCALE, 150 / SCALE); var bodytwoanchor:b2Vec2 = new b2Vec2(400 / SCALE, 150 / SCALE); pulleydef.Initialize(counterweight, support, bodyoneanchor, bodytwoanchor, counterweight.GetWorldCenter(), support.GetWorldCenter(), 1); world.CreateJoint(pulleydef as b2JointDef);[/as]

The first b2Vec2 defines the first anchor point for the pulley, the second b2Vec2 defines the second anchor point for the pulley. In the initialize method we pass : . our first b2Body. . our second b2Body. . the first pulley anchor point. . the second pulley anchor point. . where to attach the pulley on the first b2Body (we attach to the center with counterweight.GetWorldCenter()). . where to attach the pulley on the second b2Body. . finally a scale value used to multiply the value generated by the action of the first b2Body. For example with a value of 2 the second b2Body will move twice as fast. You could also specify a max length for the first and second b2Body but Ill leave that to you to experiment with. Now lets discover the last joint in this overview, the b2GearJoint . Creating a Box2D gear joint

26 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

Its logical to finish with the b2gearJoint since it connects two joints together. All the joint types we looked at provide really cool functionality but whats even cooler is using them together to create a complex mechanic. The b2GearJoint connects either two b2RevoluteJoint together or a b2RevoluteJoint and a b2PrismaticeJoint. Once connected the two joints influence each other. The two joints must reference the same static b2Body in order to work properly. The value generated by the first b2Body is reflected by the second b2Body. You have also a ratio value that will multiply the value generated by the first b2Body. In the following example, the wheel on the left has a motor that you can control with the two green arrows. The wheel on the right (b2RevoluteJoint) and the platform (b2PrismaticJoint) have no motor but they are all linked together using a b2GearJoint. When the first wheel turns it makes the second wheel turn and this second wheel makes the platform move:

Relevant code:
[as]geardef = new b2GearJointDef(); geardef.bodyA = secondweel; geardef.bodyB = firstweel; geardef.joint1 = revolutejoint as b2Joint; geardef.joint2 = revolutejointtwo as b2Joint; geardef.ratio = -1; world.CreateJoint(geardef as b2JointDef);[/as]

Linking the second wheel and the first together. First b2Body is the wheel on the right, second b2Body is the wheel on the left (with the motor). We then pass the b2RevoluteJoint, the first one being attached to the first b2Body and second one to the second b2Body. Here we inverse the generated value with a ratio of -1.

27 of 28

12/23/2011 11:43 AM

http://www.actionscript.org/resources/articles/1087/1/GettingStartedw...

Box2D Article Conclusion Box2D AS3 framework provides you with everything you need to create 2D type of platform, car, RPG games and other. For small project or tutorial like this one its fine to use quick and dirty direct references to graphics and b2Body like I did, but for bigger projects you should wrap up all functionality and references into classes to cut down the amount of code you have to write since creating b2Body or b2joint or other in Box2D can require a lot of line of code. This is what I did for the MightyDog game where all the Box2D elements are created dynamically by xml using a well structured set of classes. I hope you will have as much fun as I do have when I work with Box2D!

28 of 28

12/23/2011 11:43 AM

Você também pode gostar