Box2D 2.1a Tutorial – Part 2 (Joints)

Welcome to Part 2 of my Box2D tutorial series. In the last tutorial we looked at the very basics of creating a world and populating it with a box . While it was necessary to learn that first, it was not terribly exciting. In this tutorial topic, we will learn about joints, which will allow us to start creating some cool stuff!

Joints are actually relatively simple to construct and follow a similar creation processes as when we create bodies. The first step is to define a joint definition where we set various properties, such as the bodies it connects, range restrictions, max torque etc. Once that is done, we use the world to create a joint from this definition. Too easy right ;-)

There are in total eight different joints, each serving a different purpose in Box2D. We will have a look at all of them. As with the last tutorial, I am following the manual in Box2D closely, so for more detailed descriptions it is best to have a look at it.

Mouse Joint

The first joint that we will look at will be the mouse joint. The mouse joint simply allows us to click on, and manipulate the bodies in the world. This is useful for interacting with the Box2D world, so we will learn about it first.

The mouse joint is actually the hardest to set up since we have to write a lot more code for it to work compared to the other joints. Fortunately, the Test.as file in the TestBed folder under examples in the Box2D zip provides us with this code:

protected var _mouseJoint:b2MouseJoint;
protected var _input:Input;
protected var _mouseXWorldPhys:Number;
protected var _mouseYWorldPhys:Number;
protected var _mouseXWorld:Number;
protected var _mouseYWorld:Number;
protected var _mousePVec:b2Vec2 = new b2Vec2();

protected function update(e:Event):void
{
	var timeStep:Number = 1 / 30;
	var velocityIterations:int = 6;
	var positionIterations:int = 2;

	UpdateMouseWorld();
	MouseDestroy();
	MouseDrag();

	_world.Step(timeStep, velocityIterations, positionIterations);
	_world.ClearForces();
	_world.DrawDebugData();
	General.Input.update();
}

protected function GetBodyAtMouse(includeStatic:Boolean = false):b2Body
{
	_mousePVec.Set(_mouseXWorldPhys, _mouseYWorldPhys);
	var aabb:b2AABB = new b2AABB();
	aabb.lowerBound.Set(_mouseXWorldPhys - 0.001, _mouseYWorldPhys - 0.001);
	aabb.upperBound.Set(_mouseXWorldPhys + 0.001, _mouseYWorldPhys + 0.001);
	var body:b2Body = null;
	var fixture:b2Fixture;

	// Query the world for overlapping shapes.
	function GetBodyCallback(fixture:b2Fixture):Boolean
	{
	var shape:b2Shape = fixture.GetShape();
		if (fixture.GetBody().GetType() != b2Body.b2_staticBody || includeStatic)
		{
			var inside:Boolean = shape.TestPoint(fixture.GetBody().GetTransform(), _mousePVec);
			if (inside)
			{
				body = fixture.GetBody();
				return false;
			}
		}
		return true;
	}
	_world.QueryAABB(GetBodyCallback, aabb);
	return body;
}

protected function MouseDestroy():void
{
	if (!Input.mouseDown && Input.isKeyPressed(68/*D*/))
	{
		var body:b2Body = GetBodyAtMouse(true);

		if (body)
		{
			_world.DestroyBody(body);
			return;
		}
	}
}

protected function MouseDrag():void
{
	if (Input.mouseDown && !_mouseJoint)
	{
		var body:b2Body = GetBodyAtMouse();

		if (body)
		{
			var md:b2MouseJointDef = new b2MouseJointDef();
			md.bodyA = _world.GetGroundBody();
			md.bodyB = body;
			md.target.Set(_mouseXWorldPhys, _mouseYWorldPhys);
			md.collideConnected = true;
			md.maxForce = 300.0 * body.GetMass();
			_mouseJoint = _world.CreateJoint(md) as b2MouseJoint;
			body.SetAwake(true);
		}
	}

	if (!Input.mouseDown)
	{
		if (_mouseJoint)
		{
			_world.DestroyJoint(_mouseJoint);
			_mouseJoint = null;
		}
	}

	if (_mouseJoint)
	{
		var p2:b2Vec2 = new b2Vec2(_mouseXWorldPhys, _mouseYWorldPhys);
		_mouseJoint.SetTarget(p2);
	}
}

	protected function UpdateMouseWorld():void
	{
		_mouseXWorldPhys = (Input.mouseX) / PIXELS_TO_METRE;
		_mouseYWorldPhys = (Input.mouseY) / PIXELS_TO_METRE;

		_mouseXWorld = (Input.mouseX);
		_mouseYWorld = (Input.mouseY);
}

You are required to create an instance of the Input class since later on we call some static functions that rely on it being instantiated first. The original source code requires you to create a MovieClip that is attached to stage and pass it in as a reference. I did not really like this and would rather just pass in a reference to stage directly so I have modified the class a bit (see the source code file at the bottom).

public function MouseJointTutorial()
{
	addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}

private function onAddedToStage(e:Event):void
{
	init();
	setup();
}

private function init():void
{
    //... other code. See end of blog for complete source code
    _input = new Input(stage);
    addEventListener(Event.ENTER_FRAME, update);
}

For this reason, all the code we write now will be triggered only once the class has been added to stage so that we don’t pass in a null reference to the Input class.

I won’t go through all the code, needless to say on each update frame it detects to see if the mouse is being pressed on a body, and if so, creates a mouse joint on it and applies a force to the body. It also cleans up after itself too avoid wasting memory.
So now you should be able to manipulate a box and see the physics engine at work!

Distance Joint

The next joint we will look at is the distance joint. It is pretty simple to create. All it does is maintain a distance between two bodies. For each of the two bodies we specify the anchor point in world coordinates. FYI wherever you see box1, box2 etc – those are dynamic bodies. _groundBody which I use later on is a static body.


var distanceJointDef:b2DistanceJointDef = new b2DistanceJointDef();
distanceJointDef.Initialize(box1, box2, box1.GetWorldCenter(), box2.GetWorldCenter());
_world.CreateJoint(distanceJointDef);

Rope Joint

Following on from the Distance Joint we have the Rope Joint. The Rope Joint is very similar to the distance joint. Both enforce a maximum distance between two bodies. The difference however is that there is no minimum distance that is enforced between the two bodies. This makes the joint suitable for using as a rope.

var ropeJointDef:b2RopeJointDef = new b2RopeJointDef();
ropeJointDef.bodyA = box1;
ropeJointDef.bodyB = box2;
ropeJointDef.localAnchorA = new b2Vec2(0,0);
ropeJointDef.localAnchorB = new b2Vec2(0,0);
ropeJointDef.maxLength = 6;
ropeJointDef.collideConnected = true;
_world.CreateJoint(ropeJointDef);

Revolute Joint

The next joint is the revolute joint. I find this one pretty useful. We attach this joint to two bodies. If one is a static body and the other is a dynamic body then the dynamic body will spin as if someone nailed a paper plate to a wall. Two dynamic bodies can also be attached together and will rotate around each other. There are various properties we can set such as limiting the range to revolve around. One thing you might like to do is add some friction to the joint. If you just create a bare bones revolute joint then if something causes it spin then it will never stop. By enabling the motor and setting a max torque to something like 1.0, then it will eventually come to a rest thus resembling friction.

var revoluteJointDef:b2RevoluteJointDef = new  b2RevoluteJointDef();
revoluteJointDef.Initialize(box1, _groundBody, box1.GetWorldCenter());

revoluteJointDef.maxMotorTorque = 1.0;
revoluteJointDef.enableMotor = true;

_world.CreateJoint(revoluteJointDef);

Prismatic Joint

The prismatic joint allows only one degree of freedom. For example, if we attached a dynamic body to a static body with a prismatic joint then we can slide it along an axis (similar to the moving levels in a platformer). If we attached a dynamic body to another dynamic body, then you will notice that both boxes rotate relative to each other.


var worldAxis:b2Vec2 = new b2Vec2(1.0, 0.0);

var prismaticJointDef:b2PrismaticJointDef = new b2PrismaticJointDef();
prismaticJointDef.Initialize(box1, _groundBody, box1.GetWorldCenter(), worldAxis);
prismaticJointDef.lowerTranslation = -5.0;
prismaticJointDef.upperTranslation = 2.5;
prismaticJointDef.enableLimit = true;
prismaticJointDef.maxMotorForce = 1.0;
prismaticJointDef.motorSpeed = 0.0;
prismaticJointDef.enableMotor = true;

_world.CreateJoint(prismaticJointDef);

Pulley Joint

Next up is the pulley joint. As the name suggests it is used for creating a pulley system. You can specify a ratio of how far one side changes compared to the other. The pulley does take a bit of tweaking to get it set up correctly for how you want it to function.


var anchor1:b2Vec2 = box1.GetWorldCenter();
var anchor2:b2Vec2 = box2.GetWorldCenter();

var groundAnchor1:b2Vec2 = new b2Vec2(anchor1.x, anchor1.y - (300 / PIXELS_TO_METRE)); var groundAnchor2:b2Vec2 = new b2Vec2(anchor2.x, anchor2.y - (300 / PIXELS_TO_METRE));

var ratio:Number = 1.0;

var pulleyJointDef:b2PulleyJointDef = new b2PulleyJointDef();
pulleyJointDef.Initialize(box1, box2, groundAnchor1, groundAnchor2, anchor1, anchor2, ratio);
pulleyJointDef.maxLengthA = 600 / PIXELS_TO_METRE;
pulleyJointDef.maxLengthB = 600 / PIXELS_TO_METRE;

_world.CreateJoint(pulleyJointDef);

Gear Joint

Gears are the next joint we will look at. A gear could be created by making a body in the shape of a gear and applying a motor, but a gear joint will be much more efficient and simpler to set up. Gear joints are created by using a mixture of either revolute or prismatic joints connected to a static body. As one body changes it subsequently affects the other body. Note, that in the initialization function the static body must be the first parameter .


var revoluteJointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
revoluteJointDef.Initialize(_groundBody, box1, box1.GetWorldCenter());  //ground must be the first body
revoluteJointDef.lowerAngle = -0.5 * b2Settings.b2_pi; // -90 degrees
revoluteJointDef.upperAngle = 0.25 * b2Settings.b2_pi; // 45 degrees
revoluteJointDef.enableLimit = true;
revoluteJointDef.maxMotorTorque = 10.0;
revoluteJointDef.motorSpeed = 0.0;
revoluteJointDef.enableMotor = true;

var revoluteJoint:b2RevoluteJoint = _world.CreateJoint(revoluteJointDef) as b2RevoluteJoint;
var worldAxis:b2Vec2 = new b2Vec2(1.0, 0.0);

var prismaticJointDef:b2PrismaticJointDef = new b2PrismaticJointDef();
prismaticJointDef.Initialize(_groundBody, box2, box2.GetWorldCenter(), worldAxis);  //ground must be the first body
prismaticJointDef.lowerTranslation = -5.0;
prismaticJointDef.upperTranslation = 2.5;
prismaticJointDef.enableLimit = true;
prismaticJointDef.maxMotorForce = 1.0;
prismaticJointDef.motorSpeed = 0.0;
prismaticJointDef.enableMotor = true;

var prismaticJoint:b2PrismaticJoint = _world.CreateJoint(prismaticJointDef) as b2PrismaticJoint;

var gearJointDef:b2GearJointDef = new b2GearJointDef();

gearJointDef.joint1 = revoluteJoint;
gearJointDef.joint2 = prismaticJoint;
gearJointDef.bodyA = box1;
gearJointDef.bodyB = box2;
gearJointDef.ratio = 2.0 * b2Settings.b2_pi / (300 / PIXELS_TO_METRE);

_world.CreateJoint(gearJointDef);

Line Joint

If you are wanting to create a suspension for a model of a car then the line joint is what you will be after. It was created specifically for this purpose. It is like the prismatic joint but with the rotation restriction removed. For example, a wheel can slide along an axis (the shock absorber) while rotating. The spring portion of the shock absorber can be modelled by creating friction using the motor variables.


var worldAxis:b2Vec2 = new b2Vec2(0.0, 1.0);

var lineJointDef:b2LineJointDef = new b2LineJointDef();
/// Box2D source code:  A line joint. This joint provides one degree of freedom: translation along an axis fixed in body1. You can use a joint limit to restrict the range of motion and a joint motor to drive the motion or to model joint friction.
lineJointDef.Initialize(groundBody, box1, box1.GetWorldCenter(), worldAxis);
lineJointDef.lowerTranslation = -2.0;
lineJointDef.upperTranslation = 2.0;
lineJointDef.enableLimit = true;
lineJointDef.maxMotorForce = 200.0;
lineJointDef.motorSpeed = 10.0;
lineJointDef.enableMotor = true;

_world.CreateJoint(lineJointDef);

Weld Joint

Finally, we come to the last joint, the weld joint. The weld joint allows us to weld two bodies together. Pretty simple. If you are wanting to use it for setting up breakable structures then you should take note of the Box2D manual comment “It is tempting to use the weld joint to define breakable structures. However, the Box2D solver is iterative so the joints are a bit soft. So chains of bodies connected by weld joints will flex. Instead it is better to create breakable bodies starting with a single body with multiple fixtures. When the body breaks, you can destroy a fixture and recreate it on a new body.”


var weldJointDef:b2WeldJointDef = new b2WeldJointDef();
weldJointDef.Initialize(box1, box2, box1.GetWorldCenter());

 _world.CreateJoint(weldJointDef);

So that is the basics on creating different types of joints and their uses. You should now have learnt enough to start experimenting and making some cool worlds/contraptions. I have included the full source code files for creating each type of joint below. The next topic in this tutorial series will be custom shapes and textures. :)

Zipped project file here

  • Roachbody

    Allan , You Rock!!

  • Who Cares!

    Simply, You are AMAZING!!!!!!

  • Natanr123

    Hi allan this post was very helpfull But I had a problem when using the rope joint
    it happend when i tried to set the starting position of the joint exactly in center position of the body then there is no movement. I change the RopeJointTutorial.as to demonstrate the problem. Just replace the orignal code with this one and you can see that the rope joint sample doesnt work
    Can you help fix this problem

    package  { import Box2D.Dynamics.b2Body; import Box2D.Dynamics.Joints.b2RopeJointDef; import Box2D.Common.Math.*;
     /**  * @author Allan Bishop  */ public class RopeJointTutorial extends MouseJointTutorial {  override protected function setup():void  {   _world.SetGravity(new b2Vec2(0,0) );   var box1:b2Body = createBox(300, 300, 30, 30);   box1.SetFixedRotation(true);   //var box2:b2Body = createBox(450, 30, 30, 30);      var ropeJointDef:b2RopeJointDef = new b2RopeJointDef();     ropeJointDef.bodyA = box1;   //ropeJointDef.bodyB = box2;   ropeJointDef.bodyB = _world.GetGroundBody();
          ropeJointDef.localAnchorA = new b2Vec2(0,0);   ropeJointDef.localAnchorB = new b2Vec2(300/30,300/30);   ropeJointDef.maxLength = 6;   ropeJointDef.collideConnected = false;   _world.CreateJoint(ropeJointDef);  } }}

  • allanbishop

    I tried your code and there does seem to be a bug. For me, the box did not appear. However, changing the gravity back to 10 then worked. I then tried using a value of 0.0001 and that worked too. I will try to find out why gravity set to 0 causes the problem.

  • allanbishop

    Ok I have fixed the bug (well the bug I was experiencing anyway). Download the updated b2RopeJoint.as. Let me know if this solves the problem for you.

  • daniel

    Hey Allan!
    nice Turtorials!
    Im trying to build a seesaw, using RevoluteJoints, but the joint is starting to shake, when the hero jump on it. Do you have any idea for this?
    tnx
    daniel

  • allanbishop

    Thanks :) , when you are creating the joint are you setting the collideConnected to false with the JointDef? I forget if it is on or off by default but it would explain the shaking.

  • daniel

     Yep! Thank you very much! collideConnected= true, helped me a lot! No problem with gravity, shaking! But i have a new issue :)
    attached…check this :-)

  • allanbishop

    Ok it looks like at the time when you are creating the joint, the position of the beam on top of the fulcrum is not in the correct position. You need to ensure that they are overlapping properly when you create the joint (based on my understanding of the problem from the picture)

  • daniel

    Thanks Allan!
    Im not sure how to set this up properly! :S
    Both of the objects are at the same x and y position.
    this is acting weird.. :-)
    So actually what do you mean overlapping properly?

  • allanbishop

    Well it looks like the problem is that the joint is rotating but the plank is rotating around the fullcrum by a radius instead of having no radius (I have drawn what I mean)? If I am understanding the problem correctly then this happens if a joint is made when the two pieces (the plank and fullcrum) are not in place properly when the joint is added. There would have been a gap in the Y world co-ordinates. The centre of the plank needs to be overlapping the top of the fullcrum to make a T- shape and then the joint, when created on the centre of the plank, will “pin” onto the top of the fullcrum. That is all I can help with for now as it is 1:30am but I will check back in the morning.

  • daniel

    Thanks Allan! :)
    If you were going to make a seesaw, which joint type you would use? :-)
    i cant make this work by myself..

  • allanbishop

    You would want a B2RevoluteJoint. Create the fullcrum body and place on the ground as you have. Create a plank body and ensure the y position of it is equal the y position of the fullcrum + fullcrum.height/2. Then create your joint and pass in the plank.getworldcenter() as the position of it.

  • Pop-corm2

    very useful. thx!

  • http://twitter.com/gogorush Hou

    wonderful tutorial and nice examples 

  • Linuxtyh

    It really helps me,
    U R great
    Thank u very much~~~

    From China

  • Nikhilfreestyle

    Thanks for the tute. is there any way where you could make the second body of prismatic joint slide on first without the first one equally getting pushed back by it in a zero gravity field?
    I want my second body of prismatic joint to slide on first without affecting first one at all.