The following function adds a new rigid body to the world. The first two parameters (x and y) define the position of our new body. The third parameter (a) is the initial angle or rotation of the body in radians. Linear and angular damping (ld and ad) are optional values that slow down the body's velocity over time. Since Box2D does not simulate the effects of air resistance, a moving body with 0 damping will continue moving until it collides with another body. The greater the damping value, the faster the moving body will grind to a halt. The rest of the parameters are not required and get a bit technical so please refer to the official Box2D documentation if you want to learn more.
-- simplify body and shape creation local def = b2.BodyDef ( ) function b2.World:NewBody ( type, x, y, a, ld, ad, fr, ib, as, is ) def.type = type def.position:Set( x, y ) def.angle = a or 0 def.linearDamping = ld or 0 def.angularDamping = ad or 0 def.fixedRotation = fr or false def.bullet = ib or false def.allowSleep = as or false def.awake = is or true return self:CreateBody ( def ) end
The next couple of functions are meant to simplify adding new shapes to a body. Rectangles take in a width and height (w and h) and circles require a radius and a local position (lx and ly). I have taken the liberty of adding a couple of 'assert' calls that will raise a Lua error if you specify a bad parameter. Keep in mind that Box2D works with meters so don't supply a radius of 100 if you plan on simulating a tennis ball. The density, friction and restitution parameters (d, f and r) affect how bodies respond when colliding with each other. A shape with 0 density (or zero mass) is considered static and won't move at all. The greater the density, the more massive the shape will become and the more force will be required to accelerate it. Friction affects how slippery the surface of your shape is and must be between 0 and 1. A shape with 0 friction will slide along the surface of other shapes. Restitution measures the elasticity of a shape and should also be between 0 and 1. A restitution of 0 means that your shape will bounce off other shapes without losing (almost) any velocity. The last parameter (is) specifies if your shape is a 'sensor'. Sensor shapes don't respond to collisions at all and will simply pass through other shapes.
local def = b2.PolygonShape ( ) local fix = b2.FixtureDef ( ) function b2.Body:NewBox ( w, h, d, f, r, is ) assert ( w > 0 and h > 0 ) def:SetAsBox ( w, h ) fix.density = d or 0 fix.friction = f or 0 fix.restitution = r or 0 fix.isSensor = is or false fix.shape = def return self:CreateFixture ( fix ) end local def = b2.CircleShape ( ) local fix = b2.FixtureDef ( ) function b2.Body:NewCircle ( radius, lx, ly, d, f, r, is ) assert ( radius > 0 ) def.radius = radius def.position:Set( lx or 0, ly or 0 ) fix.density = d or 0 fix.friction = f or 0 fix.restitution = r or 0 fix.isSensor = is or false fix.shape = def return self:CreateFixture ( fix ) end
|Get function||Units||Set function|
|p||GetPosition ( )||meters (m)||SetTransform ( position, angle )|
|GetAngle ( )||radians|
|GetMass ( )||kilograms (kg)||SetMassData ( massData )|
|v||GetLinearVelocity ( )||meters/second||SetLinearVelocity ( velocity )|
|w||GetAngularVelocity ( )||radians/second||SetAngularVelocity ( velocity )|
|GetInertia ( )||kg-mē|
Each rigid body in Box2D updates its position and angle based on its current linear and angular velocity. Let's look at some pseudo-code to better understand how it works:
futurePosition = position + ( linearVelocity + gravity ) * ( 1 - linearDamping )
futureAngle = angle + angularVelocity * ( 1 - angularDamping )
The real code is a bit more elaborate, but I just wanted to illustrate how gravity and damping affect the velocity of a body. The damping parameter can be larger than 1 at which point the damping effect becomes increasingly sensitive to the time step.
|ApplyForce ( force, position )||Newtons (N)||Apply a 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|
|ApplyForceToCenter ( force )||Newtons (N)||Apply a force to the center of mass|
|ApplyLinearImpulse ( impulse, position )||N-seconds or kg-m/s||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|
|ApplyAngularImpulse ( impulse )||kg*mē/s||Apply an angular impulse|
|ApplyTorque ( torque )||N-meters||Apply a torque. This affects the angular velocity without affecting the linear velocity of the center of mass|
Now, we are going to make a controllable body within our Box2D simulation. In particular we will be using the "ApplyForce" function to move a circle shape whenever the player presses a keyboard button. The ApplyForce function takes in a Newtonian force vector and a position where that force is to be applied. In this case, we want to apply the force directly to the center of mass of the player body, otherwise a torque effect will be produced. The player body has a density of 0.1 kg/mē so notice that if this value was higher we would need a greater force in order to move the body. Also take note that I've set a linear damping of 2 for the player body so that it decelerates nicely when the keys are released. For rendering the the simulation, I have used a few function from the last physics tutorial.
-- create a new world gravity = b2.Vec2 ( 0, 0 ) world = b2.World ( gravity ) player = world:NewBody ( "dynamicBody", 0, 0, 0, 2, 0, false, false, false, false ) player:NewCircle ( 1, 0, 0, 0.1, 0.5, 0.5, false ) player:OnCreate ( ) timer = Timer ( ) timer:start ( 16, true ) timer.on_tick = function ( timer ) -- check for player input local position = b2.Vec2 ( ) player:GetPosition ( position ) if keyboard:is_down ( KEY_UP ) == true then player:ApplyForce ( b2.Vec2 ( 0, 10 ), position, true ) end if keyboard:is_down ( KEY_DOWN ) == true then player:ApplyForce ( b2.Vec2 ( 0, -10 ), position, true ) end if keyboard:is_down ( KEY_LEFT ) == true then player:ApplyForce ( b2.Vec2 ( -10, 0 ), position, true ) end if keyboard:is_down ( KEY_RIGHT ) == true then player:ApplyForce ( b2.Vec2 ( 10, 0 ), position, true ) end -- update the physics simulation local seconds = timer:get_delta_ms ( ) / 1000 world:Step ( seconds, 10, 8 ) -- iterate all bodies and update their sprites local body = world:GetBodyList ( ) while body do body:OnUpdate ( ) body = body:GetNext ( ) end end