One of the most complicated and math-intensive components of game development is collision detection and response. If all you need is to calculate when a couple of rectangles overlap, it’s not a big deal, but for games that involve lots of irregularly shaped sprites moving and reacting to each other realistically, you’ll want to use an industrial strength tool to get the job done.
Enter the physics engine.
The idea behind using a physics engine in your game is that instead of hacking together a very-loose approximation of how real objects might interact for your game, dropping in an engine whose sole purpose is to run the physics simulation allows you to leave it to the engine to handle object dynamics and interactions and concentrate on the game itself.
To this end a number of different Physics engines, both 2D and 3D have made their way to JavaScript, many of them ports from other languages, but some written from scratch. For an overview of a number of different engines, take a look at Chandler Prall’s JavaScript Physics Engine Comparison.
Of the myriad of open-source 2D engines that are available, one stands out: Box2D. One reason for this is that it was used as the simulation engine behind mega-hit Angry birds, whose physics-based gameplay required a bullet-proof physics implementation. Box2D is the 2D physics engine of choice for many games, and thus was ported wholesale over to Flash and ActionScript a while back. To get a JavaScript version, some ambitious folks have taken advantage of the similarities between ActionScript and JavaScript and created a converter that converted the ActionScript to JavaScript.
There are a few ports floating about, but the most popular one is Box2DWeb:
http://code.google.com/p/box2dweb/
Some work has also been done to convert directly from C++ to JavaScript using emscripten (See box2d.js) but this has a different API than box2dweb and doesn’t appear to be kept up.
Box2DWeb doesn’t have it’s own documentation per se, but it shares the same API as the Flash library it was converted from, so the Box2DFlash Documention can be used to figure out the Box2DWeb API. If you’re interested in really diving into Box2D, the official Box2D C++ manual also makes for a worthwhile read, although it contains some newer features not found in Box2DWeb.
Bootstrapping
To get a Box2D world up and running, you’ll need to pull in the library, which is packed up nicely in a single JavaScript file Box2dWeb-2.1.a.3.js
The examples below are going to use the following basic HTML wrapper:
This pulls in the Box2DWeb library and example.js file that will contain all the example code.
All of Box2DWeb comes namespaced inside of the Box2D
object, sometimes pretty deeply nested under sub-objects. So that you don’t go insane with all the namespacing, it’s recommended that you pull out some of the most-used classes into easy-to-access variables, as shown below:
This article is going to build up a small example that shows you how to use Box2D and how it might integrate into your game engine. This example will wrap many of the more verbose pieces of Box2D in wrapper classes to simplify the API you need to expose to your game.
Creating the world
The main container object for simulations in Box2D is the b2World object. It take two parameters, a gravity vector and whether or not it should skip simulating inactive bodies. 2D Vectors in Box2DWeb are created using the b2Vec2 object, whose constructor takes in x
and y
components. Creating a world with earth gravity that puts inactive objects to sleep would look like:
You generally want to set the second parameter to true so that Box2D doesn’t waste CPU cycles simulating the big stack of boxes that’s just siting there, not moving, until the player crashes into them and wakes them up. This can, however, lead to situations where you need to be careful to progamatically wake up objects when you manually move them to a state where they should be simulated (like when you pick up a box with the mouse cursor and leave it hanging in mid air).
One other thing you’ll want to take into consideration is the relationship between Box2D units and your rendering target (canvas in these examples.) The Box2D length unit is the meter and having objects that are sized in meters makes it happiest (happiness, in regards to a physics engine, refers to calculation stability.) But since many games like to think in pixels, you’ll want some idea of a scaling ratio between Box2D units and pixels. Let’s settle on a scale of 30 pixels per meter for now, but you can adjust this as you like.
Wrapping world creation and a couple of additions in a simple constructor function gives the following code:
Stepping the world
To advance the simulation by a certain amount of time, you’ll need to manually call the b2World.Step
method (notice, all methods in Box2DWeb begin with capital letters, which is un-javascript-like but matches the Box2DFlash API). Step
takes three parameters: a time step, the number of velocity iterations. and the number of position iterations.
The Box2D manual suggests using 8 and 3 as velocity and position iterations respectively, but these can be tuned as needed, trading accuracy for speed by increasing those numbers.
The time step parameter is fairly self explanatory: it’s the amount of time in fractions of a second that Box2D should simulate the world for. Box2D works best when this is a fixed amount of time and not a variable amount of time dependant on the framerate. This means that you’ll need to do some house keeping to make sure that the physics simulation runs at a constant frame rate, even when being driven by requestAnimationFrame.
Building on the Physics class from the last code listing, you could write a step method as:
As you can see above, Box2D also supports a debug draw mode which, when passed a canvas element, will draw a basic vector debug rendering output onto that canvas. This will be used for the first few examples and a helper method to activate it is shown below:
Tying this together with initialization and a gameLoop
using requestAnimationFrame gets you the following:
Two items to note in the above listing. The first is that since requestAnimationFrame stops firing when your game’s tab is unfocused, if you run your simulation code from a requestAnimationFrame loop you’ll want to put in an upper time limit on the size of dt
to prevent your game from fast-forwarding when the user refocuses the tab.
The other option is to put your simulation in a setTimeout or setInterval driven loop which doesn’t exhibit this behavior, but using requestAnimationFrame as a sort of automatic pause for your game can be useful in single-player games. If you’re building a multi-player game, it’s probably not a good plan and using setTimeout or setInterval might be better.
You’ll also want to add in the requestAnimationFrame Shim
Adding in Bodies
An empty world without anything going on in it is pretty boring. In order to give it some life, we’ll need to add some Box2D bodies to it. Bodies are what Box2D calls the objects that it simulates.
Bodies can either be static, kinematic, or dynamic. Static and kinematic bodies are used for walls and platforms that don’t need to react to collisions, while dynamic bodies are used for everything that’s needs to be simulated normally. Both static and kinematic bodies are treated as if they have infinite mass and can be moved manually by the developer. Kinematic bodies can also have a velocity. Static and kinematic bodies don’t collide with other static and kinematic bodies, only with dynamic bodies.
To create a Box2D body that interacts with other bodies, you need two things, a body definition and one or more fixture definitions. The body definition defines the initial attributes of the entire body, such as it’s position, velocity, angular velocity, damping (the inverse of bounciness) and boolean attributes such as whether to start out active, if the body is allowed to sleep. You can also decide whether the body should be treated as bullet
, which garners it more processor intensive but accurate collision detection to prevent small, fast moving objects from passing through other objects entirely. You can reuse body definitions if you like.
One additional feature of bodies is that you can set a piece of arbitrary userData
on them. This userData
is often used to tie the physics object back to a graphical representation.
Fixtures are the glue that binds shapes to a body. A body can have one or more fixtures. All fixtures are rigid inside of a single body, which means that the individual fixtures don’t move relative to each other but rather combine to create a single rigid body. Fixtures also contain additional information such as density, friction, restitution, and collision flags that control with other fixtures to collide with. You can also flag fixtures as sensors to prevent a collision response but still receive notification of a collision. Fixtures are created by calling b2Body.CreateFixture
and passing in a fixture definition.
To create a fixture definition you can attach to a body, you need to give it a shape to work with. Box2D supports two different types of shapes. The first type, b2CircleShape
represent, not surprisingly, shapes that are perfect circles. The second type b2PolygonShape
are used for shapes that can be represented with a series of vertices. The vertices of b2PolygonShape
’s must define a convex polygon in clockwise order (if your vertices are counter-clockwise, your shape won’t collide with anything.)
Since boxes are such a common shape, Box2D provides a helper method to easily create the vertices for a b2PolygonShape
box of a given width and height called SetAsBox
, which takes a two parameters that correspond to 1/2 the width and 1/2 the height of the vertices to create.
Convex polygons are polygons that have no indents (visually, it means you should only be able to draw a line from one vertex to it’s neighbors without passing through the shape.) This means if you want to create a more complicated shape, such as the capital letter L, you cannot do so with a single shape in a single fixture. You must add multiple fixtures to your body instead. Once thing to remember is that your collision shape does not need to match your game sprite exactly, but can instead be a simplified representation of that shape. So even if your video game character running around with a big bazooka doesn’t look like a convex shape, modeling them as one is probably ok.
Keeping your collision shapes simple with result in faster collision detection and will likely make for better gameplay. Concave polygons tend to get stuck on things, so even if you could realistically model Bazooka guy, he might be better off as concave anyway so his Bazooka doesn’t get caught on ledges and leave him dangling off cliffs unrealistically as a multi-fixture rigid-body.
With all that in mind, let’s create a simple class that makes it easy to create bodies. This class will take in the physics object from the previous section and go through the steps necessary to add a new body to the world:
The Body
defines a simple constructor function that, using a set of defaults defined above, let’s you create single-fixture Box2D objects easily without jumping through all the normal definition and fixture hoops.
Modifying the init()
method from above to add in some objects as shown below to get some objects on the screen:
If you view the example you’ll see the three boxes fall down and then change color to gray when they are put to sleep by the simulation.
Taking over rendering
While Box2dWeb’s debug rendering is a boon to getting started quickly, at some point you’re going to want to handle your own rendering so that you can build a game other than “Attack of the Transparent Boxes.”
There are two ways to go about this. The first is to keep a list of bodies separate from Box2D and loop over those after each step and render them. The second is to use the world.GetBodyList()
to return the first body and then call body.GetNext()
to return the next element. This works because Box2D keeps a linked list of bodies to iterate over with. You can pull the element you’ve associated with the body out by calling GetUserData()
.
Either method will work and the second makes bookkeeping easier, but if you have game elements that aren’t simulated with Box2D (which you might) you must use the former.
In these examples, were going to use the latter method as it’s less code.
Bodies will also need how to render themselves. The easiest way to handle this is simply to add in a draw()
method to our simple Body wrapper class that looks at its type and then renders itself by drawing the appropriate shape. To make this more applicable to normal games as well, bodies will also support drawing an image.
Drawing shapes with canvas isn’t particularly difficult, but with Box2D, the main thing to watch out for here is correctly positioning and rotating the drawn such that the way it’s drawn on the screen correctly reflects Box2D’s internal representation.
By correctly applying the matrix operations using the built-in canvas matrix transformations in global to specific order, we can make drawing the shapes straightforward.
The three transforms we need to worry about are the global scale (if you remember, we’re drawing objects at 30 pixels to a meter), the object translation and then the object’s angle. The global scale transform can be handled outside of the main loop, while the latter two transformations both need to be applied per object.
Using the Box2D GetBodyList()
method, the Physics.step()
method is modified to draw the objects itself unless debug rendering is on:
In a normal game if you already have a rendering loop, you’d iterate over the sprites and then reach in and grab the Box2D representation data each step.
Next up is the Body class drawing method. This method will save the current transformation matrix, translate and then rotate the matrix so that the object can be drawn centered at the origin and then will draw the object based on the shape. In this example, the shape will be drawn if the object has a color
property set.
Lastly, to demonstrate how to draw images, it will draw an image if the image
property is set (based on the width and height properties - these need to be set even if the object type itself isn’t block)
The full method is shown below:
We get the current position and rotation by calling body.GetPosition()
and body.GetAngle()
.
Circle shapes are simply drawn as a 360 degree arc path and then filled. Polygon shapes draw a path using the same points as the shape definition. Blocks just use the fillRect
method to draw a rectangle.
See example 2 for some more shapes all falling together, rendered by the above rendering code.
Picking and Moving
Depending on the genre of game you are building, you may want to add in the ability to directly interact with your game object via the mouse or touch. In order to achieve this you must first be able to determine the object at a specific pixel position on your canvas.
Box2D natively supports a way to query bodies’ bounding boxes given a world position. Given the returned body you could then loop over each of the bodies fixtures and match the position against the exact shapes. Luckily Box2DWeb has wrapped this functionality up in a single method call: QueryPoint()
.
Given a vector location in world coordinates, the QueryPoint()
method will execute a callback for each fixture that matches.
As a first pass, let’s add in the ability to click on an object and have it impart an impulse onto the object. With Box2D there are three ways you can get an dynamic object moving: set its velocity, apply a force or apply an impulse. One thing you should not do is just try to set a dynamic object’s position, as this effectively breaks the simulation and can cause unexpected effects with objects behaving badly. If you really need you move an object to a position, you can call body.SetTransform
, but this method is undocumented in the Box2DFlash documentation, which should give you an idea of whether it’s a good practice or not.
Among the three good options, applying a force or an impulse are essentially the same thing - the only difference is that the force takes into consideration the length of the time step to determine the impact on the object, while the impulse is independent of the length of the time step. Forces are good to use when you want consistent speed over a number of frames, while impulses are great as you can just set and forget them. You might use the former to keep a player running at a consistent speed while the latter works well for things like explosions.
You set a force on an object with ApplyForce(force,point)
and an impulse with ApplyImpulse(impulse,point)
. Both ApplyForce
and ApplyImpulse
take a vector as the first argument that controls direction and strength. They also both take a second argument that defines the point where the force is applied. If you don’t want the object to torque (i.e. start to rotate), you can apply the force to an object’s center using body.GetWorldCenter()
.
Forces work well with situations where you want objects to behave realistically and do things like smoothly speed up from a standstill. A lot of times in games, however, you don’t necessarily want your player to have to slow down to a stop before changing direction. You can defy the laws of physics by directly setting the velocity on an object as opposed to applying forces and impulses. If you set the velocity each frame, you can get de-facto consistent speed that is useful in things like space shooters and platformers. The only thing to watch out for is that dynamic object-to-object interactions will be slightly out-of-whack when two objects of different masses that both have their velocity set interact.
You can set linear and angular (rotational) velocity using SetLinearVelocity(direction)
and SetAngularVelocity(number)
. While you might be tempted to try to keep reseting the angular velocity to try to prevent a object from rotating, this won’t work well and you’re better off setting the fixedRotation
property on the body definition or calling SetFixedRotation(true)
.
Starting with picking, let’s add a method to the top-level Physics object that will call a callback whenever an object is mousedown’d on or touched:
This uses the native addEventListener syntax for portability sake, but if you’re using jQuery you can substitute $.fn.on
to make the function more compact.
To test this method out to apply a vertical impulse, we’ll add the following to the init method:
You can see this in action on Example 3 Clicking on an object will launch it up and to the right. If you want to visually see the differences between ApplyImpulse
, ApplyForce
and SetLinearVelocity
, try swapping the call to ApplyImpulse
out with the others to see the effect.
You’ll notice the larger the object, the less effect the impulse has. This is because the Box2D assumes objects have a uniform density (controlled by the density property in the fixture definition) and so larger objects weigh more and need more force to move.
In addition to the QueryPoint
, Box2DWeb also provides a number of other method to query the world for objects, including by bounding box (QueryAARB
), by arbitrary shape (QueryShape
), and by querying for collisions along a line (RayCast
). RayCast
is particularly useful for doing things like visibility detection (can enemy A see the player?) and determining what bullets and shrapnel will hit.
Listening for collisions
Depending on the game you are creating, simply being able to create and control discrete objects may be enough to get going, but if you want to add true physics-based gameplay (such as determining the strength of collisions and which objects are touching which, you’ll want to use Box2D’s contact listener functionality.
Box2D provides four different callbacks you can listen for when collision occur. The first BeginContact
occurs when contact is initiated between two objects. The second EndContact
is triggered when two objects stop touching. The third callback PreSolve
, is called on every collision, but before the collision is sent to the solver to resolve. All three of these only provide details on what two objects are in contact and normal details with what’s known as the contact manifold, but don’t give us a strength-of-impact impulse.
To get that detail, you’ll need to use the fourth callback, PostSolve
, which is called whenever there is an impulse on a body caused by another body. As bodies are crashing into each other all the time, PostSolve
will get called very frequently, so it’s important not to do too much processing each time the callback is triggered. Two bodies coming into contact with each other could each only get a single BeginContact
and EndContact
callback, but will mostly likely get a number of PreSolve
and PostSolve
callbacks.
Two important words of warning: first, you should not, under any circumstance, add, change, or remove bodies during the collision callbacks as this will most likely disrupt Box2D’s collision resolution process. If you need to make changes to any aspects of the physics simulation as a result of one of these four callbacks, you should queue up what needs to be done and then make those changes after the step call. Secondly, the data structure for contact information is reused, so if you do queue up steps to change afterwards, you should make sure to copy the individual pieces of that structure out to separate objects.
Because it provides the most interesting data, we’re going to use the PostSolve
callback to highlight objects based on the strength of their most recent impact.
Because the callback is global, we’ll need to reach into the object on contact and grab our wrapper object using GetUserData()
if you want to have per-object collision reactions.
For example, to add in a collision listener that calls a contact
method on each body (if that method exists) you could extend the Physics wrapper as is shown below:
The above method can be extended to add listener support for BeginContact
and EndContact
by adding those callbacks to the listener
object. BeginContact
and EndContact
both only take the contact
object as a parameter.
To extend a body with a contact callback, you could do something like this:
Here the blue box will change it’s color based on the strength of the last impact. You can use this callback to determine when objects have received enough damage to be destroyed or explode.
Take a look at Example 4 to see this code in action. The contact callback has been added to the box that is originally blue, and the add-an-impulse with a click behavior from Example 3 is still in there so you can see how collisions trigger.
Adding Joints
The last main piece of Box2D that we haven’t talked about yet is joints. Joints allow you to constrain bodies in one or more dimensions to another body or another point
Box2D supports a number of different joints, with descriptions paraphrased from the Box2D manual:
- Distance Joints - the distance between two points on two bodies will stay constant
- Revolute Joints - a joint that forces two bodies to share a common anchor point, acting like a hinge. You can constrain this joint to limit the angles between the two objects as well as turn on a motor to drive rotation.
- Prismatic Joint - allows for movement between two objects along a single axis. You can limit constrain the joint to limit the minimum and maximum distance as well as turn on a motor to drive translation.
- Pulley Joint - used to create an idealized pulley, which connects two bodies. As one body goes up, the other goes down.
- Gear joint - a joint that combines two of any combination of revolute and prismatic joints and acts like an idealized gear. You can control the ratio of rotation or translation.
- Mouse Joint - used to connect a single body to a changeable destination point. Primarily used for “soft” drag and drop of objects.
Mouse Joints
Joints are easiest to see work when you can manipulate the bodies directly, so to start to play around with them the first thing to do is set up a Mouse Joint to allow you to drag objects around the screen (using mouse joints in a testbed is the most common use for this type of joint)
Mouse joints are created by first creating a joint definition object of the correct type (in this case Box2D.Dynamics.Joints.b2MouseJointDef
), then setting the affected bodies, bodyA
and bodyB
, and then setting any additional tuning parameters to control the strength and effect of the joint. Finally world.CreateJoint
is called to create an actual joint from the definition.
For simplicity’s sake, the code below uses only the mousedown
, mousemove
and mouseup
events. To add in touch support, you could add in touchstart
, touchmove
and touchend
events. Adding support for multiple touches would allow you to drag multiple objects around the page at once (see Mobile Game Primer for how to do multi-touch)
To add in object drag-and-drop, the following code does the trick:
Most of this is relatively straightforward, but one piece that might need some explaining is the self.world.GetGroundBody()
spot. Box2D by default creates an empty body with no fixtures in it that it calls the Ground Body. In this case, the MouseJoint attaches the first body to the Ground body, but that body isn’t actually used for anything.
For the other joints, all of which do act on two bodies, the ground body is useful when you need a static body to connect to (remember static bodies won’t move) This pinions one end of the joint to prevent it from moving at all. You don’t need to use the ground body, you could use any other static body, but the ground body is nice because it’s always there and has no fixtures so you don’t need to worry about collisions.
You can see from the code above that each time the mouse is depressed, we look for a body at the point. If we find one, the object is saved for future use. Then, the first time the mouse is moved, it creates a new mouse joint. On that and every subsequent mousemove
event, we update the target of the joint to be the new location of the mouse, which will move the object towards the mouse if possible (the object will not move through static or dynamic objects, and will exert a force only upto the maxForce
parameter.) Finally, when the mouse is released, the joint is destroyed.
Take a look at Example 5 and try dragging objects around with your mouse.
Distance Joints
Distance joints, as mentioned above, enforce a set distance between to points on two bodies. The joint doesn’t, however, limit rotation in any way. You can hook up any types of bodies, but the joint will only move dynamic bodies.
With the exception of the MouseJoint
above and the GearJoint
, described at the end of this section, most joints are created by calling an Initialize
method on the Joint definition and passing in some initial parameters.
To create a distance joint, you need to pass in the two affected bodies to Initialize
and then two 2D vectors defining the anchor locations (in world coordinates) for both bodies.
For example:
This will anchor two boxes at their center (see the distance joint example in Example 6).
Additionally you can set properties on the joint definition for dampingRatio
and frequencyHz
, which control, respectively, how efficiently distance is realigned and the response frequency. These allow you to turn distance joints into soft springs by lowering the damping ratio and upping the response frequency. (See the second distance example to watch this in action).
Once a joint is created, if you want to draw the joint on screen (or just need additional information about the joint) you can query the joint itself using GetAnchor1()
and GetAnchor2()
to return 2D vectors is world coordinates.
Revolute Joints
Revolute joints connect two bodies together at a single point, but allow rotation around that point. If you connect two dynamic bodies together, it will act as if there is a hinge between those bodies at that point. If you connect to a static body, the dynamic body will rotate around that point.
The Initialize
method takes the two bodies and a third parameter defining the hinge location in world coordinates.
For example:
This creates two boxes hinged at one corner (See the Revolute joint example in Example 6
Revolute joints also support setting a limit on rotation and optionally turning on a motor to drive torque around the joint. See the Flash Documentation for the additional options on the b2RevoluteJointDef
class.
Prismatic Joints
Prismatic Joints act like a frictionless piston between two bodies. Creating one is similar to creating a distance joint: you specify two bodies and then two anchor points, as shown below:
The example above also turns on a upper and lower bounds limit using enableLimit
and setting the lowerTranslation
and upperTranslation
.
Like the RevoluteJoint
you can turn on a motor to drive motion along the joint’s axis. See the Box2DWeb Documentation for more details on the options that enable motor movement.
Pulley Joints
The pulley joint behaves exactly as you might expect: it connects two bodies together in a method that mimics a pulley. In general you’ll want to connect either two dynamic bodies or a dynamic and kinematic body, otherwise the pulley won’t do much.
Initializing a pulley joint is slightly more complicated as you need to provide four points in addition to the two bodies: two static anchor points that represent the top of the pulley for each body and then the anchor point on each body. As a final parameter to Initialize
you need to provide a movement ratio. To have both boxes move the same distance you and use a ratio of 1. To have body1 move twice as far as body2, use a ratio of 2.
Here’s an example that creates a pulley between two boxes:
Pulley joints run into problems when the distance between the pulley anchor and object is reduced to 0, so you should put some other static object in the way to prevent this from happening. In the pulley example in Example 6 The static anchors are blocked by the static body at the top of the page.
Gear Joints
The final joint supported by Box2DWeb is the Gear joint. Gear joints take any combination of two Revolute or Prismatic joints and create a joint linking their motion.
The Revolute or Prismatic joints you use must both have a static body as their first body (this is a good place to use the ground body). You can also set a ratio
property to control the movement ratio between bodies.
The Gear joint definition doesn’t have an Initialize
method to get things going, so you need to set up the bodies and joints yourself on the properties of the joint definition.
To set up a simple gear between two rotating boxes, you would first create the initial two Revolute joints, and then add them to a new Gear joint, as shown below:
If you need to destroy the joints, make sure you destroy the Gear joint first before either of the sub joints or the simulation could go haywire.
Joint Playground
The example used to this point has been modified to demonstrate some simple joints between two boxes. You can press any key on the keyboard to switch between joint types and use the mouse to manipulate bodies. See Example 6.
Wrapping Up
This article went over the details of getting started with Box2DWeb, a JavaScript port of the popular Box2D 2D physics library. It covered setup, rendering, picking, collisions, impulses, and joints. Hopefully this is enough of an introduction to Box2DWeb to get you started building your own physics-based simulations and games. If you want to dig further, there’s no substituion for the Box2DFlash Reference Docs and the Original C++ Box2D Reference Materials. Now go and simulate some bodies!
Comments