This post was originally published at Tutorial: Physics-based animation
In previous tutorials on programmatic animations, we covered animation via transition.to() and “enterFrame” listeners. In this tutorial, we will discuss physics as yet another method of “animating” objects.
If you’re already using physics in your app, moving objects with “enterFrame” listeners or transitions may cause conflict with the physics engine. Why? Because the physics engine naturally wants to move things by physical means, for example simulated forces/gravity, application of velocity, etc. Thus, if you need to move objects which are physical bodies, you should usually do so with APIs and properties in the physics library (of course there are some exceptions, but typically you should use physical methods to control physical objects).
In this tutorial, we’re going to create a basic platform which moves back and forth along a horizontal axis, similar to platforms commonly found in many 2D platformer games.
The basic idea is simple: set up the physics engine, create a platform object, and make it a physical body. Next, create two invisible rectangles which are set as “sensors” (sensors detect collision but do not register any other physical reaction). Finally, apply steady velocity to the platform and, when it collides with a sensor rectangle, reverse the velocity so that the platform moves in the other direction.
-- 1) Set up the physics engine
local physics = require("physics")
physics.setDrawMode( "hybrid" ) -- "hybrid" for testing; "default" for gameplay
-- 2) Create the platform
local platform = display.newRect( display.contentCenterX, 250, 100, 25 )
platform:setFillColor( 1,0,0 )
physics.addBody( platform, "kinematic" )
platform.travelDistance = 200 -- Set the total travel distance
platform.speed = 40 -- Set the speed for the platform
platform.id = 1 -- Set platform ID for collision detection with sensors (see below)
-- 3) Create the sensor objects
local leftSensor = display.newRect( 0, platform.y, 10, platform.height )
leftSensor.isVisible = false
physics.addBody( leftSensor, "dynamic", isSensor=true )
leftSensor.gravityScale = 0 -- Make the sensor float (no effect from gravity)
leftSensor.id = 1 -- Set sensor ID for collision detection with respective platform
leftSensor.x = platform.x - ( platform.travelDistance * 0.5 )
local rightSensor = display.newRect( 0, platform.y, 10, platform.height )
rightSensor.isVisible = false
physics.addBody( rightSensor, "dynamic", isSensor=true )
rightSensor.gravityScale = 0 -- Make the sensor float (no effect from gravity)
rightSensor.id = 1 -- Set sensor ID for collision detection with respective platform
rightSensor.x = platform.x + ( platform.travelDistance * 0.5 )
-- 4) Set up the collision handler function/listener
local function onCollision( self, event )
if ( "began" == event.phase and self.id == event.other.id ) then
local vx,vy = self:getLinearVelocity()
self:setLinearVelocity( -vx, -vy )
platform.collision = onCollision
platform:addEventListener( "collision", platform )
-- 5) Set the platform in motion
platform:setLinearVelocity( platform.speed, 0 )
1) Set up the physics engine
Since we are using physics, we must enable the engine. In this example, we simply start the engine and set the display to “hybrid” mode so that we can see the physics engine trace/outline of the various objects (in particular the invisible sensors).
2) Create the platform
In this sample, we create a simple vector rectangle that is 100×25 pixels in size, centered horizontally with a y position of 250, and then we color (fill) it red. Next, we use physics.addBody() to make it a physical object. Note that in the same line, we set it to a kinematic body type — this is because, in most platform games, the player and other objects are affected by gravity, but moving platforms “float” in air unaffected. While we could make the platform individually immune to gravity (object.gravityScale), it would still be prone to other forces like the player jumping onto it, and that would cause the platform to move off its intended axis. Thus, a better solution is to make it a kinematic object, meaning that it will be immune to all external forces, including gravity, other objects bumping into it, etc.
In addition, we set a few other properties for the platform, including
travelDistance which will be used to calculate where the sensors are placed. We also store the
speed for the platform and give it an
id value which will be used to ensure that it only responds/reacts when it collides with its associated sensors, not with the sensors of other platforms (a likely scenario if we created multiple platforms moving around in a more complex setup).
3) Create the sensor objects
As demonstrated in the tutorial on “enterFrame” listeners, we could monitor the position of the platform on every runtime frame and then, when it surpasses a certain point along its path, change its direction. The problem with this approach is that, if we decided to add multiple platforms to a more complex setup, we would need a separate listener for every platform, constantly monitoring its platform’s position.
Fortunately, because we’re using physics, we can solve the directional change event in a more elegant physics-based manner. We do this by creating two sensor objects on opposite sides of the platform’s movement axis — essentially invisible “walls” that the platform will collide with. Once again, we use simple vector rectangles set to the same height as platform. Note that the starting x postion of
0 is not the final location (we will calculate that shortly).
On the next two lines, we make the object invisible, then we make it into a physical body. One important distinction is that we set the body type to dynamic (the default body type). While this may seem counter-intuitive because the sensor will not move or be affected by force/gravity, collisions will only occur/register if at least one of the bodies is a “dynamic” type. Remember that we set the platform itself as “kinematic,” so in this case, the sensor must be “dynamic” to register a collision response. In the same line, note that the table of “options” contains
isSensor=true which sets the body to the sensor type. Finally, on the next line, we set the gravityScale to
0 so that the sensor does not fall/respond under the force of gravity.
On the next line, we add a property for the
id of the sensor, set to the same
id as the platform. This value will be used in the collision handler to conditionally check that the platform collided with one of its associated sensors. Expanding on this concept, if we decided to create multiple platforms in a more complex setup, each platform would have a unique
id value, and each of its associated sensors would share the same
As a final step in creating the sensor, we set its x position based on half of the travel distance to the left of the platform’s starting x location.
This entire process is repeated for the second sensor (
rightSensor), and its x position, naturally, is set to half of the travel distance to the right of the platform’s starting x location.
If we run our code at this point, the objects in the simulation will look like this:
4) Set up the collision handler function/listener
We need a function to handle when collisions happen. In this case, we are using the table event format where we provide the object’s
self reference as well as the actual event table.
In this sample, we are only concerned about the start of the collision (
"began" phase) and we only care about the collision if the platform (
self) hits one of the sensors with the same
id value. If both of these conditions are met, we effectively reverse the platform’s direction by getting the current linear velocity and then we re-apply it using negative values.
5) Set the platform in motion
To set the platform in motion, we take the
speed value set for the platform and apply it to the x argument of object:setLinearVelocity() (the y argument can remain at
nil in this example, since the platform is only moving horizontally). In this manner, the platform will continuously “bounce” back and forth between the two sensors.
As you can see, physics is a simple and viable way to “animate” moving objects in a game. Combined with collision detection and other methods, moving platforms and a myriad of other scenarios are easily achievable using Corona’s built-in physics engine.
This post was originally published at Tutorial: Physics-based animation