Although Corona's Box2D-powered physics engine can easily simulate x and y directional gravity, radial gravity isn't
For those who don't know what radial gravity is, it's essentially like the simulated gravitational pull of a planet, or a magnet pulling outlying metal objects toward it. This tutorial will outline how to build a basic radial gravity simulation with properties that can be easily altered, including the size and strength of the gravitational field.
For purposes of this tutorial, let's quickly create a test project:
Open the Corona Simulator.
Click New Project from the welcome window or select
For the project/application name, type RadialGravity
and ensure that the Blank template option is selected. Leave the screen size settings at default but set Default Orientation to Sideways, then click OK (Windows) or Next (Mac). This will create the basic files for the test project in the location (folder) that you specified.
Locate the project folder, open the main.lua
file in your chosen text editor, then insert these lines of code:
local physics = require( "physics" ) physics.start() -- Start the physics engine physics.setGravity( 0, 0 ) -- Set "space" gravity math.randomseed( os.time() ) -- Seed the pseudo-random number generator -- Set radial gravity simulation values local fieldRadius = 120 local fieldPower = 0.4
The first step is to create the gravitational field that will exert a pull force on objects that enter it. In Corona, we can create this field as a simple radial body set as a sensor. In your project, add the following lines:
local field = display.newCircle( display.contentCenterX, display.contentCenterY, fieldRadius ) field.alpha = 0.2 -- Add physical body (sensor) to field physics.addBody( field, "static", { isSensor=true, radius=fieldRadius } )
The common method behind other objects being affected by radial gravity is their collision with this field. Essentially, when an object enters the field — collision phase of "began"
— we'll create a physics touch joint between the object and the center of the field. Similarly, if an object exits the field "ended"
)
First, let's add the following code to the project to handle objects which enter the gravitational field:
local function collideWithField( self, event ) local objectToPull = event.other if ( event.phase == "began" and objectToPull.touchJoint == nil ) then -- Create touch joint after short delay (10 milliseconds) timer.performWithDelay( 10, function() -- Create touch joint objectToPull.touchJoint = physics.newJoint( "touch", objectToPull, objectToPull.x, objectToPull.y ) -- Set physical properties of touch joint objectToPull.touchJoint.frequency = fieldPower objectToPull.touchJoint.dampingRatio = 0.0 -- Set touch joint "target" to center of field objectToPull.touchJoint:setTarget( self.x, self.y ) end ) end end field.collision = collideWithField field:addEventListener( "collision" )
Inspecting this in more detail, we do the following:
First, with local objectToPull = event.other
With the first conditional check, we confirm that the "began"
phase has occurred and that the object doesn't already have a touch joint applied (this second condition is simply an
After a short timer delay (see notes below), we add a touch joint to the object, set the basic joint properties, and then set its target to the center of the gravitational field self.x
/self.y
)
Because certain physical actions like creating joints can't be done in the exact same time step as a collision, we must do so following a short timer timer.performWithDelay()
call to create the touch joint.
In the case of a touch joint, you don't actually join the object to another object as you would with most physical joints. For this joint type, the "target" is simply a point in world coordinates.
So that the gravitational field doesn't maintain permanent control over objects which exit its radius, we need to add some code to break (remove) the object's touch joint.
In your project, add the following code inside the existing collideWithField()
function:
local function collideWithField( self, event ) local objectToPull = event.other if ( event.phase == "began" and objectToPull.touchJoint == nil ) then -- Create touch joint after short delay (10 milliseconds) timer.performWithDelay( 10, function() -- Create touch joint objectToPull.touchJoint = physics.newJoint( "touch", objectToPull, objectToPull.x, objectToPull.y ) -- Set physical properties of touch joint objectToPull.touchJoint.frequency = fieldPower objectToPull.touchJoint.dampingRatio = 0.0 -- Set touch joint "target" to center of field objectToPull.touchJoint:setTarget( self.x, self.y ) end ) elseif ( event.phase == "ended" and objectToPull.touchJoint ~= nil ) then objectToPull.touchJoint:removeSelf() objectToPull.touchJoint = nil end end
This addition is simple: in the "ended"
collision phase, we remove the touch joint, effectively allowing the object to continue on with its normal physical behavior.
Let's complete our project by generating 5 objects which approach the gravitational field at random speeds. Add the following code to your project following the previous lines:
for i = 1,5 do local object = display.newCircle( 0, (i*50)+10, 10 ) physics.addBody( object, { radius=10 } ) local velocity = math.random( 80, 120 ) object:setLinearVelocity( velocity, 0 ) end
Now save the project, refresh/reload it, and you'll see 5 white circles move toward the field. Upon contacting it, each circle will be drawn/attracted to the center of the field. Those moving fast enough may break free of the field's radius and fly off on their own natural path, while those that remain inside the field will be continually pulled closer toward the center.
To test behavior variations, change the fieldPower
variable value near the beginning of the project to something higher or lower. A lower value like 0.2
will make the field's pull more gentle, while a higher value like 0.8
will pull objects very strongly toward the center.
As you can see, simulating radial gravity is easily accomplished with some basic physics setup and one collision detection function. This concept can be used for planets with gravitational pull, magnets, or literally any other type of game object where you want to pull one or more objects toward another. Enjoy experimenting with the possibilities!