Table of Contents
25. Module box2d¶
Box 2D physics is an open source C++ engine for simulating rigid bodies in 2D
.
The library Box 2D is available as a plugin/module.
The version and status of box2d can be found here.
Note
Important
function onTouchDown(key,x,y) local box2d_scale = tPhysic:getScale() x,y = mbm.to2dw(x,y) x,y = x / box2d_scale, y / box2d_scale -- box2d x,y world can be used now. end
25.1. box2d Methods¶
25.1.1. box2d getVersion¶
require "box2d"
version = box2d:getVersion()
print(version)
25.1.2. box2d new¶
- new(number * gravity_x, number * gravity_y number * scale_box_2d, number * velocityIterations, number * positionIterations, number * multiplyStep)¶
Create a new instance of a box2d.
- Parameters
number – gravity in the axis x, default is
0.0
.number – gravity in the axis y, default is
-90.8
.number – scale to apply in the world default is
10
.number – velocity iterations default is
10
.number – position iterations default is
3
.number – multiply step default is
1
.
- Returns
box2d table.
Example:
require "box2d"
local gravity_x = 0
local gravity_y = -90.8
local scale_box_2d = 10
local velocityIterations = 10
local positionIterations = 3
local multiplyStep = 1
tPhysic = box2d:new(gravity_x,gravity_y,scale_box_2d,velocityIterations,positionIterations,multiplyStep)
print('Gravity :', tPhysic:getGravity())
print('Scale :', tPhysic:getScale())
print('Velocity iteration :', tPhysic:getVelocityIterations())
print('Position iteration :', tPhysic:getPositionIterations())
print('Multiply step :', tPhysic:getMultiply())
Other example:
require "box2d"
tPhysic = box2d:new() --no arguments needed (same as before)
print('Gravity :', tPhysic:getGravity())
print('Scale :', tPhysic:getScale())
print('Velocity iteration :', tPhysic:getVelocityIterations())
print('Position iteration :', tPhysic:getPositionIterations())
print('Multiply step :', tPhysic:getMultiply())
25.1.3. box2d contact listener¶
When bodies move around in the physics scene and bounce off each other, Box 2D will handle all the necessary collision detection and response so you don’t need to worry about that.
But the whole point of making a physics game is that occasionally something should happen in the game as a result of some of the bodies hitting each other eg. when the player touches a monster he should die, or a ball bouncing on the ground should make a sound etc.
A way to get this information back from the physics engine is using this abstracted contact listener implemented by the engine.
There are four callbacks to handle collision detection in Box 2D and the engine implemented them all (abstracted way).
- BeginContact
Called when two fixtures begin to touch. It has the following signature:
function onBeginContact(tMesh_a, tMesh_b) end
- EndContact
Called when two fixtures cease to touch. It has the following signature:
function onEndContact(tMesh_a, tMesh_b) end
- PreSolve
Called several times per step. Is called after a contact is updated. It has the following signature:
function onPreSolve(tMesh_a, tMesh_b, tManifold) end
Note
This is called only for awake bodies.This is called even when the number of contact points is zero.This is not called for sensors.If you set the number of contact points to zero, you will not get an EndContact callback. However, you may get a BeginContact callback the next step.See manifold table for more information.
- PostSolve
Called several times per step. This lets you inspect a contact after the solver is finished. This is useful for inspecting impulses. It has the following signature:
function onPostSolve(tMesh_a, tMesh_b, tImpulse) end
Note
This is only called for contacts that are touching, solid, and awake.The b2ContactImpulse used by the engine has the following members:
tImpulse = { count = 0, normalImpulses = {[1] = 0, [2] = 0}, tangentImpulses = {[1] = 0, [2] = 0}}
- setContactListener(function onBeginContact, function onEndContact, function onPreSolve, function onPostSolve)¶
- Parameters
function – onBeginContact callback (may be
nil
).function – onEndContact callback (may be
nil
).function – onPreSolve callback (may be
nil
).function – onPostSolve callback (may be
nil
).
Example:
require "box2d"
function print_manifold(tManifold)
print('type:',tManifold.type)
print('pointCount:',tManifold.pointCount)
print('localNormal.x:',tManifold.localNormal.x)
print('localNormal.x:',tManifold.localNormal.y)
print('localPoint.x:',tManifold.localPoint.x)
print('localPoint.x:',tManifold.localPoint.y)
for i =1, #tManifold.points do
print('point:',i)
print('\tlocalPoint.x:',tManifold.points[1].localPoint.x)
print('\tlocalPoint.y:',tManifold.points[1].localPoint.y)
print('\tnormalImpulse:',tManifold.points[1].normalImpulse)
print('\ttangentImpulse:',tManifold.points[1].tangentImpulse)
end
print('\n')
end
-- Callbacks
function onBeginContact(tMesh_a, tMesh_b)
print('\n')
print('Begin contact:')
print('Collision:',tMesh_a.name,tMesh_b.name)
end
function onEndContact(tMesh_a, tMesh_b)
print('\n')
print('End contact:')
print('Collision:',tMesh_a.name,tMesh_b.name)
end
function onPreSolve(tMesh_a, tMesh_b, tManifold)
print('\n')
print('onPreSolve:')
print('Collision:',tMesh_a.name,tMesh_b.name)
print_manifold(tManifold)
end
function onPostSolve(tMesh_a, tMesh_b, tImpulse)
print('\n')
print('Impulse:')
print('Impulse.count:', tImpulse.count)
print('Impulse.normalImpulses[1]:', tImpulse.normalImpulses[1])
print('Impulse.normalImpulses[2]:', tImpulse.normalImpulses[2])
print('Impulse.tangentImpulses[1]:', tImpulse.tangentImpulses[1])
print('Impulse.tangentImpulses[2]:', tImpulse.tangentImpulses[2])
end
-- End callbacks
mbm.setColor(1,1,1) --set background color to white
tPhysic = box2d:new()
tShapeQuad = shape:new('2dw',-10, 500)
tShapeCircle = shape:new('2dw',50 , 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeBigCircle = shape:new('2dw',200,800)
tShapeQuad.name = 'rectangle'
tShapeCircle.name = 'circle'
tShapeBigCircle.name = 'big circle'
tShapeGround.name = 'ground'
tShapeQuad:create('rectangle',100,100)
tShapeCircle:create('circle',100,100)
tShapeBigCircle:create('circle',200,200)
tShapeGround:create('rectangle',1000,20)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addDynamicBody(tShapeBigCircle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:setContactListener(onBeginContact,onEndContact,onPreSolve,onPostSolve)
camera = mbm.getCamera('2d')
camera.y = 300
25.1.4. box2d gravity¶
- getGravity¶
Return gravity used by box2d.
- Returns
number
gravity x,number
gravity y - of box2d.
Example:
require "box2d"
tPhysic = box2d:new()
print('Gravity :', tPhysic:getGravity())
- setGravity(number gravity_x, number gravity_y)¶
Set a new gravity to box2d.
- Parameters
number – gravity in the axis x.
number – gravity in the axis y.
Example:
require "box2d"
tPhysic = box2d:new()
tPhysic:setGravity(-3,95.8)
25.1.5. box2d iterations¶
- getVelocityIterations¶
Return the velocity iterations used by box2d.
- Returns
number
velocity iterations - of box2d.
Example:
require "box2d"
tPhysic = box2d:new()
print('Velocity iteration :', tPhysic:getVelocityIterations())
- getPositionIterations¶
Return the position iterations used by box2d.
- Returns
number
position iterations - of box2d.
Example:
require "box2d"
tPhysic = box2d:new()
print('Velocity iteration :', tPhysic:getPositionIterations())
25.1.6. box2d manifold¶
A manifold table has the following structure:
tManifold = {
type = 'circles' or 'face_a' or 'face_b',
pointCount = 0,
localNormal = {x = 0, y = 0 },
localPoint = {x = 0, y = 0 },
points = {
[1] = {
localPoint = {x = 0, y = 0 },
normalImpulse = 0,
tangentImpulse = 0
},
[2] = {
localPoint = {x = 0, y = 0 },
normalImpulse = 0,
tangentImpulse = 0
},
}
- getManifolds(tBody, boolean * checkIsTouching, boolean * checkIsEnabled)¶
- param renderizable
body.
- param boolean
checkIsTouching instruct to check b2Contact::IsTouching() from Box2d (default if
false
).- param boolean
checkIsEnabled instruct to check b2Contact::IsEnabled() from Box2d (default if
false
).- return
table
- manifold array.
Example:
--this is not a real example
function print_manifold(tManifold)
print('type:', tManifold.type)
print('pointCount:', tManifold.pointCount)
print('localNormal.x:', tManifold.localNormal.x)
print('localNormal.x:', tManifold.localNormal.y)
print('localPoint.x:', tManifold.localPoint.x)
print('localPoint.x:', tManifold.localPoint.y)
for i =1, #tManifold.points do
print('point:', i)
print('\tlocalPoint.x:', tManifold.points[1].localPoint.x)
print('\tlocalPoint.y:', tManifold.points[1].localPoint.y)
print('\tnormalImpulse:', tManifold.points[1].normalImpulse)
print('\ttangentImpulse:', tManifold.points[1].tangentImpulse)
end
print('\n')
end
--when is colliding otherwise will not have manifold.
local tManifolds = tPhysic:getManifolds(tMesh_a)
for i =1, #tManifolds do
print('\n')
print('Manifold index:',i)
print_manifold(tManifolds[i])
end
- setManifolds(tManifolds)¶
- param table
manifolds previously got using getManifolds method.
Example:
require "box2d"
-- Callbacks
function onPreSolve(tMesh_a, tMesh_b, tManifold)
local tManifolds = tPhysic:getManifolds(tMesh_a)
for i =1, #tManifolds do
print('tManifold index ', i)
-- change something in the manifold
end
tPhysic:setManifolds(tMesh_a,tManifolds)
end
-- End callbacks
mbm.setColor(1,1,1) --set background color to white
tPhysic = box2d:new()
tShapeQuad = shape:new('2dw',-10, 500)
tShapeCircle = shape:new('2dw',50 , 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeBigCircle = shape:new('2dw',200,800)
tShapeQuad.name = 'rectangle'
tShapeCircle.name = 'circle'
tShapeBigCircle.name = 'big circle'
tShapeGround.name = 'ground'
tShapeQuad:create('rectangle',100,100)
tShapeCircle:create('circle',100,100)
tShapeBigCircle:create('circle',200,200)
tShapeGround:create('rectangle',1000,20)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addDynamicBody(tShapeBigCircle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:setContactListener(nil,nil,onPreSolve,nil)
Important
Do not modify the manifold unless you understand the internals of Box2D.
25.1.7. box2d world manifold¶
A manifold world table has the following structure:
tWorldManifold = { normal = {x = 0, y = 0 },
separations = [1] = 0, [2] = 0,
points = {[1] = {x = 0, y = 0},
[2] = {x = 0, y = 0}}
- getWorldManifold(tBody)¶
- param renderizable
body.
- return
table
- world manifold array.
Example:
require "box2d"
function print_manifold_world(tWorldManifold)
print('normal.x:',tWorldManifold.normal.x)
print('normal.x:',tWorldManifold.normal.y)
print('separations[1]:',tWorldManifold.separations[1])
print('separations[2]:',tWorldManifold.separations[2])
for i =1, #tWorldManifold.points do
print('point:',i)
print('\tx:',tWorldManifold.points[1].x)
print('\ty:',tWorldManifold.points[1].y)
end
print('\n')
end
-- Callbacks
function onPreSolve(tMesh_a, tMesh_b, tManifold)
local tWorldManifolds = tPhysic:getWorldManifolds(tMesh_a)
for i =1, #tWorldManifolds do
print('WorldManifold index ', i)
print_manifold_world(tWorldManifolds[i])
end
end
-- End callbacks
mbm.setColor(1,1,1) --set background color to white
tPhysic = box2d:new()
tShapeQuad = shape:new('2dw',-10, 500)
tShapeCircle = shape:new('2dw',50 , 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeBigCircle = shape:new('2dw',200,800)
tShapeQuad.name = 'rectangle'
tShapeCircle.name = 'circle'
tShapeBigCircle.name = 'big circle'
tShapeGround.name = 'ground'
tShapeQuad:create('rectangle',100,100)
tShapeCircle:create('circle',100,100)
tShapeBigCircle:create('circle',200,200)
tShapeGround:create('rectangle',1000,20)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addDynamicBody(tShapeBigCircle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:setContactListener(nil,nil,onPreSolve,nil)
camera = mbm.getCamera('2d')
camera.y = 300
25.1.8. box2d multiply¶
multiply
parameter manipulates it doing literally a multiplication.world->Step(delta * multiplyStep, velocityIterations, positionIterations);
- getMultiply¶
Return the multiply step used by box2d.
- Returns
number
multiply step - of box2d.
Example:
require "box2d"
tPhysic = box2d:new()
print('Multiply step :', tPhysic:getMultiply())
- setMultiply(number multiply_step)¶
Set a new multiply step to box2d.
- Parameters
number – multiply step to the box2d, default is
1.0
.
Example:
require "box2d"
tPhysic = box2d:new()
tPhysic:setMultiply(2)
25.1.9. box2d scale¶
The engine provide an internal scale to be able to adapt to any size of objects.
The default value is
10
.
Here what the engine does internally to get values:
for(auto mesh : box_2d->all_meshes)
{
auto body = mesh->body;
if(body->typePhysics != b2_staticBody)
{
const b2Vec2 pos = body->GetPosition();
mesh->position.x = pos.x * box_2d->scale;
mesh->position.y = pos.y * box_2d->scale;
mesh->angle.z = body->GetAngle();
}
}
Here what the engine does internally to set values:
auto body = mesh->body;
const float scalePercentage = 1.0f / box_2d->scale;
const b2Vec2 position(mesh->position.x * scalePercentage, mesh->position.y * scalePercentage);
body->SetTransform(position,mesh->angle.z);
body->SetAwake(true);
Note
Setting scale to 1
makes the original behavior from box2d.
It means that if you suspect that this scale is interfering in yours result then just set it to 1
.
require "box2d"
tPhysic = box2d:new()
print('Scale :', tPhysic:getScale())
require "box2d"
tPhysic = box2d:new()
tPhysic:setScale(20)
25.1.10. box2d pause¶
require "box2d"
tPhysic = box2d:new()
tPhysic:pause()
25.1.11. box2d ray cast¶
Ray casting is often used to find out what objects are in a certain part of the world. A ray is just a straight line, and you can use a function provided by box2d to check if the line crosses a body. You can also find out what the
normal
is at the point the line hits the body.The points
x_start
,y_start
andx_end
,y_end
are used to define a start, end and direction for the ray, and the max fraction specifies how far along the ray should be checked for an intersection.The following image may make this clearer.
Remark about fraction:
A fraction of 0 means the start of line, fraction of 1 means the end of line and in this figure, the fraction of 2 would intersect the shape.
A ray cast have the following signature:
function onRayCast(tMesh,x,y,nx,ny,fraction)
return fraction -- Zero means to end the ray cast
end
Note
If you return 0 the ray cast is ended by box2d
- rayCast(number x_start, number y_start, number x_end, number y_end, function onRayCastCallBack)¶
- Parameters
number – x start point of the ray-cast.
number – y start point of the ray-cast.
number – x end point of the ray-cast.
number – y end point of the ray-cast.
function – call back function ray-cast.
Example:
require "box2d"
--Ray cast callback
function onRayCast(tMesh,x,y,nx,ny,fraction)
message = string.format('Crossed line: %s, at x:%g y:%g normal nx:%g ny:%g fraction:%g',tMesh.name,x,y,nx,ny,fraction)
print(message)
tMesh:setColor(0.7,0,0)
tShapePoint:setPos(x,y)
tShapePoint.visible = true
return fraction --if you return 0 it means end of ray-cast
end
mbm.setColor(1,1,1) --set background color to white
local gravity_x = 0
local gravity_y = -9.8 --very slow to be able to see
tPhysic = box2d:new(gravity_x,gravity_y)
line_start = {x = -300, y = 200}
line_end = {x = 300, y = 50}
tLine = line:new('2dw')
tLine:add({line_start.x,line_start.y,line_end.x,line_end.y})
tLine:setColor(0,0,1) -- Blue color
tShapeQuad = shape:new('2dw',-20, 500)
tShapeCircle = shape:new('2dw',50 , 300)
tShapeGround = shape:new('2dw',0,-50)
tShapePoint = shape:new('2dw')
tShapeQuad.name = 'rectangle'
tShapeCircle.name = 'circle'
tShapeQuad:create('rectangle',100,100)
tShapeCircle:create('circle',100,100)
tShapePoint:create('circle',20,20)
tShapeGround:create('rectangle',1000,20)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addStaticBody(tShapeGround)
function loop(delta,fps)
tShapeQuad:setColor(0.7,0,0.7) --reset the shape's color
tShapeCircle:setColor(0.7,0,0.7) --reset the shape's color
tShapePoint.visible = false
local pStart = {x = line_start.x , y = line_start.y }
local pEnd = {x = line_end.x , y = line_end.y }
tPhysic:rayCast(pStart.x,pStart.y,pEnd.x,pEnd.y,onRayCast)
end
25.1.12. box2d start¶
require "box2d"
tPhysic = box2d:new()
tPhysic:start() --resume the simulation
25.1.13. box2d queryAABB¶
- queryAABB(number lowerBound_x, number lowerBound_y, number upperBound_x, number upperBound_y, function onQueryAABBBox2d)¶
Perform a AABB algorithm collision for all objects given the bound.
The callback is called in case of collision.
It has to have the following signature:
function onQueryAABBBox2d(tMesh)
end
Example:
require "box2d"
mbm.setColor(1,1,1) --set background color to white
function onQueryAABBBox2d(tMesh)
print('Collide with:',tMesh.name)
if tMesh.name == 'circle' then
tLine:setColor(1,0,0) -- red for circle
elseif tMesh.name == 'rectangle' then
tLine:setColor(0,0,1) --blue for rectangle
end
end
local gravity_x = 0
local gravity_y = -9.8 --very slow to be able to see
tPhysic = box2d:new(gravity_x,gravity_y)
local lowerBound = {x = 0, y = 0}
local upperBound = {x = 100, y = 100}
tLine = line:new('2dw')
tLine:add( {lowerBound.x,lowerBound.y,
lowerBound.x,upperBound.y,
upperBound.x,upperBound.y,
upperBound.x,lowerBound.y,
lowerBound.x,lowerBound.y})
tLine:setColor(1,1,1) --white color means no colision
tShapeQuad = shape:new('2dw',-20, 500)
tShapeCircle = shape:new('2dw',50 , 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeQuad.name = 'rectangle'
tShapeCircle.name = 'circle'
tShapeQuad:create('rectangle',100,100)
tShapeCircle:create('circle',100,100)
tShapeGround:create('rectangle',500,20)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addStaticBody(tShapeGround)
function loop(delta)
tPhysic:queryAABB(lowerBound.x,lowerBound.y,upperBound.x,upperBound.y,onQueryAABBBox2d)
end
25.2. box2d Body¶
Bodies are the fundamental objects in the physics scene, but they are not what you actually see bouncing around and colliding with each other. Body represent the actual physics and for this engine, is a kind of glue beteween the actual body and any renderizable.
You can think of a body as the properties of an object that you cannot see (draw) or touch (collide with) but the engine will copy the position and angle to the current object.
The main properties of bodies for box2d are:
- Mass
how heavy it is
- Velocity
how fast and which direction it’s moving
- Rotational inertia
how much effort it takes to start or stop spinning
- Angular velocity
how fast and which way it’s rotating
- Location
where it is. This is the position of object (
x,y
)
- Angle
which way it is facing. This is the angle
az
of object
There are three types of body available for box2d: static
, dynamic
and kinematic
. The engine implement them all.
Any renderizable object can be added to box2d to be simulated with its physics properties, however, it makes sense for 2D
objects.
The body is based on physics properties which can be modified through setPhysics from meshDebug object.
25.2.1. Bullet body¶
- setBullet(tBody, boolean value)¶
Should this body be treated like a bullet for continuous collision detection?
- Parameters
renderizable – body.
boolean – value.
Example:
tPhysic:setBullet(tBody,true)
25.2.2. Character body¶
A Character body is a dynamic body but using fixed rotation and do not allowing to sleep.
Let’s see an example:
require "box2d"
mbm.setColor(1,1,1) --set background color to white
local gravity_x = 0
local gravity_y = -9.8 --very slow to be able to see
tPhysic = box2d:new(gravity_x,gravity_y)
tShapeQuad = shape:new('2dw',-310, 50)
tShapeCircle = shape:new('2dw', 0, 300)
tShapeTriangle = shape:new('2dw', 50, 500)
tShapeGround = shape:new('2dw',0,-50)
tShapeQuad:create('rectangle',100,100)
tShapeCircle:create('circle',100,100)
tShapeTriangle:create('triangle',100)
tShapeGround:create('rectangle',1000,20)
local density, friction, restitution = 1, 10, 0.8 -- restitution 0.1 -> 0.8 (super)
tPhysic:addDynamicBody(tShapeCircle,density, friction, restitution)
tPhysic:addKinematicBody(tShapeQuad)
tPhysic:setLinearVelocity(tShapeQuad,2,0) -- move right 2 unit per second
tPhysic:addDynamicBody(tShapeTriangle)
tPhysic:setFixedRotation(tShapeTriangle,true)
tPhysic:setSleepingAllowed(tShapeTriangle,false)
tPhysic:addStaticBody(tShapeGround)
25.2.3. Damping options body¶
- setAngularDamping(tBody, number angular_damping)¶
- Parameters
renderizable – body.
number – angular damping.
Example:
tPhysic:setAngularDamping(tBody,15)
25.2.4. Density option body¶
- setDensity(tBody, number density, boolean * reset_mass)¶
Set the density of all fixtures in the body.
The optional flag reset_mass instruct to call b2Body::ResetMassData to update the body’s mass.
- Parameters
renderizable – body.
number – density.
boolean – reset mass flag, default is
true
.
Example:
tPhysic:setDensity(tBody,10,true)
25.2.5. Destroy body¶
- destroyBody(tBody)¶
- Parameters
renderizable – body.
Example:
tPhysic:destroyBody(tBody)
Note
The engine will destroy the body in the next cycle.
25.2.6. Dynamic body¶
Dynamic body can move, spin and also be influenced by the gravity.
- addDynamicBody(renderizable mesh, number * density, number * friction, number * restitution, number * reduceX, number * reduceY, boolean * isSensor, boolean * isBullet)¶
Create a new instance of a
dynamic
body.- Parameters
renderizable – any type of mesh previouslly loaded according.
number – density default is
1.0
.number – friction default is
10.0
.number – restitution default is
0.1
.number – scale of reduction on
x
axis. default is1.0
(real size).number – scale of reduction on
y
axis. default is1.0
(real size).boolean – sensor flag (
true
orfalse
) default isfalse
.boolean – bullet flag (
true
orfalse
) default isfalse
.
- Returns
body table.
For this example let’s create differents types of dynamic bodies:
require "box2d"
mbm.setColor(1,1,1)
local gravity_x = 0
local gravity_y = -9.8 --very slow to be able to see
tPhysic = box2d:new(gravity_x,gravity_y)
tShapeQuad = shape:new('2dw',-100, 720)
tShapeCircle = shape:new('2dw', 0, 500)
tShapeTriangle = shape:new('2dw', 50, 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeQuad:create('rectangle',100,100)
tShapeCircle:create('circle',100,100)
tShapeTriangle:create('triangle',100)
tShapeGround:create('rectangle',1000,20)
local density, friction, restitution = 1, 10, 0.8 -- restitution 0.1 -> 0.8 (super)
tPhysic:addDynamicBody(tShapeCircle,density, friction, restitution)
density = 10
tPhysic:addDynamicBody(tShapeQuad,density) --heavy
tPhysic:addDynamicBody(tShapeTriangle)
tPhysic:addStaticBody(tShapeGround)
25.2.7. Filter options body¶
- setEnable(tBody, boolean value)¶
Disable or enable a body changing internally the flag maskBits for each fixture in the body.
- Parameters
renderizable – body.
boolean – value.
Example:
require "box2d"
mbm.setColor(1,1,1)
local gravity_x = 0
local gravity_y = -9.8 --very slow to be able to see
tPhysic = box2d:new(gravity_x,gravity_y)
tShapeQuad = shape:new('2dw',-100, 300)
tShapeCircle = shape:new('2dw', 0, 200)
tShapeTriangle = shape:new('2dw', 50, 100)
tShapeGround = shape:new('2dw',0,-50)
tShapeQuad:create('rectangle',100,100)
tShapeCircle:create('circle',100,100)
tShapeTriangle:create('triangle',100)
tShapeGround:create('rectangle',1000,20)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addDynamicBody(tShapeQuad) --heavy
tPhysic:addDynamicBody(tShapeTriangle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:setEnable(tShapeCircle,false)
Internally it will change all fixture mask bits as is showing bellow:
const uint16 maskBits = enable ? 0xFFFF : 0;
b2Fixture* fixtureList = body->GetFixtureList();
while (fixtureList)
{
b2Filter oldFilter(fixtureList->GetFilterData());
oldFilter.maskBits = maskBits;
fixtureList->SetFilterData(oldFilter);
fixtureList = fixtureList->GetNext();
}
- setFilter(tBody * body, b2Filter filter)¶
Set the contact filtering data.
If body is not supplied it will set the filter for all bodies.
The default values are
0x0001
for categoryBits and0xFFFF
for maskBits, or in other words every fixture says:‘I am a thing and I will collide with every other thing,’
- Parameters
renderizable – body (optional).
b2Filter – filter box2d.
The filter shall have this members (default values):
tFilter =
{ categoryBits = 0x0001, -- I am (16 bits)
maskBits = 0xFFFF, -- Collide with (16 bits)
groupIndex = 0} -- Group collision (override collision). (16 bits)
- categoryBits
The collision category bits. Normally you would just set one bit.
- maskBits
The collision mask bits. This states the categories that this shape would accept for collision.
- groupIndex
Collision groups allow a certain group of objects to never collide (negative) or always collide (positive). Zero means no collision group. Non-zero group filtering always wins against the mask bits.
Example:
require "box2d"
mbm.setColor(1,1,1)
local gravity_x = 0
local gravity_y = -9.8 --very slow to be able to see
tPhysic = box2d:new(gravity_x,gravity_y)
tFilterA = { categoryBits = 1, -- I am A (0000-0001)
maskBits = 132, -- Collide with C,D (1000-0100)
groupIndex = 0} -- Override collision
tFilterB = { categoryBits = 2, -- I am B (0000-0010)
maskBits = 128, -- Collide with D (1000-0000)
groupIndex = 0} -- Override collision
tFilterC = { categoryBits = 4, -- I am C (0000-0100)
maskBits = 129, -- Collide with A,D (1000-0001)
groupIndex = 0} -- Override collision
tFilterD = { categoryBits = 128, -- I am D (1000-0000)
maskBits = 255, -- Collide with all (1111-1111)
groupIndex = 0} -- Override collision
tShapeQuad = shape:new('2dw', -20, 300)
tShapeCircle = shape:new('2dw', 0, 200)
tShapeTriangle = shape:new('2dw', 50, 100)
tShapeGround = shape:new('2dw',0,-50)
tShapeQuad:create('rectangle',100,100)
tShapeCircle:create('circle',100,100)
tShapeTriangle:create('triangle',100)
tShapeGround:create('rectangle',1000,20)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeTriangle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:setFilter(tShapeQuad, tFilterA)
tPhysic:setFilter(tShapeCircle, tFilterB)
tPhysic:setFilter(tShapeTriangle, tFilterC)
tPhysic:setFilter(tShapeGround, tFilterD)
25.2.8. Force options body¶
- applyForce(tBody, number fx, number fy, number * wx, number *wy)¶
Apply a gradual force at a world point. If the force is not applied at the center of mass, it will generate a torque and affect the angular velocity.
This function always wake up the body.
- Parameters
renderizable – body.
number – fx force world, usually in Newtons (N).
number – fy force world, usually in Newtons (N).
number – wx point the world position of the point of application.
number – wy point the world position of the point of application.
Example:
require "box2d"
tPhysic = box2d:new()
tPhysic:setScale(10)
tShapeQuad = shape:new('2dw',-310, 50)
tShapeCircle = shape:new('2dw', 0, 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeQuad:create('rectangle',40,40)
tShapeCircle:create('circle',40,40)
tShapeGround:create('rectangle',1000,20)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addStaticBody(tShapeGround)
function onKeyDown(key)
local force = 20000
local wx,wy = tPhysic:getWorldCenter(tShapeQuad) --same effect as applyForceToCenter
if mbm.getKeyCode('up') == key then -- in this example we keep pressing to up
tPhysic:applyForce(tShapeQuad,0 ,force, wx, wy)
elseif mbm.getKeyCode('left') == key then
tPhysic:applyForce(tShapeQuad,-force,0, wx, wy)
elseif mbm.getKeyCode('right') == key then -- and in the air right
tPhysic:applyForce(tShapeQuad,force,0, wx, wy)
end
end
- applyForceToCenter(tBody, number fx, number fy)¶
Apply a force at a world point at the center of mass,
- Parameters
renderizable – body.
number – fx force world, usually in Newtons (N).
number – fy force world, usually in Newtons (N).
Example:
require "box2d"
tPhysic = box2d:new()
tPhysic:setScale(10)
tShapeQuad = shape:new('2dw',-310, 50)
tShapeCircle = shape:new('2dw', 0, 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeQuad:create('rectangle',40,40)
tShapeCircle:create('circle',40,40)
tShapeGround:create('rectangle',1000,20)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addStaticBody(tShapeGround)
function onKeyDown(key)
local force = 20000
if mbm.getKeyCode('up') == key then
tPhysic:applyForceToCenter(tShapeQuad,0 ,force)
elseif mbm.getKeyCode('left') == key then
tPhysic:applyForceToCenter(tShapeQuad,-force,0)
elseif mbm.getKeyCode('right') == key then
tPhysic:applyForceToCenter(tShapeQuad,force,0) -- in this example we keep pressing to right
end
end
- applyLinearImpulse(tBody, number fx, number fy, number * wx, number *wy)¶
Apply an impulse at a point. This immediately modifies the velocity. It also modifies the angular velocity if the point of application is not at the center of mass. This wakes up the body.
- Parameters
renderizable – body.
number – fx force world, usually in Newtons (N).
number – fy force world, usually in Newtons (N).
number – wx point the world position of the point of application.
number – wy point the world position of the point of application.
Example:
require "box2d"
tPhysic = box2d:new()
tPhysic:setScale(10)
tShapeQuad = shape:new('2dw',-310, 50)
tShapeCircle = shape:new('2dw', 0, 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeQuad:create('rectangle',40,40)
tShapeCircle:create('circle',40,40)
tShapeGround:create('rectangle',1000,20)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addStaticBody(tShapeGround)
function onKeyDown(key)
local force = 2000
local wx,wy = tPhysic:getWorldCenter(tShapeQuad)
if mbm.getKeyCode('up') == key then
tPhysic:applyLinearImpulse(tShapeQuad,0 ,force, wx, wy)
elseif mbm.getKeyCode('left') == key then
tPhysic:applyLinearImpulse(tShapeQuad,-force,0, wx, wy)
elseif mbm.getKeyCode('right') == key then -- in this example we pressed once to right
tPhysic:applyLinearImpulse(tShapeQuad,force,0, wx, wy)
end
end
- applyLinearImpulseToCenter(tBody, number fx, number fy)¶
Apply an impulse to the center of mass. This immediately modifies the velocity.
- Parameters
renderizable – body.
number – fx force world, usually in Newtons (N).
number – fy force world, usually in Newtons (N).
Example:
require "box2d"
tPhysic = box2d:new()
tPhysic:setScale(10)
tShapeQuad = shape:new('2dw',-310, 50)
tShapeCircle = shape:new('2dw', 0, 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeQuad:create('rectangle',40,40)
tShapeCircle:create('circle',40,40)
tShapeGround:create('rectangle',1000,20)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addStaticBody(tShapeGround)
function onKeyDown(key)
local force = 2000
if mbm.getKeyCode('up') == key then
tPhysic:applyLinearImpulseToCenter(tShapeQuad,0 ,force) -- in this example we pressed once to up
elseif mbm.getKeyCode('left') == key then
tPhysic:applyLinearImpulseToCenter(tShapeQuad,-force,0)
elseif mbm.getKeyCode('right') == key then
tPhysic:applyLinearImpulseToCenter(tShapeQuad,force,0)
end
end
- applyAngularImpulse(tBody, number angular_impulse, boolean * wake)¶
Apply an angular impulse.
- Parameters
renderizable – body.
number – angular impulse in units of kg*m*m/s.
boolean – wake up the body (default is
true
).
Example:
tPhysic:applyAngularImpulse(tBody,50,true)
25.2.9. Friction options body¶
- setFriction(tBody, number friction, boolean update_contact_list)¶
Set the coefficient of friction. By default the engine also set the friction on contact list.
- Parameters
renderizable – body.
number – friction coefficient.
boolean – update contact list flag (default is
true
).
Example:
tPhysic:setFriction(tBody,5,true)
25.2.10. Gravity options body¶
- getGravityScale(tBody)¶
Get the gravity scale of the body.
- Parameters
renderizable – body.
- Returns
number
- gravity scale
Example:
local gravity_scale = tPhysic:getGravityScale(tBody)
- setGravityScale(tBody, number gravity_scale)¶
Set the gravity scale of the body.
- Parameters
renderizable – body.
number – gravity scale.
Example:
tPhysic:setGravity(tBody,2.0)
25.2.11. Inertia options body¶
- getInertia(tBody)¶
Get the rotational inertia of the body about the local origin.
- Parameters
renderizable – body.
- Returns
number
- The rotational inertia, usually in kg-m^2.
Example:
local inertia = tPhysic:getInertia(tBody)
25.2.12. Mass options body¶
- getMass(tBody)¶
Get the total mass of the body.
- Parameters
renderizable – body.
- Returns
number
- the mass, usually in kilograms (kg).
Example:
local mass = tPhysic:getMass(tBody)
- setMass(tBody)¶
The mass of the shape, usually in kilograms.
- Parameters
renderizable – body.
number – mass.
Example:
tPhysic:setMass(tBody,10)
25.2.13. Kinematic body¶
A kinematic body is very similar to a static body because, when it collides with a dynamic body it always holds its ground, and forces the dynamic body to retreat out of the way. The difference is that a kinematic body can move.
- addKinematicBody(renderizable mesh, number * density, number * friction, number * restitution, number * reduceX, number * reduceY, boolean * isSensor)¶
Create a new instance of a
kinematic
body.- Parameters
renderizable – any type of mesh previouslly loaded according.
number – density default is
1.0
.number – friction default is
10.0
.number – restitution default is
0.1
.number – scale of reduction on
x
axis. default is1.0
(real size).number – scale of reduction on
y
axis. default is1.0
(real size).boolean – sensor flag (
true
orfalse
) default isfalse
.
- Returns
body table.
For this example let’s create a kinematic body as a square:
require "box2d"
mbm.setColor(1,1,1) --set background color to white
local gravity_x = 0
local gravity_y = -9.8 --very slow to be able to see
tPhysic = box2d:new(gravity_x,gravity_y)
tShapeQuad = shape:new('2dw',-310, 50)
tShapeCircle = shape:new('2dw', 0, 500)
tShapeTriangle = shape:new('2dw', 50, 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeQuad:create('rectangle',100,100)
tShapeCircle:create('circle',100,100)
tShapeTriangle:create('triangle',100)
tShapeGround:create('rectangle',1000,20)
local density, friction, restitution = 1, 10, 0.8 -- restitution 0.1 -> 0.8 (super)
tPhysic:addDynamicBody(tShapeCircle,density, friction, restitution)
tPhysic:addKinematicBody(tShapeQuad)
tPhysic:setLinearVelocity(tShapeQuad,2,0) -- move right 2 unit per second
tPhysic:addDynamicBody(tShapeTriangle)
tPhysic:addStaticBody(tShapeGround)
25.2.14. Position options body¶
- getPosition(tBody)¶
Get the body position NOT considering the box2d scale .
- Parameters
renderizable – body.
- Returns
number
x -number
- y .
Example:
require "box2d"
local x,y = tPhysic:getPosition(tBody)
print('raw position :',x,y)
print('scaled position:',tBody.x,tBody.y)
-- possible output using scale default (10)
-- raw position : -2 7.929474
-- scaled position: -20 79.29475
25.2.15. Restitution options body¶
- setRestitution(tBody, number restitution, boolean update_contact_list)¶
Set the coefficient of restitution. By default the engine also set the restitution on contact list.
- Parameters
renderizable – body.
number – restitution coefficient.
boolean – update contact list flag (default is
true
).
Example:
tPhysic:setRestitution(tBody,10,true)
25.2.16. Rotation options body¶
- setFixedRotation(tBody, boolean value)¶
Set this body to have fixed rotation. This causes the mass to be reset.
- Parameters
renderizable – body.
boolean – value.
Example:
tPhysic:setFixedRotation(tBody,true)
25.2.17. Sleep options body¶
- setSleepingAllowed(tBody, boolean value)¶
Disable sleeping on body. If you disable sleeping, the body will be woken.
- Parameters
renderizable – body.
boolean – value.
Example:
tPhysic:setSleepingAllowed(tBody,false)
25.2.18. State options body¶
- isActive(tBody)¶
Get the active state of the body.
- Parameters
renderizable – body.
- Returns
boolean
- is active
Example:
local is_active = tPhysic:isActive(tBody)
print('Body is active?:', tostring(is_active))
- isAwake(tBody)¶
Get the sleeping state of this body.
- Parameters
renderizable – body.
- Returns
boolean
- is awake
Example:
local is_awake = tPhysic:isActive(tBody)
print('Body is awake?:', tostring(is_awake))
- setActive(tBody, boolean value)¶
Set the active state of the body. An inactive body is not simulated and cannot be collided with or woken up. If you pass a flag of true, all fixtures will be added to the broad-phase. If you pass a flag of false, all fixtures will be removed from the broad-phase and all contacts will be destroyed. Fixtures and joints are otherwise unaffected. You may continue to create/destroy fixtures and joints on inactive bodies. Fixtures on an inactive body are implicitly inactive and will not participate in collisions, ray-casts, or queries. Joints connected to an inactive body are implicitly inactive. An inactive body is still owned by a b2World object and remains in the body list.
- Parameters
renderizable – body.
boolean – value.
Example:
tPhysic:setActive(tBody,true)
- setAwake(tBody, boolean value)¶
Set the sleep state of the body. A sleeping body has very low CPU cost.
- Parameters
renderizable – body.
boolean – value.
Example:
tPhysic:setAwake(tBody,false)
25.2.19. Static body¶
when a static body collides with a dynamic body, it always holds its ground, and forces the dynamic body to retreat out of the way. The static body will never move.
- addStaticBody(renderizable mesh, number * density, number * friction, number * reduceX, number * reduceY, boolean * isSensor)¶
Create a new instance of a
static
body.- Parameters
renderizable – any type of mesh previouslly loaded according.
number – density default is
0.0
.number – friction default is
0.3
.number – scale of reduction on
x
axis. default is1.0
(real size).number – scale of reduction on
y
axis. default is1.0
(real size).boolean – sensor flag (
true
orfalse
) default isfalse
.
- Returns
body table.
For this example let’s create a ground which represent the static body:
require "box2d"
mbm.setColor(1,1,1)
local gravity_x = 0
local gravity_y = -9.8 --very slow to be able to see
tPhysic = box2d:new(gravity_x,gravity_y)
tShapeQuad = shape:new('2dw',-20, 720)
tShapeCircle = shape:new('2dw',100, 500)
tShapeTriangle = shape:new('2dw',50 , 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeQuad:create('rectangle',100,100)
tShapeCircle:create('circle',100,100)
tShapeTriangle:create('triangle',100)
tShapeGround:create('rectangle',500,20)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addDynamicBody(tShapeTriangle)
tPhysic:addStaticBody(tShapeGround)
25.2.20. Test point on body¶
- testPoint(tBody, number x, number y)¶
Test a point for containment in all fixture from a body.
- Parameters
renderizable – body.
number – x in world coordinates.
number – y in world coordinates.
- Returns
boolean
- hit any fixture
Example:
require "box2d"
mbm.setColor(1,1,1)
tPhysic = box2d:new()
tPhysic:setScale(15)
tShapeQuad = shape:new('2dw', -20, -20)
tShapeQuad:create('rectangle', 100,200)
tPhysic:addStaticBody(tShapeQuad)
function onTouchMove(key,x,y)
x,y = mbm.to2dw(x,y)
if tPhysic:testPoint(tShapeQuad,x,y) then
tShapeQuad:setColor(0.8,0,0)
else
tShapeQuad:setColor(0.8,0,0.8)
end
end
25.2.21. Torque options body¶
- applyTorque(tBody, number torque, boolean * wake)¶
Apply a torque. This affects the angular velocity without affecting the linear velocity of the center of mass.
- Parameters
renderizable – body.
number – torque about the z-axis (out of the screen), usually in N-m.
boolean – wake up the body (default is
true
).
Example:
tPhysic:applyTorque(tBody,50,true)
25.2.22. Transform options body¶
Important
You should consider the box2d scale to everything related to any transform method to real world and vice versa.
- setTransform(tBody, number * x, number * y, number * angle)¶
Set a new position of the body’s origin and rotation.
Manipulating a body’s transform may cause non-physical behavior but sometimes it is desired.
If not supplied any of arguments it will use the own position and angle.
- Parameters
number – x position of body.
number – y position of body.
number – angle
z
of body.
Example:
require "box2d"
tPhysic = box2d:new()
tPhysic:setScale(10) -- setTransform method considers box2d scale.
tShapeQuad = shape:new('2dw')
tShapeQuad:create('rectangle',100,100)
tPhysic:addDynamicBody(tShapeQuad)
tShapeQuad:setPos(55,33) -- will not change the position since box2d is in control
tShapeQuad.az = math.rad(15) -- tilt 15 degree
tPhysic:setTransform(tShapeQuad) -- Now we say to box 2d to use this new position and z angle
-- tPhysic:setTransform(tShapeQuad,55,33,math.rad(15)) --same as above
Important
const float scalePercentage = 1.0f / box2d->scale;
const b2Vec2 position(newPosition->x * scalePercentage,newPosition->y * scalePercentage);
body->SetTransform(position,newAngleDegree);
- getWorldCenter(tBody)¶
Get the world position of the center of mass.
- Parameters
renderizable – body.
- Returns
number
x -number
- y .
Example:
require "box2d"
tPhysic = box2d:new()
tPhysic:setScale(1)
tShapeQuad = shape:new('2dw',45,38) --put some place different from origin
tShapeQuad:create('rectangle',100,100)
tPhysic:addStaticBody(tShapeQuad)
local px, py = tPhysic:getWorldCenter(tShapeQuad)
tShapePoint = shape:new('2dw')
tShapePoint:create('circle',10,10)
tShapePoint:setPos(px,py)
tShapePoint:setColor(0,0,1) -- our blue point
print('px:',px,'py:',py)
- getWorldPoint(tBody, number x, number y)¶
The function getWorldPoint is used to convert a location relative to the body (body coordinates) into world coordinates.
- Parameters
renderizable – body.
number – x point on the body measured relative the the body’s origin.
number – y point on the body measured relative the the body’s origin.
- Returns
number
x -number
- y .
Example:
require "box2d"
tPhysic = box2d:new()
tPhysic:setScale(1)
tShapeQuad = shape:new('2dw',66,99) --put some place different from origin
tShapeQuad:create('rectangle',100,100)
tShapeQuad.az = math.rad(10) -- tilt 10 degree
tPhysic:addStaticBody(tShapeQuad)
local x, y = 50,50 -- we know the size
local px, py = tPhysic:getWorldPoint(tShapeQuad,x,y)
tShapePoint = shape:new('2dw')
tShapePoint:create('circle',10,10)
tShapePoint:setPos(px,py)
tShapePoint:setColor(0,0,1) -- our blue point
print('px:',px,'py:',py)
- getWorldVector(tBody, number x, number y)¶
Get the world coordinates of a vector given the local coordinates.
- Parameters
renderizable – body.
number – x local fixed in the body.
number – y local fixed in the body.
- Returns
number
x -number
- y .
Example:
require "box2d"
tPhysic = box2d:new()
tPhysic:setScale(1)
tShapeQuad = shape:new('2dw',50,50)
tShapeQuad:create('rectangle',100,100)
tShapeQuad.az = math.rad(180) -- tilt 180 degree
tPhysic:addStaticBody(tShapeQuad)
local x, y = 100,100
local px, py = tPhysic:getWorldVector(tShapeQuad,x,y)
tShapePoint = shape:new('2dw')
tShapePoint:create('circle',10,10)
tShapePoint:setPos(px,py)
tShapePoint:setColor(0,0,1) -- our blue point
print('px:',px,'py:',py)
- getLocalPoint(tBody)¶
Gets a local point relative to the body’s origin given a world point.
- Parameters
renderizable – body.
number – x world coordinates.
number – y world coordinates.
- Returns
number
x -number
- y corresponding local point relative to the body’s origin.
Example:
local x,y = tPhysic:getLocalPoint(tBody,10,5)
- getLocalCenter(tBody)¶
Get the local position of the center of mass.
- Parameters
renderizable – body.
- Returns
number
x -number
- y local center.
Example:
local x,y = tPhysic:getLocalCenter(tBody)
25.2.23. Type options body¶
- setType(tBody, string type)¶
Set the type of body. This may alter the mass and velocity.
Acceptable types are:
static
,kinematic
ordynamic
.- Parameters
renderizable – body.
string – type of body.
Example:
tPhysic:setType(tBody,'static')
- getType(tBody)¶
Retrieve the body type.
The possible types are:
static
,kinematic
ordynamic
.- Parameters
renderizable – body.
- Returns
string
- type
Example:
local type_body = tPhysic:getType(tBody)
25.2.24. Velocity options body¶
- getLinearVelocity(tBody)¶
Get the linear velocity of the center of mass.
- Parameters
renderizable – body.
- Returns
number
x -number
- y the linear velocity of the center of mass.
Example:
local lx,ly = tPhysic:getLinearVelocity(tBody)
- getAngularVelocity(tBody)¶
Get the angular velocity.
- Parameters
renderizable – body.
- Returns
number
- angular velocity in radians/second.
Example:
local angular_velocity = tPhysic:getAngularVelocity(tBody)
- setAngularVelocity(tBody, number omega)¶
Set the angular velocity.
- Parameters
renderizable – body.
number – omega. the new angular velocity in radians/second.
Example:
local omega = math.rad(15)
tPhysic:setAngularVelocity(tBody,omega)
- setLinearVelocity(tBody, number x, number y)¶
Set the linear velocity of the center of mass.
- Parameters
renderizable – body.
number – x. the new linear velocity of the center of mass.
number – y. the new linear velocity of the center of mass.
Example:
tPhysic:setLinearVelocity(tBody,50,2)
25.3. box2d Joint¶
Box2D has a lot of joints that can be used to connect two bodies. These joints mostly are to be used to simulate the interaction between objects to form hinges, pistons, ropes, wheels, pulleys, vehicles, chains, etc. Learning how to use joints effectively helps to create a more engaging and interesting scene. The method used to create a joint is defined here.
Joint common properties
Although each joint has a different behavior, they have some properties in common. Here are the properties which are common to build each joint:
tJoint = { name = 'my joint name', collideConnected = false, }Also for some joints you have to need to specify details for the specific type of joint that you are making. This commonly includes an anchor point on each body, limits on the range of movement, and motor settings.
- Anchor points
Typically a point on each body is given as the location around which the bodies must interact. Depending on the joint type, this point will be the center of rotation, the locations to keep a certain distance apart, etc.
- Joint limits
Revolute and prismatic joints can be given limits, which places a restriction on how far the bodies can rotate or slide.
- Joint motors
Revolute, prismatic and line (wheel) joints can be given motor settings, which means that instead of spinning or sliding around freely, the joint acts as if it had it’s own power source. The motor is given a maximum force or torque, and this can be used in combination with a target velocity to spin or slide bodies in relation to each other.
25.3.1. Joints definition¶
Let’s see the joints available for box2d and its characteristics:
25.3.1.1. Distance joint¶
- Distance
A point on each body will be kept at a fixed distance apart.
This requires defining an anchor point on both bodies and the non-zero length of the distance joint. The definition uses local anchor points so that the initial configuration can violate the constraint slightly. This helps when saving and loading a game.
Warning
Do not use a zero or short length.
Here the table definition:
tJoint =
{
name = 'distance',
localAnchorA = {x = 0.0, y = 0.0},
localAnchorB = {x = 0.0, y = 0.0},
length = 1.0,
frequencyHz = 0.0,
dampingRatio = 0.0,
collideConnected = false
}
Hint
localAnchorA
, localAnchorB
and length
.pos_1 = tPhysic:getPosition(mesh_1) -- position from box2d might be different if is there scale pos_2 = tPhysic:getPosition(mesh_2) -- position from box2d might be different if is there scale tJoint = { anchor1 = {x = pos_1.x, y = pos_1.y}, -- default values anchor2 = {x = pos_2.y, y = pos_2.y}, -- default values -- others parameters from table definition ... }
Of course if you pass any of localAnchorA
, localAnchorB
and length
it will be overridden.
25.3.1.2. Friction joint¶
- Friction
Reduces the relative motion between the two bodies.
Here the table definition:
tJoint =
{
name = 'friction',
localAnchorA = {x = 0, y = 0},
localAnchorB = {x = 0, y = 0},
maxForce = 0.0,
maxTorque = 0.0,
collideConnected = false
}
Hint
localAnchorA
and localAnchorB
.tJoint = { anchor = {x = 0, y = 0}, -- Default value -- others parameters from table definition ... }
Of course if you pass any of localAnchorA
or localAnchorB
it will be overridden.
25.3.1.3. Gear joint¶
- Gear
Controls two other joints (revolute or prismatic) so that the movement of one affects the other.
This definition requires two existing revolute or prismatic joints (any combination will work).
Here the table definition:
tJoint = { name = 'gear', ratio = 1.0, indexA = absolute_index_joint_A, indexB = absolute_index_joint_B, collideConnected = false }
Note
It is mandatory the body 1 and 2 have joint.The index have preference, in other words, the engine will look first for the absolute index.
25.3.1.4. Motor joint¶
- Motor
Controls the relative motion between two bodies.
A typical usage is to control the movement of a dynamic body with respect to the ground.
Here the table definition:
tJoint = { name = 'motor', linearOffset = { x = 0, y = 0}, angularOffset = 0.0, maxForce = 1.0, maxTorque = 1.0, correctionFactor = 0.3, collideConnected = false, }
25.3.1.5. Mouse joint¶
- Mouse
Pulls a point on one body to a location in the world.
This requires a world target point, tuning parameters, and the time step.
Here the table definition:
tJoint = { name = 'mouse', target = {x = 0, y = 0}, maxForce = 0.0, frequencyHz = 5.0, dampingRatio = 0.7, collideConnected = false, }
Note
Mouse joint does not need a second body however to keep the signature, we pass it twice on create joint method.
25.3.1.6. Prismatic joint¶
- Prismatic
The relative rotation of the two bodies is fixed, and they can slide along an axis.
This requires defining a line of motion using an axis and an anchor point. The definition uses local anchor points and a local axis so that the initial configuration can violate the constraint slightly. The joint translation is zero when the local anchor points coincide in world space. Using local anchors and a local axis helps when saving and loading a game.
Here the table definition:
tJoint =
{
name = 'prismatic',
localAnchorA = {x = 0, y=0},
localAnchorB = {x = 0, y=0},
localAxisA = {x = 1, y=0}, -- Must be normalized
referenceAngle = 0.0,
enableLimit = false,
lowerTranslation = 0.0,
upperTranslation = 0.0,
enableMotor = false,
maxMotorForce = 0.0,
motorSpeed = 0.0,
collideConnected = false,
}
Hint
localAnchorA
, localAnchorB
and localAxisA
.tJoint = { anchor = {x = 0, y = 0}, -- Default value. It initialize localAnchorA and localAnchorB axis = {x = 0, y = 1}, --Default value. It initialize localAxisA. must be normalized -- others parameters from table definition ... }
Of course if you pass any of localAnchorA
, localAnchorB
and localAxisA
it will be overridden.
25.3.1.7. Pulley joint¶
- Pulley
A point on each body will be kept within a certain distance from a point in the world.
This requires two ground anchors, two dynamic body anchor points, and a pulley ratio.
Here the table definition:
tJoint =
{
name = 'pulley',
groundAnchorA = {x = -1.0 , y = 1.0},
groundAnchorB = {x = 1.0 , y = 1.0},
localAnchorA = {x = -1.0 , y = 0.0},
localAnchorB = {x = 1.0 , y = 0.0},
lengthA = 0.0,
lengthB = 0.0,
ratio = 1.0,
collideConnected = true,
}
Hint
localAnchorA
, localAnchorB
, groundAnchorA
, groundAnchorB
, lengthA
and lengthB
.localAnchorA
, localAnchorB
, groundAnchorA
, groundAnchorB
, lengthA
and lengthB
.tJoint = { anchorA = {x = body_a.x, y = body_a.y}, -- default values comes from body a anchorB = {x = body_b.x, y = body_b.y}, -- default values comes from body b -- others parameters from table definition ... }
Of course if you pass any of localAnchorA
, localAnchorB
, groundAnchorA
, groundAnchorB
, lengthA
and lengthB
it will be overridden.
lengthA + lengthA == constant
.lengthA + ratio * lengthB == constant
25.3.1.8. Revolute joint¶
- Revolute
A hinge or pin, where the bodies rotate about a common point.
This requires defining an anchor point where the bodies are joined. The definition uses local anchor points so that the initial configuration can violate the constraint slightly. You also need to specify the initial relative angle for joint limits. This helps when saving and loading a game. The local anchor points are measured from the body’s origin rather than the center of mass because:
you might not know where the center of mass will be.
if you add/remove shapes from a body and recompute the mass,the joints will be broken.
Here the table definition:
tJoint =
{
name = 'revolute',
localAnchorA = { x = 0.0, y = 0.0},
localAnchorB = { x = 0.0, y = 0.0},
referenceAngle = 0.0,
lowerAngle = 0.0,
upperAngle = 0.0,
maxMotorTorque = 0.0,
motorSpeed = 0.0,
enableLimit = false,
enableMotor = false,
collideConnected = false,
}
Hint
localAnchorA
, localAnchorB
and referenceAngle
.pos_mesh_2 = tPhysic:getPosition(mesh_2) tJoint = { anchor = {x = pos_mesh_2.x, y = pos_mesh_2.y}, -- Default value, position from mesh '2' -- others parameters from table definition ... }
Of course if you pass any of localAnchorA
, localAnchorB
or referenceAngle
it will be overridden.
25.3.1.9. Rope joint¶
- Rope
A point on each body will be constrained to a maximum distance apart.
This requires two body anchor points and a maximum lengths.
Here the table definition:
tJoint = { name = 'rope', localAnchorA = { x =-1.0, y = 0.0}, localAnchorB = { x = 1.0, y = 0.0}, maxLength = 0.0, collideConnected = false, }
Warning
The maximum length of the rope must be larger than b2_linearSlop (0.005) or the joint will have no effect.
25.3.1.10. Weld joint¶
- Weld
Holds the bodies at the same orientation.
You need to specify local anchor points where they are attached and the relative body angle. The position of the anchor points is important for computing the reaction torque.
Here the table definition:
tJoint =
{
name = 'weld',
localAnchorA = { x = 0.0, y = 0.0},
localAnchorB = { x = 0.0, y = 0.0},
referenceAngle = 0.0,
frequencyHz = 0.0,
dampingRatio = 0.0,
collideConnected = false,
}
Hint
localAnchorA
, localAnchorB
and referenceAngle
.pos_mesh_2 = tPhysic:getPosition(mesh_2) tJoint = { anchor = {x = pos_mesh_2.x, y = pos_mesh_2.y}, -- Default value, position from mesh '2' -- others parameters from table definition ... }
Of course if you pass any of localAnchorA
, localAnchorB
or referenceAngle
it will be overridden.
25.3.1.11. Wheel joint¶
- Wheel
A combination of revolute and prismatic joints, useful for modeling vehicle suspension. (old
line
joint).
This requires defining a line of motion using an axis and an anchor point. The definition uses local anchor points and a local axis so that the initial configuration can violate the constraint slightly. The joint translation is zero when the local anchor points coincide in world space. Using local anchors and a local axis helps when saving and loading a game.
Here the table definition:
tJoint =
{
name = 'wheel',
localAnchorA = { x = 0, y = 0 },
localAnchorB = { x = 0, y = 0 },
localAxisA = { x = 1, y = 0 },
enableMotor = false,
maxMotorTorque = 0.0,
motorSpeed = 0.0,
frequencyHz = 2.0,
dampingRatio = 0.7,
collideConnected = false,
}
Hint
localAnchorA
, localAnchorB
and localAxisA
.tJoint = { anchor = {x = 0, y = 0}, -- Default value. It initialize localAnchorA and localAnchorB axis = {x = 0, y = 1}, --Default value. It initialize localAxisA. -- others parameters from table definition ... }
Of course if you pass any of localAnchorA
, localAnchorB
and localAxisA
it will be overridden.
25.3.2. Creating joint¶
- joint(tBody_A, tBody_B, tJoint)¶
The signature is always like this. The first body then the second and the joint table definition.
- Parameters
renderizable – first body.
renderizable – second body.
table – joint definition. see box2d joint
- Returns
number
- index absolute of joint. (zero is error).
Note
number
indicating the absolute index in the engine for this joint.25.3.2.1. Distance joint¶
Next, an example of joint using distance joint table.
require "box2d"
mbm.setColor(1,1,1)
tPhysic = box2d:new()
tShapeQuad = shape:new('2dw',-200, 720)
tShapeCircle1 = shape:new('2dw',-100, 400)
tShapeCircle2 = shape:new('2dw', 100, 400)
tShapeTriangle = shape:new('2dw', 50, 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeWall = shape:new('2dw',300,100)
tShapeQuad:create('rectangle',100,100)
tShapeCircle1:create('circle',100,100)
tShapeCircle2:create('circle',80,80)
tShapeTriangle:create('triangle',100)
tShapeGround:create('rectangle',1000,20)
tShapeWall:create('rectangle',20,200)
local density, friction, restitution = 0.5, 10, 1.0
tPhysic:addDynamicBody(tShapeCircle1,density, friction, restitution)
tPhysic:addDynamicBody(tShapeCircle2,density, friction, restitution)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeTriangle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeWall)
tJoint =
{
name = 'distance',
localAnchorA = {x = 0.0, y = 0.0},
localAnchorB = {x = 0.0, y = 0.0},
--length: we are omitting the distance let the engine calculate it
frequencyHz = 0.0,
dampingRatio = 0.0,
collideConnected = false
}
local indexJoint = tPhysic:joint(tShapeCircle1,tShapeCircle2,tJoint)
25.3.2.2. Friction joint¶
Next, an example of joint using friction joint table.
require "box2d"
mbm.setColor(1,1,1)
tPhysic = box2d:new()
tShapeQuad = shape:new('2dw',-200, 720)
tShapeCircle1 = shape:new('2dw',-100, 400)
tShapeCircle2 = shape:new('2dw', 100, 400)
tShapeTriangle = shape:new('2dw', 50, 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeWall = shape:new('2dw',300,100)
tShapeQuad:create('rectangle',100,100)
tShapeCircle1:create('circle',100,100)
tShapeCircle2:create('circle',80,80)
tShapeTriangle:create('triangle',100)
tShapeGround:create('rectangle',1000,20)
tShapeWall:create('rectangle',20,200)
local density, friction, restitution = 0.5, 10, 1.0
tPhysic:addDynamicBody(tShapeCircle1,density, friction, restitution)
tPhysic:addDynamicBody(tShapeCircle2,density, friction, restitution)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeTriangle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeWall)
tJoint =
{
name = 'friction',
localAnchorA = {x = 0, y = 0},
localAnchorB = {x = 0, y = 0},
maxForce = 5000.0,
maxTorque = 5000.0,
collideConnected = true
}
local indexJoint = tPhysic:joint(tShapeQuad,tShapeGround,tJoint)
25.3.2.3. Gear joint¶
Next, an example of joint using gear joint table.
require "box2d"
mbm.setColor(1,1,1)
tPhysic = box2d:new()
tShapeBoxMain = shape:new('2dw',0, 100)
tShapeBoxLeft = shape:new('2dw',-50, 150)
tShapeBoxRight = shape:new('2dw',50, 150)
tShapeCircle = shape:new('2dw',400, 500)
tShapeGround = shape:new('2dw',0,40)
tShapeRightWall = shape:new('2dw',450,0)
tShapeBoxMain:create('rectangle',100,100)
tShapeBoxLeft:create('rectangle',50,50)
tShapeBoxRight:create('rectangle',50,50)
tShapeCircle:create('circle',100,100)
tShapeGround:create('rectangle',1000,20)
tShapeRightWall:create('rectangle',20,400)
tShapeGround.az = math.rad(2) --tilt a litle bit
tPhysic:addDynamicBody(tShapeBoxMain)
tPhysic:addDynamicBody(tShapeBoxLeft)
tPhysic:addDynamicBody(tShapeBoxRight)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
local size_box = {x = 0, y = 0}
size_box.x, size_box.y = tShapeBoxMain:getSize()
size_box.x = size_box.x / tPhysic:getScale() --consider the box2d scale
size_box.y = size_box.y / tPhysic:getScale() --consider the box2d scale
local corner_box_left = {x = size_box.x / 2,y = size_box.y / 2} -- corner of the box (top,left)
local corner_box_right = {x = -size_box.x / 2,y = size_box.y / 2} -- corner of the box (top,right)
--[[
corner box top left -> *-------* <- corner_box top,right
| main |
| box |
|-------|
]]
tJointLeft =
{
name = 'revolute',
localAnchorA = corner_box_left, --in the corner of the box A (top,left)
localAnchorB = {x = 0, y =0}, --in the center of the left box
referenceAngle = tShapeBoxLeft.az - tShapeBoxMain.az,
lowerAngle = math.rad(-90), -- no effect since enableLimit is false
upperAngle = math.rad(45), -- no effect since enableLimit is false
maxMotorTorque = 10000.0,
motorSpeed = 10.0,
enableLimit = false,
enableMotor = false,
collideConnected = false,
}
tJointRight =
{
name = 'revolute',
localAnchorA = corner_box_right, --in the corner of the box A (top,right)
localAnchorB = {x = 0, y =0}, --in the center of the rigth box
referenceAngle = tShapeBoxLeft.az - tShapeBoxMain.az,
lowerAngle = math.rad(-90), -- no effect since enableLimit is false
upperAngle = math.rad(45), -- no effect since enableLimit is false
maxMotorTorque = 10000.0,
motorSpeed = 10.0,
enableLimit = false,
enableMotor = false,
collideConnected = false,
}
local indexA = tPhysic:joint(tShapeBoxMain,tShapeBoxLeft,tJointLeft)
local indexB = tPhysic:joint(tShapeBoxMain,tShapeBoxRight,tJointRight)
tJLeft = tPhysic:getJoint(tShapeBoxLeft)
tJRight = tPhysic:getJoint(tShapeBoxRight)
tJoint =
{
name = 'gear',
ratio = 1.0,
indexA = indexA,
indexB = indexB,
collideConnected = false
}
local indexJointGear = tPhysic:joint(tShapeBoxLeft,tShapeBoxRight,tJoint)
25.3.2.4. Motor joint¶
Next, an example of joint using motor joint table.
require "box2d"
mbm.setColor(1,1,1)
tPhysic = box2d:new()
tShapeQuad = shape:new('2dw',-200, 520)
tShapeCircle1 = shape:new('2dw',-80, 400)
tShapeCircle2 = shape:new('2dw', 100, 400)
tShapeTriangle = shape:new('2dw', 50, 300)
tShapeGround = shape:new('2dw',0,-300)
tShapeRightWall = shape:new('2dw',450,0)
tShapeLeftWall = shape:new('2dw',-450,0)
tShapeQuad:create('rectangle',100,100)
tShapeCircle1:create('circle',200,200,10) --low resolution of the circle
tShapeCircle2:create('circle',80,80)
tShapeTriangle:create('triangle',100)
tShapeGround:create('rectangle',1000,20)
tShapeRightWall:create('rectangle',20,400)
tShapeLeftWall:create('rectangle',20,400)
local density, friction, restitution = 0.5, 10, 1.0
tPhysic:addDynamicBody(tShapeCircle1,density, friction, restitution)
tPhysic:addDynamicBody(tShapeCircle2,density, friction, restitution)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeTriangle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeLeftWall)
tPhysic:addStaticBody(tShapeRightWall)
tJoint =
{
name = 'motor',
linearOffset = { x = 15, y = 0}, --relative distance between body 2 and body 1
angularOffset = math.rad(180.0), --relative angle between body 2 and body 1
maxForce = 100000.0,
maxTorque = 10000.0,
correctionFactor = 1.3,
collideConnected = false,
}
local indexJoint = tPhysic:joint(tShapeQuad,tShapeCircle1,tJoint)
25.3.2.5. Mouse joint¶
Next, an example of joint using mouse joint table.
require "box2d"
mbm.setColor(1,1,1)
tPhysic = box2d:new()
tPhysic:setScale(10) --set our desired scale
tShapeQuad = shape:new('2dw',-200, 300)
tShapeCircle1 = shape:new('2dw',-100, 300)
tShapeCircle2 = shape:new('2dw', 100, 300)
tShapeTriangle = shape:new('2dw', 50, 300)
tShapeGround = shape:new('2dw',0,-350)
tShapeLeftWall = shape:new('2dw',-400,0)
tShapeRightWall = shape:new('2dw',400,0)
tShapeUpWall = shape:new('2dw',0,350)
tShapeQuad:create('rectangle',100,100)
tShapeCircle1:create('circle',100,100)
tShapeCircle2:create('circle',80,80)
tShapeTriangle:create('triangle',100)
tShapeGround:create('rectangle',1000,20)
tShapeRightWall:create('rectangle',20,700)
tShapeLeftWall:create('rectangle',20,700)
tShapeUpWall:create('rectangle',1000,20)
local density, friction, restitution = 0.5, 10, 1.0
tPhysic:addDynamicBody(tShapeCircle1,density, friction, restitution)
tPhysic:addDynamicBody(tShapeCircle2,density, friction, restitution)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeTriangle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
tPhysic:addStaticBody(tShapeLeftWall)
tPhysic:addStaticBody(tShapeUpWall)
tAllDynamicObject = {} --store all dynamic objects that we want to apply mouse joint
table.insert(tAllDynamicObject,tShapeQuad)
table.insert(tAllDynamicObject,tShapeCircle1)
table.insert(tAllDynamicObject,tShapeCircle2)
table.insert(tAllDynamicObject,tShapeTriangle)
--our mouse joint table
tJoint =
{
name = 'mouse',
target = {x=0,y=0}, -- updated in the moment that we click on object and keep clicked
maxForce = 0, -- updated according to mass
frequencyHz = 30.0,
dampingRatio = 0.7,
collideConnected = false,
}
tMouseJoint = nil --point to mouse joint when created
tMeshJoint = nil --point to mesh that we have selected (clicked)
--called onTouchDown and onTouchMove
function setTarget(x,y)
x , y = x / tPhysic:getScale(),y / tPhysic:getScale() -- we have to consider the scale
if tMouseJoint then
tMouseJoint:setTarget(x,y)
end
tJoint.target.x,tJoint.target.y = x,y
end
function onTouchDown(key,x,y)
x,y = mbm.to2dw(x,y)
for i = 1, #tAllDynamicObject do
local tMesh = tAllDynamicObject[i]
if tPhysic:testPoint(tMesh,x,y) and tMouseJoint == nil then
tJoint.maxForce = 1000 * tPhysic:getMass(tMesh)
setTarget(x,y) -- update the target
if tPhysic:joint(tMesh,tMesh,tJoint) > 0 then
tMouseJoint = tPhysic:getJoint(tMesh)
tMeshJoint = tAllDynamicObject[i]
break
end
end
end
end
function onTouchMove(key,x,y)
if tMouseJoint then
x , y = mbm.to2dw(x,y)
setTarget(x,y)-- update the target
end
end
function onTouchUp(key,x,y)
if tMouseJoint then
tPhysic:destroyJoint(tMeshJoint) --no long are holding the object, destroy it
tMouseJoint = nil --mark as nil
end
end
Note
25.3.2.6. Prismatic joint¶
Next, an example of joint using prismatic joint table.
require "box2d"
mbm.setColor(1,1,1)
tPhysic = box2d:new()
tShapeBoxA = shape:new('2dw',-300, 100)
tShapeBoxB = shape:new('2dw',-350, 150)
tShapeCircle = shape:new('2dw',400, 500)
tShapeGround = shape:new('2dw',0,40)
tShapeRightWall = shape:new('2dw',450,0)
tShapeBoxA:create('rectangle',100,100)
tShapeBoxB:create('rectangle',50,10)
tShapeCircle:create('circle',100,100)
tShapeGround:create('rectangle',1000,20)
tShapeRightWall:create('rectangle',20,400)
tShapeGround.az = math.rad(2) --tilt a litle bit
tPhysic:addDynamicBody(tShapeBoxA)
tPhysic:addDynamicBody(tShapeBoxB)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
local size_box_a = {x = 0, y = 0}
size_box_a.x, size_box_a.y = tShapeBoxA:getSize()
size_box_a.x = size_box_a.x / tPhysic:getScale() --consider the box2d scale
size_box_a.y = size_box_a.y / tPhysic:getScale() --consider the box2d scale
local corner_box_a = {x = size_box_a.x / 2,y = -size_box_a.y / 2} -- corner of the box (bottom, right)
local size_box_b = {x = 0, y = 0}
size_box_b.x, size_box_b.y = tShapeBoxB:getSize()
size_box_b.x = size_box_b.x / tPhysic:getScale() --consider the box2d scale
size_box_b.y = size_box_b.y / tPhysic:getScale() --consider the box2d scale
local corner_box_b = {x = -size_box_b.x / 2,y = -size_box_b.y / 2} -- corner of the box (bottom left)
--[[
|-------|
| | |-------|
| box A | | |
|-------* <- corner box a bottom,right | box B |
corner box b bottom left -> *-------|
]]
tJoint =
{
name = 'prismatic',
localAnchorA = {x = corner_box_a.x, y = corner_box_a.y},
localAnchorB = {x = corner_box_b.x, y = corner_box_b.y},
localAxisA = {x = 0, y=1}, -- Direction of elevation (normalized)
referenceAngle = 0.0,
enableLimit = true,
lowerTranslation = 0.0,
upperTranslation = size_box_a.y, --How high? same as maximum of first box
enableMotor = false, --enable on key up
maxMotorForce = 50000.0,
motorSpeed = 5.0,
collideConnected = false,
}
local indexJoint = tPhysic:joint(tShapeBoxA,tShapeBoxB,tJoint)
tJointPrismatic = tPhysic:getJoint(tShapeBoxA)
function onKeyDown(key)
if mbm.getKeyCode('up') == key then
tJointPrismatic:enableMotor(true)
end
end
function onKeyUp(key)
if mbm.getKeyCode('up') == key then
tJointPrismatic:enableMotor(false)
end
end
25.3.2.7. Pulley joint¶
Next, an example of joint using pulley joint table.
require "box2d"
mbm.setColor(1,1,1)
tPhysic = box2d:new()
tPhysic:setScale(10)
tShapeBox1 = shape:new('2dw',-200, -80)
tShapeBox2 = shape:new('2dw', 200, -308)
tShapeGround = shape:new('2dw',0,-350)
tShapeGround:create('rectangle',1000,20)
tShapeBox1:create('rectangle',100,100)
tShapeBox2:create('rectangle',50,50)
tPhysic:addDynamicBody(tShapeBox1)
tPhysic:addDynamicBody(tShapeBox2)
tPhysic:addStaticBody(tShapeGround)
groundAnchorA = shape:new('2dw',-200,300)
groundAnchorB = shape:new('2dw',200,300)
groundAnchorA:create('circle',20,20)
groundAnchorB:create('circle',20,20)
size_box_2_y = select(2,tShapeBox2:getSize())
half_size_box_2_y = size_box_2_y / 2
box_2d_scale = tPhysic:getScale()
tJoint =
{
name = 'pulley',
groundAnchorA = {x = groundAnchorA.x / box_2d_scale , y = groundAnchorA.y / box_2d_scale},
groundAnchorB = {x = groundAnchorB.x / box_2d_scale , y = groundAnchorB.y / box_2d_scale},
localAnchorA = {x = 0.0 , y = 0.0},
localAnchorB = {x = 0.0 , y = half_size_box_2_y / box_2d_scale},
lengthA = 100.0 / box_2d_scale,
lengthB = 400.0 / box_2d_scale,
ratio = 1.0,
collideConnected = true,
}
local indexJoint = tPhysic:joint(tShapeBox1,tShapeBox2,tJoint)
25.3.2.8. Revolute joint¶
Next, an example of joint using revolute joint table.
require "box2d"
mbm.setColor(1,1,1)
tPhysic = box2d:new()
tShapeBoxA = shape:new('2dw',0, 100)
tShapeBoxB = shape:new('2dw',50, 150)
tShapeCircle = shape:new('2dw',400, 500)
tShapeGround = shape:new('2dw',0,40)
tShapeRightWall = shape:new('2dw',450,0)
tShapeBoxA:create('rectangle',100,100)
tShapeBoxB:create('rectangle',50,50)
tShapeCircle:create('circle',100,100)
tShapeGround:create('rectangle',1000,20)
tShapeRightWall:create('rectangle',20,400)
tShapeGround.az = math.rad(2) --tilt a litle bit
tPhysic:addDynamicBody(tShapeBoxA)
tPhysic:addDynamicBody(tShapeBoxB)
tPhysic:addDynamicBody(tShapeCircle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
local size_box = {x = 0, y = 0}
size_box.x, size_box.y = tShapeBoxA:getSize()
size_box.x = size_box.x / tPhysic:getScale() --consider the box2d scale
size_box.y = size_box.y / tPhysic:getScale() --consider the box2d scale
local corner_box = {x = size_box.x / 2,y = size_box.y / 2} -- corner of the box (top,right)
--[[
|-------* <- corner_box top,right
| |
| box A |
|-------|
]]
-- If we omit local anchors A and B, the engine will use the second body to calculate it.
tJoint =
{
name = 'revolute',
localAnchorA = corner_box, --in the corner of the box A (top,right)
localAnchorB = {x = 0, y =0}, --in the center of the box B
referenceAngle = tShapeBoxB.az - tShapeBoxA.az,
lowerAngle = math.rad(-90), -- no effect since enableLimit is false
upperAngle = math.rad(45), -- no effect since enableLimit is false
maxMotorTorque = 10000.0,
motorSpeed = 10.0,
enableLimit = false,
enableMotor = true,
collideConnected = false,
}
local indexJoint = tPhysic:joint(tShapeBoxA,tShapeBoxB,tJoint)
25.3.2.9. Rope joint¶
Next, an example of joint using rope joint table.
require "box2d"
mbm.setColor(1,1,1)
tPhysic = box2d:new()
tShapeQuad = shape:new('2dw',-200, 720)
tShapeCircle1 = shape:new('2dw',-100, 400)
tShapeCircle2 = shape:new('2dw', 100, 400)
tShapeTriangle = shape:new('2dw', 50, 300)
tShapeGround = shape:new('2dw',0,-50)
tShapeWall = shape:new('2dw',300,100)
tShapeQuad:create('rectangle',100,100)
tShapeCircle1:create('circle',100,100)
tShapeCircle2:create('circle',80,80)
tShapeTriangle:create('triangle',100)
tShapeGround:create('rectangle',1000,20)
tShapeWall:create('rectangle',20,200)
local density, friction, restitution = 0.5, 10, 1.0
tPhysic:addDynamicBody(tShapeCircle1,density, friction, restitution)
tPhysic:addDynamicBody(tShapeCircle2,density, friction, restitution)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeTriangle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeWall)
tJoint =
{
name = 'rope',
localAnchorA = { x =-1.0, y = 0.0},
localAnchorB = { x = 1.0, y = 0.0},
--maxLength = 0.0, -- let the engine calculate the max length according to the current position of the bodies
collideConnected = false,
}
local indexJoint = tPhysic:joint(tShapeCircle1,tShapeCircle2,tJoint)
25.3.2.10. Weld joint¶
Next, an example of joint using weld joint table.
require "box2d"
mbm.setColor(1,1,1)
tPhysic = box2d:new()
tShapeQuad = shape:new('2dw',-200, 520)
tShapeCircle1 = shape:new('2dw',-80, 400)
tShapeCircle2 = shape:new('2dw', 100, 400)
tShapeTriangle = shape:new('2dw', 50, 300)
tShapeGround = shape:new('2dw',0,-300)
tShapeRightWall = shape:new('2dw',450,0)
tShapeLeftWall = shape:new('2dw',-450,0)
tShapeQuad:create('rectangle',100,100)
tShapeCircle1:create('circle',200,200,10) --low resolution of the circle
tShapeCircle2:create('circle',80,80)
tShapeTriangle:create('triangle',100)
tShapeGround:create('rectangle',1000,20)
tShapeRightWall:create('rectangle',20,400)
tShapeLeftWall:create('rectangle',20,400)
local density, friction, restitution = 0.5, 10, 1.0
tPhysic:addDynamicBody(tShapeCircle1,density, friction, restitution)
tPhysic:addDynamicBody(tShapeCircle2,density, friction, restitution)
tPhysic:addDynamicBody(tShapeQuad)
tPhysic:addDynamicBody(tShapeTriangle)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeLeftWall)
tPhysic:addStaticBody(tShapeRightWall)
local world_point_a = { x = 0, y = 0 }
local world_point_b = { x = 0, y = 0 }
world_point_a.x, world_point_a.y = tPhysic:getLocalPoint(tShapeQuad,0,0)
world_point_b.x, world_point_b.y = tPhysic:getLocalPoint(tShapeCircle1,0,0)
tJoint =
{
name = 'weld',
localAnchorA = world_point_a,-- If we omit local anchors A and B, the engine will use the second body to calculate it.
localAnchorB = world_point_b,-- If we omit local anchors A and B, the engine will use the second body to calculate it.
referenceAngle = tShapeQuad.az - tShapeCircle1.az, --if not supply, it will get the angle difference like this
frequencyHz = 0.0,
dampingRatio = 0.0,
collideConnected = false,
}
local indexJoint = tPhysic:joint(tShapeQuad,tShapeCircle1,tJoint)
25.3.2.11. Wheel joint¶
Next, an example of joint using wheel joint table.
require "box2d"
mbm.setColor(1,1,1)
tPhysic = box2d:new()
tPhysic:setScale(10)
tShapCar = shape:new('2dw', 0, 50)
tShapeWheel1 = shape:new('2dw',-50, 25)
tShapeWheel2 = shape:new('2dw', 50, 25)
tShapeGround = shape:new('2dw',0,-25)
tShapeGround:create('rectangle',1000,20)
tShapCar:create('rectangle',100,50)
tShapeWheel1:create('circle',50,50)
tShapeWheel2:create('circle',50,50)
tPhysic:addDynamicBody(tShapCar)
tPhysic:addDynamicBody(tShapeWheel1)
tPhysic:addDynamicBody(tShapeWheel2)
tPhysic:addStaticBody(tShapeGround)
-- Invert mass. See box2d tutorial the explanation
local massCar = tPhysic:getMass(tShapCar)
local massWheel = tPhysic:getMass(tShapeWheel1)
tPhysic:setMass(tShapeWheel1,massCar * 0.5)
tPhysic:setMass(tShapeWheel2,massCar * 0.5)
tPhysic:setMass(tShapCar, massWheel * 2)
tJoint =
{
name = 'wheel',
localAnchorA = { x = 0, y = 0 },
localAnchorB = { x = 0, y = 0 },
localAxisA = { x = 0, y = 1 },
enableMotor = true,
maxMotorTorque = 8000,
motorSpeed = 0.0,
frequencyHz = 3.0,
dampingRatio = 0.9,
collideConnected = false,
}
v1 = vec2:new()
v2 = vec2:new()
box_2d_scale = tPhysic:getScale()
--find the relative local of back wheel
v1:set(tShapCar.x,tShapCar.y)
v2:set(tShapeWheel1.x,tShapeWheel1.y)
v2:sub(v1)
v2:div(box_2d_scale)
tJoint.localAnchorA.x,tJoint.localAnchorA.y = v2.x,v2.y
local indexJoint1 = tPhysic:joint(tShapCar,tShapeWheel1,tJoint)
tShapCar.tJointWheel1 = tPhysic:getJoint(tShapeWheel1)
--find the relative local of front wheel
v1:set(tShapCar.x,tShapCar.y)
v2:set(tShapeWheel2.x,tShapeWheel2.y)
v2:sub(v1)
v2:div(box_2d_scale)
tJoint.localAnchorA.x, tJoint.localAnchorA.y = v2.x,v2.y
local indexJoint2 = tPhysic:joint(tShapCar,tShapeWheel2,tJoint)
tShapCar.tJointWheel2 = tPhysic:getJoint(tShapeWheel2)
motorSpeed = 500
direction_car = 0
function onKeyDown(key)
if mbm.getKeyCode('left') == key then
direction_car = 1
elseif mbm.getKeyCode('right') == key then
direction_car = -1
end
end
function onKeyUp(key)
if mbm.getKeyCode('left') == key or mbm.getKeyCode('right') == key then
direction_car = 0
end
end
function loop(delta,fpsProp)
tShapCar.tJointWheel1:setMotorSpeed(motorSpeed * direction_car)
tShapCar.tJointWheel2:setMotorSpeed(motorSpeed * direction_car)
end
25.3.4. Active options¶
- isActive¶
Available for all joints
Check if both bodies are active given the joint.
- Returns
boolean
- Both bodies are active?
Example:
local active = tJoint:isActive()
- setActive(boolean value)¶
Available for all joints
Set active option for both bodies given the joint.
- Parameters
boolean –
value
- Set Active for both bodies
Example:
tJoint:setActive(true)
25.3.5. Angular offset options¶
local angular_offset = tJoint:getAngularOffset()
- setAngularOffset(number angular_offset)¶
Available for motor.
- Parameters
number – angular_offset.
Example:
local angular_offset = math.rad(10)
tJoint:setAngularOffset(angular_offset)
25.3.6. Anchor options¶
- getAnchorA¶
- return
number
x -number
- y anchor on body ‘A’ in world coordinates.
Example:
local x,y = tJoint:getAnchorA()
- getAnchorB¶
- return
number
x -number
- y anchor on body ‘B’ in world coordinates.
Example:
local x,y = tJoint:getAnchorB()
25.3.7. Correction factor options¶
- getCorrectionFactor¶
Available for motor.
- Returns
number
- correction factor in range [0,1]
Example:
local correction = tJoint:getCorrectionFactor()
- setCorrectionFactor(table joint, number correction_factor)¶
Available for motor
- Parameters
number – correction factor in range [0,1]
Example:
local correction_factor = 0.5
tJoint:setCorrectionFactor(correction_factor)
25.3.8. Damping ratio options¶
- getDampingRatio¶
Available for distance , gear , wheel, weld .
- Returns
number
- Damping ratio
Example:
local damping_ratio = tJoint:getDampingRatio()
- setDampingRatio(number damping_ratio)¶
Available for distance , gear , wheel, weld .
- Parameters
number – damping ratio.
Example:
local damping_ratio = 2
tJoint:setDampingRatio(damping_ratio)
25.3.9. Destroy joint¶
- destroyJoint(tBody)¶
- Parameters
renderizable – body.
Example:
tPhysic:destroyJoint(tBody)
Note
25.3.10. Force options¶
- getMaxForce¶
Available for mouse, friction, motor, prismatic.
Max motor force for prismatic.
- Returns
number
- maximum friction force in newton.
Example:
local maximum_friction_force = tJoint:getMaxForce()
- setMaxForce(number max_friction_force)¶
Available for mouse, friction, motor, prismatic.
Max motor force for prismatic.Motor options joint
- Parameters
number – maximum friction force in newton.
Example:
local max_friction_force = 10
tJoint:setMaxForce(max_friction_force)
25.3.11. Frequency options¶
- setFrequencyHz(number hertz)¶
Available for distance , wheel, weld .
- Parameters
number – hertz.
Example:
local hertz = 30
tJoint:setFrequencyHz(hertz)
local hertz = tJoint:getFrequencyHz()
25.3.12. Get joint¶
- getJoint(tBody, number * absolute_index)¶
Retrieve a joint previously created passing any of body used to create th joint through the method joint and the absolute index (not mandatory).
- Parameters
renderizable – body.
number – absolute index of the joint in the engine. (this index can be invalid after destroying joint).
- Returns
table
- joint
Example:
tJoint = tPhysic:getJoint(tBody,indexMyJoint)
25.3.13. Length options¶
local length = tJoint:getLength()
local length = 10
tJoint:setLength(length)
25.3.14. Limit options¶
local enable = true
tJoint:enableLimit(enable)
local lower, upper = tJoint:getLimits()
- setLimits(number lower, number upper)¶
Available for revolute, prismatic.
- Parameters
number – lower.
number – upper.
Example:
local lower, upper = 10, 10
tJoint:setLimits(lower,upper)
25.3.15. Linear offset options¶
local x,y = tJoint:getLinearOffset()
- setLinearOffset(number x, number y)¶
Available for motor
- Parameters
number – x (Frame A in Meters).
number – y (Frame A in Meters).
Example:
local x,y = 10,15
tJoint:setLinearOffset(x,y)
25.3.16. Motor options¶
- getMotorSpeed¶
Available for revolute, prismatic, wheel.
- Returns
number
- speed in radian per second.
Example:
local motor_speed_rad = tJoint:getMotorSpeed() -- speed in radian per second
- setMotorSpeed(number speed)¶
Available for revolute, prismatic, wheel.
- Parameters
number – speed in radian per second.
Example:
local speed = math.rad(360)
tJoint:setMotorSpeed(speed)
local max_torque = tJoint:getMaxMotorTorque()
- setMaxMotorTorque(number max_torque)¶
Available for revolute, wheel, motor
- Parameters
number – max torque.
Example:
local torque = 5
tJoint:setMaxMotorTorque(torque)
- enableMotor(boolean value)¶
Available for revolute, prismatic, wheel.
- Parameters
boolean – value.
Example:
local enable = true
tJoint:enableMotor(enable)
25.3.17. Reaction options¶
- getReactionForce(number inv_delta)¶
Available for all joints
- Parameters
number – inv_delta.
- Returns
number
x -number
- y reaction force on body B at the joint anchor in Newtons.
Example:
local inv_delta = 1/60
local x,y = tJoint:getReactionForce(inv_delta)
- getReactionTorque(number inv_delta)¶
Available for all joints
- Parameters
number – inv_delta.
- Returns
number
- reaction torque on body B in N x m
Example:
local inv_delta = 1/60
local reaction_torque = tJoint:getReactionTorque(inv_delta)
25.3.18. Target options¶
- getTarget¶
- Available for mouse.You should consider the box2d scale to this method to transform to
2dw
coordinates.- Returns
number
x -number
- y
Example:
local box2d_scale = tPhysic:getScale()
local x,y = tJoint:getTarget()
x,y = x * box2d_scale, y * box2d_scale
- setTarget(number x, number y)¶
- Available for mouse.You should consider the box2d scale to this method.
- Parameters
number – x.
number – y.
Example:
-- part of example here
function onTouchDown(key,x,y)
x,y = mbm.to2dw(x,y)
local box2d_scale = tPhysic:getScale()
x,y = x / box2d_scale, y / box2d_scale
tJoint:setTarget(x,y)
end
25.4. box2d LiquidFun¶
LiquidFun is a 2D rigid-body and fluid simulation C++
library for games based upon Box 2D .
It provides support for procedural animation of physical bodies to make objects move and interact in realistic ways.
Discuss LiquidFun with other developers and users on the mailing list of LiquidFun.
Report issues on the issues tracker or post your questions to stackoverflow tagged with “liquidfun”.
Please take a look at LiquidFun Programmer’s Guide which is available for C++ but also applies to this wrapper over LUA
Some implementation in this wrapper is slightly different from the original version of C++
.
For example, is not available the manipulation of particle buffer directly through Lua
.
Next one example of what can be done with this feature:
Attention
This engine implements LiquidFun under feature/liquid_fun and feature/liquidFun_box2d-2.4.1 branch.
25.4.1. LiquidFun createFluid¶
The implementation of LiquidFun is a bit different from others object in this engine.
First, a renderizable is not available for the fluid, therefore, you do not need to create a separated object which represent the fluid. All you need is to configure a texture plus some parameters and the fluid is created and the engine will take care of render it.
Two tables are the arguments needed for creating a fluid , initial physical format, and options.
Next is presented the table definition for the initial physical format (which could be an array as well):
type /name sub tableinformation valuesinformation
rectangle
center
width
height
{x, y, z
}value
value
triangle
a
b
c
{x, y, z
}{x, y, z
}{x, y, z
}
circle
center
ray
{x, y, z
}value
e.g. single table:
{
type = 'rectangle',
center = {x=0,y=0,z=0},
width = 600,
height = 400,
}
{
type = 'circle',
center = {x=0,y=0,z=0},
ray = 200,
}
e.g. array table:
{
{
type = 'rectangle',
center = {x=0,y=0,z=0},
width = 600,
height = 400,
},
{
type = 'circle',
center = {x=0,y=0,z=0},
ray = 200,
}
}
And next is presented the definition for the options table:
identifier /name Typevalue Defaultvalue valuesinformation
position
table
{
x=0,y=0,z=0
} Initial position of the origin of center of particle
scale
table
{
x=1,y=1
} Initial scale of particle system.This reflect in the size of shape.E.g.: shape circleray=5
scale{x=2,y=2}
=>ray=10
color
table
{
r=1,g=1,b=1,a=1
} Color to be applied to the fluid shaderIf not present the shader will not contain particle on it
texture
string
#AAFF00FF
Path to the texture filename
is2dScreen
boolean
false
true
The fluid is 2d Screen coordinatefalse
The fluid is 2d World coordinate
is3d
boolean
false
true
The fluid is 3d World coordinatefalse
The fluid is 2d World/Screen coordinate
segmented
boolean
false
true
The texture is mapped to the whole physicsfalse
The texture is mapped to a single particle
radius
number
0.5
radius of each particle (physics)
radiusScale
number
1.0
1.0
means no scale.This scale is only for the particle (not the physics)
damping
number
0.5
Reduces velocity along the collision normalSmaller value reduces less
stride
number
0
The interval of particles in the shape.If0
,0.75
* particle diameter is used
strength
number
1.0
The strength of cohesion among the particlesin a group with flag elastic or spring
lifetime
number
0.0
Lifetime of the particle in seconds.A value <=0.0
indicates an infinite life time.
angle
number
0.0
The initial rotational angle of the particle.
angularVelocity
number
0.0
The initial angular velocity of particle
linearVelocity
table
{
x=0,y=0
} Initial Linear velocity of particle system
blend
string
one
Blend options. See blend state
operation
string
add
Blend options. See blend operation
flags
string
water
Single flag or combined. See fluid flags
groupFlags
string
solidParticleGroup
Single group flag or combined. See fluid group flags
For the next examples, it will be used this texture:
Example:
require "box2d"
mbm.setColor(0.6,0.6,0.6)
tPhysic = box2d:new()
tShapeGround = shape:new('2dw',0,-200)
tShapeLeftWall = shape:new('2dw',-200,0)
tShapeRightWall = shape:new('2dw',200,0)
tShapeUpWall = shape:new('2dw',0,200)
tShapeGround:create('rectangle',400,20)
tShapeRightWall:create('rectangle',20,400)
tShapeLeftWall:create('rectangle',20,400)
tShapeUpWall:create('rectangle',400,20)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
tPhysic:addStaticBody(tShapeLeftWall)
tPhysic:addStaticBody(tShapeUpWall)
tFluid = tPhysic:createFluid(
{ type = 'rectangle',
center = {x=0,y=0,z=0},
width = 200, height = 400,
},
{ texture ='fluid_particle.png',
color = {r = 0.0, g = 0.0, b =1.0},
radiusScale = 2.0,
flags = "water",
groupFlags = {"solidParticleGroup"},
})
Tip
Example 2 no color:
require "box2d"
mbm.setColor(0.6,0.6,0.6)
tPhysic = box2d:new()
tShapeGround = shape:new('2dw',0,-200)
tShapeLeftWall = shape:new('2dw',-200,0)
tShapeRightWall = shape:new('2dw',200,0)
tShapeUpWall = shape:new('2dw',0,200)
tShapeGround:create('rectangle',400,20)
tShapeRightWall:create('rectangle',20,400)
tShapeLeftWall:create('rectangle',20,400)
tShapeUpWall:create('rectangle',400,20)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
tPhysic:addStaticBody(tShapeLeftWall)
tPhysic:addStaticBody(tShapeUpWall)
tFluid = tPhysic:createFluid(
{ type = 'rectangle',
center = {x=0,y=0,z=0},
width = 200, height = 400,
},
{ texture ='fluid_particle.png',
radiusScale = 2.0,
flags = "water",
groupFlags = {"solidParticleGroup"},
})
Note
Example 3 array of physical shape:
require "box2d"
mbm.setColor(0.6,0.6,0.6)
tPhysic = box2d:new()
tShapeGround = shape:new('2dw',0,-200)
tShapeLeftWall = shape:new('2dw',-200,0)
tShapeRightWall = shape:new('2dw',200,0)
tShapeUpWall = shape:new('2dw',0,200)
tShapeGround:create('rectangle',400,20)
tShapeRightWall:create('rectangle',20,400)
tShapeLeftWall:create('rectangle',20,400)
tShapeUpWall:create('rectangle',400,20)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
tPhysic:addStaticBody(tShapeLeftWall)
tPhysic:addStaticBody(tShapeUpWall)
tFluid = tPhysic:createFluid(
{
{ type = 'rectangle',
center = {x=-50,y=0,z=0},
width = 50,
height = 50,
},
{ type = 'circle',
center = {x=50,y=30,z=0},
ray = 50,
},
},
{ texture ='fluid_particle.png',
flags = "powder",
groupFlags = {"solidParticleGroup"},
})
25.4.2. LiquidFun flags¶
The particle flag can be combined as array.
name informationCompatible
water
Water particleYes
zombie
Removed after next simulation stepYes
wall
Zero velocityYes
spring
With restitution from stretchingYes
elastic
With restitution from deformationYes
viscous
With viscosityYes
powder
Without isotropic pressureYes
tensile
With surface tensionYes
colorMixing
Mix color between contacting particlesNo
destructionListener
Call b2DestructionListener on destructionNo
barrier
Prevents other particles from leakingYes
staticPressure
Less compressibilityYes
reactive
Makes pairs or triads with other particlesYes
repulsive
With high repulsive forceYes
25.4.3. LiquidFun group flags¶
The particle group flag can be combined as array.
name informationCompatible
solidParticleGroup
Prevents overlapping or leakingYes
rigidParticleGroup
Keeps its shapeYes
particleGroupCanBeEmpty
Won’t be destroyed if it gets emptyYes
particleGroupWillBeDestroyed
Will be destroyed on next simulation stepYes
particleGroupNeedsUpdateDepth
Updates depth data on next simulation stepNo
25.4.4. LiquidFun flags examples¶
To exemplify how to differ the flags, see the examples bellow:
For the fluid, we will use this texture:
require "box2d"
mbm.setColor(0.6,0.6,0.6)
tPhysic = box2d:new()
tShapeGround = shape:new('2dw',0,-200)
tShapeLeftWall = shape:new('2dw',-200,0)
tShapeRightWall = shape:new('2dw',200,0)
tShapeUpWall = shape:new('2dw',0,200)
tShapeGround:create('rectangle',400,20)
tShapeRightWall:create('rectangle',20,400)
tShapeLeftWall:create('rectangle',20,400)
tShapeUpWall:create('rectangle',400,20)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
tPhysic:addStaticBody(tShapeLeftWall)
tPhysic:addStaticBody(tShapeUpWall)
tFluid = tPhysic:createFluid(
{ type = 'rectangle',
center = {x=0,y=0,z=0},
width = 200,
height = 200,
},
{ texture ='fluid_particle.png',
flags = "water",
groupFlags = {"solidParticleGroup"},
})
require "box2d"
mbm.setColor(0.6,0.6,0.6)
tPhysic = box2d:new()
tShapeGround = shape:new('2dw',0,-200)
tShapeLeftWall = shape:new('2dw',-200,0)
tShapeRightWall = shape:new('2dw',200,0)
tShapeUpWall = shape:new('2dw',0,200)
tShapeGround:create('rectangle',400,20)
tShapeRightWall:create('rectangle',20,400)
tShapeLeftWall:create('rectangle',20,400)
tShapeUpWall:create('rectangle',400,20)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
tPhysic:addStaticBody(tShapeLeftWall)
tPhysic:addStaticBody(tShapeUpWall)
tFluid = tPhysic:createFluid(
{ type = 'rectangle',
center = {x=0,y=0,z=0},
width = 200,
height = 200,
},
{ texture ='fluid_particle.png',
flags = {"water","staticPressure"},
groupFlags = {"solidParticleGroup"},
})
require "box2d"
mbm.setColor(0.6,0.6,0.6)
tPhysic = box2d:new()
tShapeGround = shape:new('2dw',0,-200)
tShapeLeftWall = shape:new('2dw',-200,0)
tShapeRightWall = shape:new('2dw',200,0)
tShapeUpWall = shape:new('2dw',0,200)
tShapeGround:create('rectangle',400,20)
tShapeRightWall:create('rectangle',20,400)
tShapeLeftWall:create('rectangle',20,400)
tShapeUpWall:create('rectangle',400,20)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
tPhysic:addStaticBody(tShapeLeftWall)
tPhysic:addStaticBody(tShapeUpWall)
tFluid = tPhysic:createFluid(
{ type = 'rectangle',
center = {x=0,y=0,z=0},
width = 200,
height = 200,
},
{ texture ='fluid_particle.png',
flags = "elastic",
groupFlags = {"solidParticleGroup"},
})
require "box2d"
mbm.setColor(0.6,0.6,0.6)
tPhysic = box2d:new()
tShapeGround = shape:new('2dw',0,-200)
tShapeLeftWall = shape:new('2dw',-200,0)
tShapeRightWall = shape:new('2dw',200,0)
tShapeUpWall = shape:new('2dw',0,200)
tShapeGround:create('rectangle',400,20)
tShapeRightWall:create('rectangle',20,400)
tShapeLeftWall:create('rectangle',20,400)
tShapeUpWall:create('rectangle',400,20)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
tPhysic:addStaticBody(tShapeLeftWall)
tPhysic:addStaticBody(tShapeUpWall)
tFluid = tPhysic:createFluid(
{ type = 'rectangle',
center = {x=0,y=0,z=0},
width = 200,
height = 200,
},
{ texture ='fluid_particle.png',
flags = "viscous",
groupFlags = {"solidParticleGroup"},
})
require "box2d"
mbm.setColor(0.6,0.6,0.6)
tPhysic = box2d:new()
tShapeGround = shape:new('2dw',0,-200)
tShapeLeftWall = shape:new('2dw',-200,0)
tShapeRightWall = shape:new('2dw',200,0)
tShapeUpWall = shape:new('2dw',0,200)
tShapeGround:create('rectangle',400,20)
tShapeRightWall:create('rectangle',20,400)
tShapeLeftWall:create('rectangle',20,400)
tShapeUpWall:create('rectangle',400,20)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
tPhysic:addStaticBody(tShapeLeftWall)
tPhysic:addStaticBody(tShapeUpWall)
tFluid = tPhysic:createFluid(
{ type = 'rectangle',
center = {x=0,y=0,z=0},
width = 200,
height = 200,
},
{ texture ='fluid_particle.png',
flags = "powder",
groupFlags = {"solidParticleGroup"},
})
25.5. LiquidFun methods¶
25.5.1. LiquidFun add particles¶
- add(table shape, table infoPosScale)¶
- Add particles given the shape. See initial physical format.Info position and scale is defined as:fieldInformation
x
y
z
sx
sy
sz
Positionx
Positiony
Positionz
Scalex
Scaley
Scalez
- Parameters
table – shape physical format.
table – info position and scale.
- Returns
number
particles added.
Example:
require "box2d"
mbm.setColor(0.6,0.6,0.6)
tPhysic = box2d:new()
tShapeGround = shape:new('2dw',0,-200)
tShapeLeftWall = shape:new('2dw',-200,0)
tShapeRightWall = shape:new('2dw',200,0)
tShapeUpWall = shape:new('2dw',0,200)
tShapeGround:create('rectangle',400,20)
tShapeRightWall:create('rectangle',20,400)
tShapeLeftWall:create('rectangle',20,400)
tShapeUpWall:create('rectangle',400,20)
tPhysic:addStaticBody(tShapeGround)
tPhysic:addStaticBody(tShapeRightWall)
tPhysic:addStaticBody(tShapeLeftWall)
tPhysic:addStaticBody(tShapeUpWall)
tFluid = tPhysic:createFluid(
{ type = 'rectangle',
center = {x=0,y=0,z=0},
width = 200,
height = 200,
},
{ texture ='fluid_particle.png',
flags = "powder",
groupFlags = {"solidParticleGroup"},
})
function onTouchDown(key,x,y)
local x,y = mbm.to2dw(x,y)
local tCircle =
{
type = 'circle',
ray = 20,
}
local infoPosScale =
{
x = x,
y = y,
sx = 1,
sy = 1,
}
local iTotalParticleAdded = tFluid:add(tCircle,infoPosScale)
print('Total particle added:',iTotalParticleAdded)
end
25.5.2. LiquidFun particle count¶
- getParticleCount¶
Get the number of particles.
- return
number
particles count.
Example:
local iTotalParticle = tFluid:getParticleCount()
print(iTotalParticle)
- getMaxParticleCount¶
Get the maximum number of particles.
- Returns
number
maximum number of particles.
Example:
local iMaxParticle = tFluid:getMaxParticleCount()
print(iMaxParticle)
- setMaxParticleCount(number max)¶
- Set the maximum number of particles.By default, there is no maximum. The particle buffers can continue togrow while b2World’s block allocator still has memory.
- Parameters
number – max maximum number of particles.
Example:
tFluid:setMaxParticleCount(500)
25.5.3. LiquidFun particle color¶
- getColor¶
- Get the particle’s color.It only has effect when created with color option.
- Returns
number
red,number
green,number
blue,number
alpha - color of fluid.
Example:
local r,g,b,a = tFluid:getColor()
- setColor(number red, number green, number blue, number * alpha)¶
- Set the particle’s color.It only has effect when created with color option.
- Parameters
number – red color.
number – green color.
number – blue color.
number – alpha color (optional).
Example:
local r,g,b,a = 1.0, 0.4, 0.3, 0.9
tFluid:setColor(r,g,b,a)
- setColor(table color)¶
- Set the particle’s color.It only has effect when created with color option.
- Parameters
table – color
{r,g,b,a}
.
Example:
local tColor = {r=1.0, g=1.0, b=0.4, a=0.3}
tFluid:setColor(tColor)
25.5.4. LiquidFun particle pause¶
- setPaused(boolean paused)¶
- Pause or unpause the particle system.When paused, b2World::Step() skips over this particle system.All b2ParticleSystem function calls still work.
- Parameters
boolean – paused value.
Example:
tFluid:setPaused(true)
25.5.5. LiquidFun particle density¶
- getDensity¶
- Get the particle density.
- Returns
number
density
Example:
local density = tFluid:getDensity()
- setDensity(number density)¶
- Change the particle density.Particle density affects the mass of the particles, which in turnaffects how the particles interact with b2Bodies. Note that the densitydoes not affect how the particles interact with each other.
- Parameters
number – density
Example:
local density = tFluid:getDensity()
tFluid:setDensity(density * 2.0)
25.5.6. LiquidFun particle gravity scale¶
- getGravityScale¶
- Get the particle gravity scale.
- Returns
number
gravity scale
Example:
local gravity_scale = tFluid:getGravityScale()
- setGravityScale(number gravity_scale)¶
- Change the particle gravity scale.Adjusts the effect of the global gravity vector on particles.
- Parameters
number – gravity scale
Example:
local gravity_scale = tFluid:getGravityScale()
tFluid:setGravityScale(gravity_scale * 2.0)
25.5.7. LiquidFun particle damping¶
- getDamping¶
- Get damping for particles
- Returns
number
damping
Example:
local damping = tFluid:getDamping()
- setDamping(number damping)¶
- Damping is used to reduce the velocity of particles.The damping parameter can be larger than
1.0
but the dampingeffect becomes sensitive to the time step when the damping parameter is large.- Parameters
number – damping
Example:
tFluid:setDamping(0.5)
25.5.8. LiquidFun particle static pressure¶
Change the number of iterations when calculating the static pressure ofparticles. By default,8
iterations. You can reduce the number ofiterations down to1
in some situations, but this may causeinstabilities when many particles come together. If you see particlespopping away from each other like popcorn, you may have to increase thenumber of iterations.For a description of static pressure, see
- getStaticPressureIterations¶
- Get the number of iterations for static pressure of particles.
- Returns
number
iterations for static pressure of particles
Example:
local iterations = tFluid:getStaticPressureIterations()
- setStaticPressureIterations(number iterations)¶
- Parameters
number – iterations
Example:
tFluid:setStaticPressureIterations(3)
25.5.9. LiquidFun particle radius¶
The radius is defined in the moment of creation of fluid.
- getRadius¶
- Get the particle radius.
- Returns
number
particle radius
Example:
local radius = tFluid:getRadius()
25.5.10. LiquidFun particle life time¶
All particules are created with0.0
lifetime (infinite lifetime).A value >0.0
is returned if the particle is scheduled to bedestroyed in the future, values <=0.0
indicate the particle has aninfinite lifetime.
- getParticleLifetime(number index)¶
- Get the lifetime (in seconds) of a particle relative to the current time.
- Parameters
number – index of particle (1 based).
- Returns
number
lifetime
Example:
local index = 1 -- first particle in the system
local lifetime = tFluid:getParticleLifetime(index)
- setParticleLifetime(number index, number lifetime)¶
- Set the lifetime (in seconds) of a particle relative to the current time.A lifetime of less than or equal to 0.0f results in the particleliving forever until it’s manually destroyed by the application.
- Parameters
number – index of particle (1 based).
number – lifetime of particle in seconds.
Example:
local index = 1 -- first particle in the system
local lifetime = 2.0
tFluid:setParticleLifetime(index,lifetime)
25.5.11. LiquidFun destroy particles¶
- destroyParticle(number index)¶
- Destroy a particle.The particle is removed after the next simulation step (see b2World::Step()).
- Parameters
number – index of particle (1 based).
Example:
local index = 1 -- first particle in the system
tFluid:destroyParticle(index)
- destroyParticlesInShape(table shape)¶
- Destroy particles inside a shape without enabling the destruction callback for destroyed particles.This function is locked during callbacks.For more information see DestroyParticleInShape(const b2Shape&, const b2Transform&,bool).
- Parameters
table – shape which encloses particles that should be destroyed.
Example:
local tShapeRect = {type = 'rectangle',
width = 100,
height = 100,
x = 0, y =0, angle = 0}
tFluid:destroyParticlesInShape(tShapeRect)
local tShapeCircle = {type = 'circle',
ray = 30,
x = 0, y =0, angle = 0}
tFluid:destroyParticlesInShape(tShapeCircle)
local tShapeTriangle = {type = 'triangle',
a = {x = -20, y = -20},
b = {x = 20, y = -20},
c = {x = 20, y = 20},
x = 0, y =0, angle = 0}
tFluid:destroyParticlesInShape(tShapeTriangle)
Note
The position x
, y
and the angle
are available for any type of shape.
- destroyParticlesInShape(renderizable body)¶
- Destroy particles inside a shape without enabling the destruction callback for destroyed particles.This function is locked during callbacks.For more information see DestroyParticleInShape(const b2Shape&, const b2Transform&,bool).
- Parameters
renderizable – body which encloses particles that should be destroyed.
Example:
local tObj -- previously loaded
tFluid:destroyParticlesInShape(tObj)
25.5.12. LiquidFun particles impulse¶
- particleApplyLinearImpulse([table | array] tImpulse)¶
- Apply an impulse to one particle. This immediately modifies the velocity. Similar to b2Body::applyLinearImpulse.
- Parameters
table –
{x,y,index}
the impulse info for each particle (or array).
Example:
local tImpulse = {x = 100, y = 25, index = 1}
tFluid:particleApplyLinearImpulse(tImpulse)
local tArraysImpulse = {{x = 100, y = 25, index = 1},{x = 100, y = 25, index = 2},{x = 100, y = 25, index = 3}}
tFluid:particleApplyLinearImpulse(tArraysImpulse)
- applyLinearImpulse(number firstIndex, number lastIndex, number x, number y)¶
- Apply an impulse to all particles between ‘firstIndex’ and ‘lastIndex’.This immediately modifies the velocity.Note that the impulse is applied to the total mass of all particles. So,calling particleApplyLinearImpulse(0, impulse) and particleApplyLinearImpulse(1, impulse)will impart twice as much velocity as calling just applyLinearImpulse(0, 1, impulse).
- Parameters
number –
firstIndex
(1 based)number –
lastIndex
(1 based)number –
x
impulsenumber –
y
impulse
Example:
local firstIndex = 1
local lastIndex = 250
local x,y = 100, 25
tFluid:applyLinearImpulse(firstIndex,lastIndex,x,y)
25.5.13. LiquidFun particles force¶
- particleApplyForce([table | array] tForce)¶
- Apply a force to the center of a particle.The world force vector is usually in Newtons (N).
- Parameters
table –
{x,y,index}
the force info for each particle (or array).
Example:
local tForce = {x = 50, y = 33, index = 1}
tFluid:particleApplyForce(tForce)
local tArrayForce = {{x = 50, y = 33, index = 1},{x = 50, y = 33, index = 2},{x = 50, y = 33, index = 3}}
tFluid:particleApplyForce(tArrayForce)
- applyForce(number firstIndex, number lastIndex, number x, number y)¶
- Distribute a force across several particles.The particles must not be wall particles.Note that the force is distributed across all the particles, so callingthis function for indices 0..N is not the same ascalling particleApplyForce(i, force) for i in 0..N.
- Parameters
number –
firstIndex
(1 based)number –
lastIndex
(1 based)number –
x
forcenumber –
y
force
Example:
local firstIndex = 1
local lastIndex = 300
local fx,fy = 50, 33
tFluid:applyForce(firstIndex,lastIndex,fx,fy)
25.5.14. LiquidFun particles colision¶
- queryAABB(number lowerBound_x, number lowerBound_y, number upperBound_x, number upperBound_y, function onQueryAABBB)¶
Perform a AABB algorithm collision for all objects given the bound.
The callback is called in case of collision.
It has to have the following signature:
function onQueryAABBB(tFluid, indexParticle)
end
Example:
-- TODO
- queryShapeAABB(table shape, function onQueryShapeAABB)¶
Perform a AABB algorithm collision for all objects given the shape.
The callback is called in case of collision.
It has to have the following signature:
function onQueryShapeAABB(tFluid, indexParticle)
end
The shape is defined as:
type /name fieldname valuesinformation
rectangle
width
height
x
y
angle
value
value
x position
y position
radian
triangle
a
b
c
x
y
angle
{x, y
}{x, y
}{x, y
}x position
y position
radian
circle
ray
x
y
angle
value
x position
y position
radian
Example:
-- TODO
- queryShapeAABB(table renderizable, function onQueryShapeAABB)¶
Perform a AABB algorithm collision for all objects given the renderizable.
The callback is called in case of collision.
It has to have the following signature:
function onQueryShapeAABB(tFluid, indexParticle)
end
Example:
-- TODO
25.5.15. LiquidFun particles compute bounds¶
- computeAABB(number lowerBound_x, number lowerBound_y, number upperBound_x, number upperBound_y)¶
- Compute the axis-aligned bounding box for all particles contained within this particle system.Returns the axis-aligned bounding box of the system.
- Parameters
number – lowerBound_x
number – lowerBound_y
number – upperBound_x
number – upperBound_y
- Returns
table
{lowerBound = {x,y},upperBound = {x,y}}
Example:
local lowerBound_x = 0
local lowerBound_y = 0
local upperBound_x = 100
local upperBound_y = 100
local tBound = tFluid:computeAABB(lowerBound_x,lowerBound_y,upperBound_x,upperBound_y)
print(tBound.lowerBound.x)
print(tBound.lowerBound.y)
print(tBound.upperBound.x)
print(tBound.upperBound.y)
25.5.16. LiquidFun particles ray cast¶
- rayCast(number x_start, number y_start, number x_end, number y_end, function onRayCastCallBack)¶
- Parameters
number – x start point of the ray-cast.
number – y start point of the ray-cast.
number – x end point of the ray-cast.
number – y end point of the ray-cast.
function – call back function ray-cast.
--Ray cast callback
function onRayCast(index,x,y,nx,ny,fraction)
message = string.format('Particle: %d, at x:%g y:%g normal nx:%g ny:%g fraction:%g',index,x,y,nx,ny,fraction)
print(message)
return fraction --if you return 0 it means end of ray-cast
end