Physics Joints

Joints

Joints are used to assemble more complex game objects from multiple rigid bodes. For example, joints would be used to join the limbs of a ragdoll figure, or the wheels of a car.

Box2D includes a number of different joint types, each with different parameters and functions. Some joint types can be powered by motors, and others are subject to various physical restrictions in their ranges of motion.

The simplest joint type is the “pivot” joint, which joins two objects at a single pivot point, and is illustrated in the “Bridge” and “Chains” sample projects.

To construct a joint, first construct the bodies that will be joined, and then submit those bodies to the desired joint constructor function.

Pivot joint


A pivot joint (also known as a “revolute joint” in Box2D terms) joins two bodies at an overlapping point. The initial arguments are the two bodies joined, followed by an anchor point in the global coordinates of the world.

Pivot joints can optionally be constrained in their range of rotation. For example, if constructing a “ragdoll” figure, the head/neck joint would have a limited range of angular motion.

myJoint = physics.newJoint( "pivot", crateA, crateB, 200,300 )

Joint motors

myJoint.isMotorEnabled -- (boolean)
myJoint.motorSpeed
myJoint.motorTorque  -- (get-only)
myJoint.maxMotorTorque  -- (set-only)

By default, joint motors have a fairly weak maximum torque, and therefore setting motorSpeed may appear to have little visible effect. Therefore, you should generally set maxMotorTorque to a high value (such as 100000) if you are trying to move any significant mass, such as rotating a wheel to power a car.

Rotation limits

Pivot joints can optionally be constrained in their range of rotation. For example, if constructing a "ragdoll" figure, the head/neck joint would have a limited range of angular motion. These rotation limits are specified in degrees:

myJoint.isLimitEnabled = true -- (boolean)
 
myJoint:setRotationLimits( -45, 45 )
a1, a2 = myJoint:getRotationLimits()

Other attributes:

myJoint.jointAngle -- (get-only; value in degrees)
myJoint.jointSpeed -- (get only; value in degrees per second)

Distance joint


A distance joint joins two bodies at a fixed distance. The initial arguments are the two bodies joined, followed by an (x,y) anchor point for each body. Anchor points are declared in the world coordinates.

myJoint = physics.newJoint( "distance", crateA, crateB, crateA.x,crateA.y, crateB.x,crateB.y )

Other attributes:

myJoint.length
myJoint.frequency
myJoint.dampingRatio

length is the distance between the anchor points, which should not be zero or very short (you should use a pivot joint if you need to directly anchor one body to another).

frequency is the mass-spring damping frequency (in Hz)

dampingRatio ranges from 0, for no damping, to 1, for critical damping.

Piston joint


A piston joint (also known as a “prismatic joint” in Box2D terms) joins two bodies along a single, restricted axis of motion, like the pistons or a shock absorber in a car. The initial arguments are the two bodies joined, followed by an anchor point in the first body and a vector defining the axis along which motion is allowed.

myJoint = physics.newJoint( "piston", crateA, crateB, crateA.x,crateA.y, axisDistanceX,axisDistanceY )

(Note: at least one of the bodies should be dynamic, with non-fixed rotation.)

Joint motors

A piston joint may also be driven by a motor. Unlike the pivot joint, this motion will be linear, along the specified axis, rather than rotational.

myJoint.isMotorEnabled -- (boolean)
myJoint.motorSpeed -- (linear speed, in units of pixels per second)
myJoint.motorForce  -- (get-only)
myJoint.maxMotorForce  -- (set-only)

Motion limits

In addition, limits on its range of linear motion may be specified:

myJoint.isLimitEnabled = true -- (boolean)
 
myJoint:setLimits( 100, 200 )
p1, p2 = myJoint:getLimits()

Other attributes:

myJoint.jointTranslation -- (get-only; linear value in pixels)
myJoint.jointSpeed -- (get only; value in pixels per second)

Friction joint


A friction joint is a special kind of pivot joint that resists motion, and is therefore “sticky”:

myJoint = physics.newJoint( "friction", crateA, crateB, 200,300 )

It exposes only two attributes:

myJoint.maxForce
myJoint.maxTorque

Weld joint


A weld joint “welds” two bodies together at a specified point in world coordinates:

myJoint = physics.newJoint( "weld", crateA, crateB, 200,300 )

This joint does not move or rotate at all, and so it exposes no attributes.

However, due to mathematical approximation, this joint may appear slightly “soft” during motion, and so if you want to rigidly bond multiple shapes together, you may want to specify them as multiple body elements within a single complex body, rather than using weld joints.

Wheel joint


A wheel joint (also known as a “line joint” in Box2D terms) combines a piston and a pivot joint, like a wheel mounted on the shock absorber of a car. Most of its properties are inherited from the standard piston joint; the difference is that the body at the end of the axis is allowed to rotate freely.

myJoint = physics.newJoint( "wheel", crateA, crateB, crateA.x,crateA.y, axisDistanceX,axisDistanceY )

Joint motors

Like a piston joint, the “shock absorber” portion of a wheel joint may be driven by a joint motor. This motion will be linear, along the specified axis, rather than rotational.

myJoint.isMotorEnabled -- (boolean)
myJoint.motorSpeed -- (linear speed, in units of pixels per second)
myJoint.motorForce  -- (get-only)
-- myJoint.maxMotorForce  -- (not currently supported in Box2D library; known bug)

Motion limits

In addition, limits on its range of linear motion may be specified:

myJoint.isLimitEnabled = true -- (boolean)
 
myJoint:setLimits( 100, 200 )
p1, p2 = myJoint:getLimits()

Other attributes:

myJoint.jointTranslation -- (get-only; linear value in pixels)
myJoint.jointSpeed -- (get only; value in pixels per second)

Pulley joint


A pulley joint attaches two bodies with an imaginary rope whose length remains constant: if one body is pulled down, the other one will move up.

This constructor is a bit complicated, because it must specify a joint anchor point within each body, and also a stationary anchor point in world coordinates for each side of the “rope” to hang from. Finally, there is a “ratio” property to optionally simulate a block and tackle arrangement, in which one side of the rope moves faster than the other. By default, this ratio is 1.0, which simulates a simple pulley.

myJoint = physics.newJoint( "pulley", crateA, crateB, anchorA_x,anchorA_y, anchorB_x,anchorB_y, crateA.x,crateA.y, crateB.x,crateB.y, 1.0 )

It exposes the following read-only attributes:

myJoint.length1 -- (get-only; value in pixels)
myJoint.length2 -- (get-only; value in pixels)
myJoint.ratio -- (get-only)

Touch joint


A touch joint (based on the Box2D “mouse joint”) connects a single object to the current position of an onscreen touch. This connection is an elastic joint with specifiable strength and behavior, but since it is impossible to apply infinite forces to the simulation, the dragged object may "lag behind" a rapidly moving touch. However, this method allows far more realistic behavior than the non-physical drag method demonstrated in the earlier "DragPlatforms" sample.

For an example of touch joints, including their use with multitouch, see the "DebugDraw" sample code.

myJoint = physics.newJoint( "touch", crate, targetX,targetY )

The initial values of targetX and targetY should be given in world coordinates, but represent the target point on the object. For example, to drag the above object by its center point, you could do the following:

touchJount = physics.newJoint( "touch", crate, crate.x, crate.y )

Alternatively, to drag an object by the point it was touched on:

touchJount = physics.newJoint( "touch", crate, event.x, event.y )

...where the "event" is passed by a touch listener. This behavior is illustrated in the "DebugDraw" sample code.

Note: because the touch joint can follow any (x,y) values, it can also be used for things besides tracking a touch: for example, it could be used to make an object follow a path, if that path was specified as a series of (x,y) values. This should make it useful for Flight Control-style line following applications, among other things.

In general, a touch joint creates a temporary elastic joint between the object and your finger, which makes the object attempt to follow the touch. But since the object remains under simulation, its motion may be stopped by other solid objects, and will interact fully with the other bodies in the world. Also, the object will rotate realistically under gravity when "picked up by one end". (To avoid this behavior, you can connect the touch joint to the object's center of mass rather than to the specific point on the object that you touched.)

Joint target

The touch-following behavior of touch joints is created by passing the current touch (x,y) position to the joint:

myJoint:setTarget( targetX, targetY )

While the updated (targetX, targetY) values will normally come from a touch, any values can be passed. For example, the object could be made to follow another object, or track the points of a path.

The current target values can also be accessed:

targetX, targetY = myJoint:getTarget()

Maximum force

The overall speed or "lag" of the joint depends on the force exerted:

myJoint.maxForce = 10000
force = myJoint.maxForce

By default, this attribute is set to 1000 times the mass of the body, which allows for fairly rapid dragging.

Frequency

The mass-spring damping frequency of the elastic joint (in Hz):

myJoint.frequency = 50
frequency = myJoint.frequency

Damping ratio

The damping ratio of the elastic joint, ranging from 0, for no damping, to 1, for critical damping:

myJoint.dampingRatio = 0.2
damping = myJoint.dampingRatio

The above attributes behave similarly to their counterparts in the Distance joint (see above). As in that case, the default values should work fairly well, but you may want to experiment with other values.

Dragging objects with touch joints


Here is a general function for making a physics object draggable, as seen in the "DebugDraw" sample code:

local physics = require("physics")
physics.start()
system.activate( "multitouch" )
 
-- A general function for dragging physics bodies
local function dragBody( event )
        local body = event.target
        local phase = event.phase
        local stage = display.getCurrentStage()
 
        if "began" == phase then
                stage:setFocus( body, event.id )
                body.isFocus = true
 
                -- Create a temporary touch joint and store it in the object for later reference
                body.tempJoint = physics.newJoint( "touch", body, event.x, event.y )
 
        elseif body.isFocus then
                if "moved" == phase then
                
                        -- Update the joint to track the touch
                        body.tempJoint:setTarget( event.x, event.y )
 
                elseif "ended" == phase or "cancelled" == phase then
                        stage:setFocus( body, nil )
                        body.isFocus = false
                        
                        -- Remove the joint when the touch ends                 
                        body.tempJoint:removeSelf()
                        
                end
        end
 
        -- Stop further propagation of touch event
        return true
end
 
-- Add a physics object
crate = display.newImage( "crate.png" )
physics.addBody( crate, { density=0.8 } )
 
-- Make object draggable
crate:addEventListener( "touch", dragBody )

Common features of all joints


In addition to the above, all joint types share several common attributes and functions:

myJoint:getAnchorA
myJoint:getAnchorB

A function that returns the (x,y) coordinates of the joint’s anchor points within objects A and B, which are the two joined objects. The values are given in the local coordinates of each object, so an anchor point at the center of the object would be at position (0, 0).

x, y = myJoint:getAnchorA()
x, y = myJoint:getAnchorB()

myJoint:getReactionForce

A function that returns the reaction force (in Newtons) at the joint anchor in the second body.

reactionForceX, reactionForceY = myJoint:getReactionForce()

myJoint.reactionTorque

A read-only property that returns the reaction torque (in N*m) at the joint anchor in the second body.

reactionTorque = myJoint.reactionTorque

Destroying joints


To destroy an existing joint, and detach the two bodies (which may then collide), use removeSelf():

myJoint:removeSelf()

Replies

Viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
klingk
User offline. Last seen 2 weeks 3 hours ago. Offline
Joined: 21 Feb 2010

There is no removeSelf() on "weld" joints....spent a bunch of time trying to remove a weld joint, throws an error that removeSelf is nil on the object.
Changed it to a "pivot" and it worked fine, just need to figure out how to make that work like a "Weld".
Assuming this is a bug.

-update- 01/27/2011 I reported this on the bug list, not sure if it was fixed in the >243 release. I was also able to get the effect I wanted with the pivot by constraining the range of motion and it seems to be performing fine, will dig out older code with welds and see if it's fixed.

horacebury's picture
horacebury
User offline. Last seen 2 hours 27 min ago. Offline
Joined: 17 Aug 2010

Some sample code showing the various joints in effect would be great here. I thought this very simple code would force the platform body to only move horizontally...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
local physics = require( "physics" )
physics.start()
physics.setGravity( 0, 10 )
physics.setDrawMode( "hybrid" ) -- normal, hybrid, debug
 
local anchor = display.newCircle( 200, 100, 50 )
physics.addBody( anchor, "static", { friction=.1, bounce=.1, density=.1, radius=50 } )
 
local platform = display.newRect( 0, 0, 100, 100 )
physics.addBody( platform, "dynamic", { friction=.1, bounce=.1, density=.1 } )
platform.isFixedRotation = true
platform.x, platform.y = 200, 300
 
local joint = physics.newJoint( "piston", anchor, platform, anchor.x, anchor.y, 5, 0 )

I also tried the same with images...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- start physics
local physics = require( "physics" )
physics.start()
physics.setGravity( 0, 10 )
physics.setDrawMode( "normal" ) -- normal, hybrid, debug
 
--local anchor = display.newCircle( 100, 100, 50 )
local anchor = display.newImage( "img.png" )
anchor.x, anchor.y = 100, 100
physics.addBody( anchor, "static", { friction=.1, bounce=.1, density=.1 } )
 
--local platform = display.newRect( 0, 0, 100, 100 )
local platform = display.newImage( "img.png" )
physics.addBody( platform, "dynamic", { friction=.1, bounce=.1, density=.1 } )
platform.isFixedRotation = true
platform.x, platform.y = 200, 100
 
-- apply joint to make platform only move sideways
local joint = physics.newJoint( "piston", anchor, platform, anchor.x, anchor.y, .5, 0 )

No luck.

corona.20.mainzel
User offline. Last seen 1 year 9 weeks ago. Offline
Joined: 18 Mar 2011

My pivot joint wont work. I do not understand why. An overlapping is impossible to accomplish as the objects are lying on top of each other.

horacebury's picture
horacebury
User offline. Last seen 2 hours 27 min ago. Offline
Joined: 17 Aug 2010

You haven't posted any code!

kkwars
User offline. Last seen 16 weeks 4 days ago. Offline
Joined: 31 Jan 2011

Hi,

I also tried creating a piston joint without succes,
can someone explain why the code posted by horacebury doesn't work?

I need to create a joint between a dynamic objet and an anchor and make it wave like a spring, can someone please give some help?

edgar86m
User offline. Last seen 5 weeks 1 hour ago. Offline
Joined: 7 Jan 2011

If anyone is having trouble understanding the joints, the original Box2d manual describes the joints with illustrations...

http://www.box2d.org/manual.html

bogeybox
User offline. Last seen 2 hours 29 min ago. Offline
Joined: 27 Jul 2011

@edgar86m , the correct link is http://www.box2d.org/manual.pdf

not .html :)

at least here, .html is broken

mobox
User offline. Last seen 3 weeks 1 day ago. Offline
Joined: 1 Feb 2012

Does corona support the box2d rope joint?

horacebury's picture
horacebury
User offline. Last seen 2 hours 27 min ago. Offline
Joined: 17 Aug 2010

In theory, the distance joint should do the trick in Corona, however having just read:

http://box2d.org/manual.pdf

I believe it might be wise to add the rope joint.

Tom
User offline. Last seen 2 hours 6 min ago. Offline
Staff
Joined: 13 Jul 2010

Corona doesn't currently support Rope Joints. That is only available in the updated Box2d library (which we haven't implemented yet). We don't have a time frame for when it will be updated in Corona.

jacques1's picture
jacques1
User offline. Last seen 3 weeks 1 day ago. Offline
Joined: 2 Jun 2011

I really think it is about time that we get some proper documentation and samples on how to make ALL the joints work.

As far as i can tell form what i have read and experimented, the wheel joints still don't work among other joints.

I also don't think we should be going through the box2d manual to try and figure out how to make the joints work.

So it would be good to get some proper code samples and help from the corona staff as its been more then a year and i still don't see anyone with solutions to the joint issues.

Ok, after quite a bit of trial and error, this does seem to work to get the wheels moving (the car is on a slope in my test) and the wheels pull the body along down the slope.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
local assetPath = "assets/"
local physics = require("physics")
physics.start()
physics.setScale( 60 )
physics.setGravity( 0, 10)
local objects = display.newGroup();objects:translate( halfW, halfH)
local ground=display.newImage(assetPath .. "ground.png",true );objects:insert(ground,true);ground.x=0;ground.y=300;ground.rotation=-2
physics.addBody( ground,"static", {density = 20, friction = 1} )
local car=display.newGroup();objects:insert(car,true)
local wheel1=display.newImage(assetPath .. "wheel_1.png",true );
car:insert(wheel1,true);wheel1.x=-20;wheel1.y=25
physics.addBody( wheel1, {density = 100, friction = 0.1, bounce = 0.0,radius=10} )
local wheel2=display.newImage(assetPath .. "wheel_1.png",true )
car:insert(wheel2,true);wheel2.x=20;wheel2.y=25
physics.addBody( wheel2, {density = 100, friction = 0.1, bounce = 0.0,radius=10} )
body=display.newImage(assetPath .. "robot_1.png" );
car:insert(body,true);body.y=0;body.y=0
physics.addBody(body,{density = 10, friction = 0.1, bounce = 0})
myJoint = physics.newJoint( "wheel", wheel1, body, wheel1.x,wheel1.y, 0,-10)
-- Right now i don't see much difference with using a -10 or a 10 for the yaxis movement.
-- I left the x-axis as 0 because i don't want the wheels to shift horizontally.
myJoint.isLimitEnabled = true
myJoint:setLimits( 0, 10 )
myJoint2 = physics.newJoint( "wheel", wheel2, body, wheel2.x,wheel2.y,0,-10) 
myJoint2.isLimitEnabled = true 
myJoint2:setLimits( 0, 10 )

Seems the wheel joint does work after all this way, my bad. And looks like the shock absorber effect works too. ( i suppose adjust the y-axis numbers and the limits numbers higher would give more 'bounce' to the absorbers.

Also the density of your objects would play a part in this too. I notice if the wheels density is not at least 5 times more then the body the body sinks into the wheels and touches the ground.
Do give some feedback for the best optimal 'car/wheel' performance, thanks.

jacques1's picture
jacques1
User offline. Last seen 3 weeks 1 day ago. Offline
Joined: 2 Jun 2011

I did notice one issue though, if i create a second 'car' (the variables are named differently) the moment i try create more wheel joints then corona crashes.

If i disable the wheel joints of car 1 and enable car2 wheel joints then car2 works fine, so its not the order or coding, it has to do with adding more joints. Please advise.

After testing using 3 wheels, it seems to be that corona crashes the moment more then 2 wheel joints are in the code.

Okay seems to be due to using setLimits on the joints, without using those there aren't anymore crashes. (.isLimitEnabled=true should still be set for each joint though).