Corona – Physics

Corona Physics

Corona incorporates the Box2d physics engine. The physics engine allow you create objects that move and interact like real physical objects. The engine works in flat 2d space.

The Corona implementation of Box2d is simple and intuitive to use. making it incredibly easy to add physics based motion to any project.

Starting physics

To work with physics you’ll to load the physics library and start the physics engine.

Start the physics engine by loading it with:

local physics = require( "physics" )

From here you’ll create physics objects and control the general physics environment through the variable physics defined in the line of code above.

Then start physics with:

physics.start()

Note: You must call start physics, with the line above doing anything else with physics.

Physics Bodies

Bodies are objects that move and react to other physics bodies like real world physical objects.

The physics engine uses a body to simulate the motion and collisions. A display object is attached to the body and positioned on the screen based on the simulation.

Bodies can be made from any other display object. This includes images, sprites, text, shapes etc.

Bodies have properties that determine how they move and react to other bodies. These properties include friction and bounce. Setting the values of these properties determines how an object will react and move when it collides with another body.

Bodies also come in several different types. The body type determines how the object will react in the simulation and interact with other bodies.

The types are:

  • Static – bodies don’t move. Use this body type to simulate walls, floor, platforms and other objects that don’t move.
  • Dynamic – bodies are effected by gravity and collisions with other bodies. These ‘’are the objects that move and collide with other objects in your physics word.
  • Kinematic – bodies are effect by collisions with other bodies but, not by gravity. This type of body is good objects that you need to drag.

Simple physics example

Here’s a simple example that illustrates the use of static and dynamic objects and could work as the set up for many projects that might use physics.

The example creates two walls, a floor, a platform and a box. The walls, floor and platform are static objects and as such do not move. The box is a dynamic object that moves and collides with the other objects.

Build this example for yourself. With this example you can test a variety of features in the physics system.

local physics = require( "physics" )
physics.start() -- Start physics
-- make floor
local floor = display.newRect( 0, display.contentHeight - 15, display.contentWidth, 30 )
-- make left wall
local left_wall = display.newRect( -15, 0, 30, display.contentHeight )
-- make right wall
local right_wall = display.newRect( display.contentWidth - 15, 0, 30, display.contentHeight )
-- Make platform
local platform = display.newRect( display.contentCenterX - 25, display.contentCenterY -15, 50, 30)

-- Make box
local box = display.newRect( 0, 0, 50, 50 )
box.x = 120
box.y = 50
-- Add physics properties to all objects
-- Note: Physics must be started first! (see line 11)
-- These objects are all static. Static objects don't move.
-- Other objects react to collisions with static objects.
physics.addBody( floor, "static", {friction=0.5, bounce=0.3} )
physics.addBody( left_wall, "static", {friction=0.5, bounce=0.3} )
physics.addBody( right_wall, "static", {friction=0.5, bounce=0.3} )
physics.addBody( platform, "static", {friction=0.5, bounce=0.3} )
-- The box is a dynamic object. Dynamic objects move as if they were physical objects.
physics.addBody( box, "dynamic", {density=3.0, friction=0.5, bounce=0.3} )

Test the example. You should see the box fall down the screen bouncing off the platform walls and floor. What’s going on here?
At the top of the script I imported physics and started the physics simulation with:

local physics = require( "physics" )
physics.start()

You can see all of the display objects defined at the top are standard shape objects. I used display.newRect() to create all of the objects.

Next I created a physics body out of each of the shapes using physics.addBody(). The addBody() method takes several parameters:

physics.addBody( object, type, {table of properties and values} )

The first parameter object, is a reference to a display object. This can be any type of display object: shape, sprite, image etc.

The second parameter sets the type of physics body to create. In this example I have used “dynamic” for the box. The other objects are all “static”. Notice how these objects act as solid unmoving objects in the simulation.

Parameters assigned to each object when it is created determine how that object reacts when it collides with other objects in the simulation.

Note: Objects that do not have a physics body are ignored by the physics simulation and have no effect on physics bodies. Physics bodies are created with physics.addBody().

The parameters determine the physical properties of an object and how it looks in the simulation. Imagine the difference between a bowling ball and a pingpong ball. Understanding these properties is best done by experimentation.

In this example the walls and platform are not moving. So we’ll change only properties of the box.

Friction – this determines how much energy is lost as an object slides along brushes against another object. Test the example with original value of 0.5. Observe how the box moves.

Change the value for friction to 0.1 and test again. Notice the box moves a little more. Losing less energy to collisions with the walls and platform.

physics.addBody( box, "dynamic", {density=3.0, friction=0.1, bounce=0.3} )

Bounce – this determines how resilient/elastic an object is. Or you could think it determines the bounciness of an object. This is setting how much energy an object loses when it hits a surface and how much energy it takes with it after a collision. Compare a rubber ball and wooden ball. Imagine how they would bounce. Try setting bounce to 0.8.

physics.addBody( box, "dynamic", {density=3.0, friction=0.5, bounce=0.8} )

With this value the object conserves most of it’s energy with each collision. Test some other values for fun.

Gravity

Gravity is the force that pulls an object toward the bottom of the screen. The default gravity has a positive Y value. Imagine the physics simulation is adding to the Y of each physics body moving it down the screen. Gravity can be set in both the X and Y directions. It can also be set to negative values.

Positive values for Y would pull objects down to the bottom of the screen. Positive values for X would pull objects to the right of the screen. Using  value of 0 for both X and Y would remove the effect of gravity from the simulation.

Use the setGravity() method to set gravity in the simulation. For example:

physics.setGravity( 0, 10 )

The above example is setting gravity in the X and Y. By default the gravity is 0 in the X and 10 in the Y. Imagine that objects are being pulled down 10 pixels each frame in the Y. This is not strictly accurate but illustrates the idea.

Try changing the values for gravity. Here’s a few to test for fun:

  • physics.setGravity( 0, 20 ) — Stronger gravity, things fall faster.
  • physics.setGravity( 0, 2 ) — Weak gravity, things fall slowly like the moon.
  • physics.setGravity( 10, 0 ) — Things fall to the right.

 

Add some more Objects

To get a better idea of how objects will react try adding more objects to the first example. Try it on your own. The code below creates three boxes.

local physics = require( "physics" )
physics.start() -- Start physics

-- make floor
local floor = display.newRect( 0, display.contentHeight - 15, display.contentWidth, 30 )
-- make left wall
local left_wall = display.newRect( -15, 0, 30, display.contentHeight )
-- make right wall
local right_wall = display.newRect( display.contentWidth - 15, 0, 30, display.contentHeight )

-- Make platform
local platform = display.newRect( display.contentCenterX - 25, display.contentCenterY -15, 50, 30)

-- Make 3 boxes
local box_1 = display.newRect( 0, 0, 50, 50 )
box_1.x = 120
box_1.y = 50

local box_2 = display.newRect( 0, 0, 50, 50 )
box_2.x = 50
box_2.y = 10

local box_3 = display.newRect( 0, 0, 50, 50 )
box_3.x = 180
box_3.y = -50

-- Add physics properties to all objects
-- Note: Physics must be started first! (see line 11)

-- These objects are all static. Static objects don't move.
-- Other objects react to collisions with static objects.
physics.addBody( floor, "static", {friction=0.5, bounce=0.3} )
physics.addBody( left_wall, "static", {friction=0.5, bounce=0.3} )
physics.addBody( right_wall, "static", {friction=0.5, bounce=0.3} )

physics.addBody( platform, "static", {friction=0.5, bounce=0.3} )

-- The box is a dynamic object. Dynamic objects move as if they were physical objects.
physics.addBody( box_1, "dynamic", {density=3.0, friction=0.5, bounce=0.5} )
physics.addBody( box_2, "dynamic", {density=3.0, friction=0.5, bounce=0.5} )
physics.addBody( box_3, "dynamic", {density=3.0, friction=0.5, bounce=0.5} )

physics.setGravity( 0, 10 )

Dragging Physics objects

Physics bodies are constantly under the effect of the physics engine. The physics engine is always setting the position and rotation of all physics bodies.

If you want to set  the position of a physics body you need to turn off the normal effects of gravity for that body, and make the body not react to collisions from other bodies. otherwise the motion as you drag will look strange and may not be what you expect.

One solution is to set the body type to “kinematic”. Kinematic bodies are effected by gravity and other bodies react when kinematic bodies collide with them.

If an object needs to act as a normal dynamic body when not being dragged you can change the body type to kinematic during the drag and back to dynamic when the drag ends.

If a body is in motion when a drag is initiated the object will have a velocity. In other words it will be in motion. You’ll need to set the velocity to 0 to stop it’s current motion. You may also want to set the body’s angularVelocity to 0 to stop it’s rotation.

local physics = require( "physics" )

physics.start() -- Start physics

-- make floor
local floor = display.newRect( 0, display.contentHeight - 15, display.contentWidth, 30 )

-- make left wall
local left_wall = display.newRect( -15, 0, 30, display.contentHeight )
-- make right wall
local right_wall = display.newRect( display.contentWidth - 15, 0, 30, display.contentHeight )

-- Make platform
local platform = display.newRect( display.contentCenterX - 25, display.contentCenterY -15, 50, 30)

-- Make 3 boxes
local box_1 = display.newRect( 0, 0, 50, 50 )
box_1.x = 120
box_1.y = 50

local box_2 = display.newRect( 0, 0, 50, 50 )
box_2.x = 50
box_2.y = 10

local box_3 = display.newRect( 0, 0, 50, 50 )
box_3.x = 180
box_3.y = -50

-- Add physics properties to all objects
-- Note: Physics must be started first! (see line 11)

-- These objects are all static. Static objects don't move.
-- Other objects react to collisions with static objects.
physics.addBody( floor, "static", {friction=0.5, bounce=0.3} )
physics.addBody( left_wall, "static", {friction=0.5, bounce=0.3} )
physics.addBody( right_wall, "static", {friction=0.5, bounce=0.3} )

physics.addBody( platform, "static", {friction=0.5, bounce=0.3} )

-- The box is a dynamic object. Dynamic objects move as if they were physical objects.
physics.addBody( box_1, "dynamic", {density=3.0, friction=0.75, bounce=0.5} )
physics.addBody( box_2, "dynamic", {density=3.0, friction=0.75, bounce=0.5} )
physics.addBody( box_3, "dynamic", {density=3.0, friction=0.75, bounce=0.5} )

physics.setGravity( 0, 5 )

-- This function handles dragging the boxes
local function on_touch( event )
local t = event.target
local phase = event.phase

if "began" == phase then
local parent = t.parent
parent:insert( t )
display.getCurrentStage():setFocus( t, event.id )

t.bodyType = "kinematic"
t:setLinearVelocity( 0, 0 ) -- Stop the body's motion x and y
t.angularVelocity = 0 -- Stop the body's rotation

t.isFocus = true

t.x0 = event.x - t.x
t.y0 = event.y - t.y
elseif t.isFocus then
if "moved" == phase then
t.last_x = t.x -- Keep track of the previous position
t.last_y = t.y -- We'll use this to throw the object

t.x = event.x - t.x0 -- Drag the body
t.y = event.y - t.y0
elseif "ended" == phase or "cancelled" == phase then
display.getCurrentStage():setFocus( t, nil )
t.isFocus = false

t.bodyType = "dynamic"

-- Thesse three lines give the box some momentum in the direction they
-- were moving when the drag ended.
local force_x = ( t.last_x - t.x ) * -100 -- Define a force in the x
local force_y = ( t.last_y - t.y ) * -100 -- define a force in the y

t:applyForce( force_x, force_y, t.x, t.y ) -- Apply the x and y force
end
end
return true
end

box_1:addEventListener( "touch", on_touch )
box_2:addEventListener( "touch", on_touch )
box_3:addEventListener( "touch", on_touch )

A simple game using physics

Here we will make a simple game using physics. The object of the game is to keep objects from touching the bottom edge of the screen by tapping them. A tap will give the objects a push in the up direction. Gravity of course will always be pulling the objects down the screen.

We will use two new features of physics here: applyLinearImpulse() and the collision event.

applyLinearImpulse()

Think of this function as giving an object a push in a direction. It takes four parameters, two pairs of x and y coordinates. You can really think of this as two parameters, each a pair of values. The first, is the vector of the push. The second, is the point where the push is applied.

Call applyLinearImpulse() to any physics body. For example:

box:applyForce( 0, 300, 234, 400 )

The example above would apply force of 0 X and 300 Y. This would be push the object downward with a force of 300. The push or impulse would be applied at 234 X and 400 Y on the screen.

Collision events

Collision events are generated by the physics engine. You can add a listener to the Runtime object to handle all collisions. For example:

Runtime:addEventListener(“collision”, on_collision)

It’s the event object passed to the collision handler (on_collision in the example above) that contains the magic. The collision event provides two properties: object1 and object2. These properties reference each of the two objects that collided. This method is simple and easy to understand.

For example, when an collision event occurs you know that event.object1 has collided with event.object2.

One problem that can arise here is knowing whether the object in question will be object1 or object2. It seems to me that the bodies that were created earlier, in your code, will be object1 and the later object will be object2. This is just a guess based on casual observation.

For best results assign each of your physics bodies a name property and assign a unique value for each. For example, in the example above I created objects box_1, box_2 and box_3. Assign each of these a name with:

box_1.name = “box 1”
box_2.name = “box 2”
box_3.name = “box 3”

You could continue and assign names to the walls and floor also. Access the name property of each object in the collision handler with: event.object1.name and event.object2.name.

local physics = require( "physics" )

physics.start() -- Start physics

-- make floor
local floor = display.newRect( 0, display.contentHeight - 15, display.contentWidth, 30 )

-- Make walls. These walls extend 500px above the top of the screen
local left_wall = display.newRect( -15, -500, 30, display.contentHeight + 500 )
local right_wall = display.newRect( display.contentWidth - 15, -500, 30, display.contentHeight + 500 )

-- Make 3 boxes
local box_1 = display.newRect( 0, 0, 50, 50 )
box_1.x = 120
box_1.y = 50

local box_2 = display.newRect( 0, 0, 50, 50 )
box_2.x = 50
box_2.y = 10

local box_3 = display.newRect( 0, 0, 50, 50 )
box_3.x = 180
box_3.y = -50

-- Add physics properties to all objects
-- Note: Physics must be started first! (see line 11)
-- These objects are all static. Static objects don't move.
-- Other objects react to collisions with static objects.
physics.addBody( floor, "static", {friction=0.5, bounce=0.3} )
physics.addBody( left_wall, "static", {friction=0.5, bounce=0.3} )
physics.addBody( right_wall, "static", {friction=0.5, bounce=0.3} )

-- The box is a dynamic object. Dynamic objects move as if they were physical objects.
physics.addBody( box_1, "dynamic", {density=1.0, friction=0.5, bounce=0.5} )
physics.addBody( box_2, "dynamic", {density=1.0, friction=0.5, bounce=0.5} )
physics.addBody( box_3, "dynamic", {density=1.0, friction=0.5, bounce=0.5} )

physics.setGravity( 0, 1 )

-- This function handles dragging the boxes
local function on_touch( event )
local target = event.target
local phase = event.phase

if phase == "began" then
-- Tapped push the object up
target:applyLinearImpulse( 0, -10, event.x, event.y )
end
end

box_1:addEventListener( "touch", on_touch )
box_2:addEventListener( "touch", on_touch )
box_3:addEventListener( "touch", on_touch )

-- Give each box a name to identify it in collisions
box_1.name = "box 1"
box_2.name = "box 2"
box_3.name = "box 3"
-- Walls need names too
floor.name = "floor"
left_wall.name = "left wall"
right_wall.name = "right wall"

-- Add an event listener for collisions
local function on_collision( event )
if event.phase == "ended" then
if event.object1.name == "floor" then
event.object2:removeSelf()
end
end
end

Runtime:addEventListener( "collision", on_collision )

Using images as physics objects

Corona has made physics very easy to use. Any display object can be turned into a physics object. This means and image can be turned into a physics object with a single call to addBody(). Simple and easy.

One thing to note is that using addBody() in this way creates rectangular bodies. Bodies with other shapes can be created but, require a little more code. I’ll be covering this later.

Starting with the game above let’s add some images. I added a couple images in place of the blocks and added an extra image for a background.

local back = display.newImageRect( "back.png", 320, 480 )
back.x = display.contentCenterX
back.y = display.contentCenterY

local physics = require( "physics" )

physics.start() -- Start physics

-- make floor
local floor = display.newRect( 0, display.contentHeight, display.contentWidth, 30 )

-- Make walls. These walls extend 500px above the top of the screen
local left_wall = display.newRect( -30, -500, 30, display.contentHeight + 500 )
local right_wall = display.newRect( display.contentWidth, -500, 30, display.contentHeight + 500 )

-- Make 3 boxes
local box_1 = display.newImageRect( "Alien_1.png", 62, 64 )
box_1.x = 120
box_1.y = 50

local box_2 = display.newImageRect( "Alien_2.png", 64, 64 )
box_2.x = 50
box_2.y = 10

local box_3 = display.newImageRect( "Alien_3.png", 58, 64 )
box_3.x = 180
box_3.y = -50

-- Add physics properties to all objects
-- Note: Physics must be started first! (see line 11)

-- These objects are all static. Static objects don't move.
-- Other objects react to collisions with static objects.
physics.addBody( floor, "static", {friction=0.5, bounce=0.3} )
physics.addBody( left_wall, "static", {friction=0.5, bounce=0.3} )
physics.addBody( right_wall, "static", {friction=0.5, bounce=0.3} )

-- The box is a dynamic object. Dynamic objects move as if they were physical objects.
physics.addBody( box_1, "dynamic", {density=1.0, friction=0.5, bounce=0.5} )
physics.addBody( box_2, "dynamic", {density=1.0, friction=0.5, bounce=0.5} )
physics.addBody( box_3, "dynamic", {density=1.0, friction=0.5, bounce=0.5} )

physics.setGravity( 0, 1 )

-- This function handles dragging the boxes
local function on_touch( event )
local target = event.target
local phase = event.phase

if phase == "began" then
-- Tapped push the object up
target:applyLinearImpulse( 0, -10, event.x, event.y )
end
end

box_1:addEventListener( "touch", on_touch )
box_2:addEventListener( "touch", on_touch )
box_3:addEventListener( "touch", on_touch )

-- Give each box a name to identify it in collisions
box_1.name = "box 1"
box_2.name = "box 2"
box_3.name = "box 3"
-- Walls need names too
floor.name = "floor"
left_wall.name = "left wall"
right_wall.name = "right wall"

-- Add an event listener for collisions
local function on_collision( event )
if event.phase == "ended" then
if event.object1.name == "floor" then
event.object2:removeSelf()
end
end
end

Runtime:addEventListener( "collision", on_collision )

A few things to note.

I added the background before everything else to make sure it ended up behind the other objects.

I used display.newImageRect() to ensure images scaled for different screen resolutions.

I changed the position of the walls and floor so they sit just outside the visible area of the screen.

Since the objects can be pushed above the top of the screen I extended the walls 500 pixels above the top. If an object was pushed up higher than this it fly over the wall and be lost. Might be a good idea to add a ceiling.

I adjusted the value for gravity to make the objects fall more slowly.

physics.setGravity( 0, 1 )

 

Leave a Reply

Your email address will not be published. Required fields are marked *