Predicting trajectory has traditionally been a confusing endeavor, and some developers simply "guess" and hope for the best. In this tutorial, we'll discuss the real solution!
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 TestLaunch
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, and insert these lines of code:
local physics = require( "physics" ) physics.start() -- Start the physics engine physics.setGravity( 0, 9.8 ) -- Set typical Earth gravity -- Create display group for predicted trajectory local predictedPath = display.newGroup() -- Create function forward references local getTrajectoryPoint local launchProjectile
The core task of predictive trajectory is gathering some basic data like the starting position and starting velocity of the projectile you want to launch. This can be accomplished with a "touch"
event listener, so add the following code to your test project after the previous commands:
local function screenTouch( event ) if ( event.phase == "moved" ) then -- Remove trajectory path group (to clear previous path), then re-create it display.remove( predictedPath ) predictedPath = display.newGroup() local startingVelocity = { x=event.x-event.xStart, y=event.y-event.yStart } for i = 1,240,2 do local s = { x=event.xStart, y=event.yStart } local trajectoryPosition = getTrajectoryPoint( s, startingVelocity, i ) local dot = display.newCircle( predictedPath, trajectoryPosition.x, trajectoryPosition.y, 4 ) end elseif ( event.phase == "ended" ) then launchProjectile( event ) end return true end Runtime:addEventListener( "touch", screenTouch )
Let's inspect this code in more detail:
The core of this function works on the "moved"
touch phase. This allows us to start touching at a certain point on the screen (this will be the origin of the projectile) and then drag around to render a dotted path that represents the predicted trajectory.
Inside the conditional "moved"
phase block, we first remove the predictedPath
display group created earlier, then we
Following that, we calculate the starting x and y "velocity" values by subtracting the starting touch location (event.xStart
/event.yStart
) from the current touch location (event.x
/event.y
).
Finally, with this basic information, we run a for
loop which draws the predicted trajectory path in 240 steps — 4 seconds of launch time at 2
in the loop since, for test purposes, 120 path points (dots) is enough to clearly represent the predicted trajectory.
Within the for
loop in the above function, the core calculation is done via the following function which uses the
getTrajectoryPoint = function( startingPosition, startingVelocity, n ) -- Velocity and gravity are given per second but we want time step values local t = 1/display.fps -- Seconds per time step at 60 frames-per-second (default) local stepVelocity = { x=t*startingVelocity.x, y=t*startingVelocity.y } local gx, gy = physics.getGravity() local stepGravity = { x=t*gx, y=t*gy } return { x = startingPosition.x + n * stepVelocity.x + 0.25 * (n*n+n) * stepGravity.x, y = startingPosition.y + n * stepVelocity.y + 0.25 * (n*n+n) * stepGravity.y } end
This trajectory calculation is based on an app running at 60 fps
value within config.lua
)0.25
in the returned x
and y
values to 0.5
and everything should mesh up perfectly.
On touch release, firing the projectile is simple. We simply generate the projectile, add a physical body, and assign the velocity values that we gathered to send it flying across the trajectory path in a perfect match. To test, add this code block to your project following the previous lines:
launchProjectile = function( event ) -- Create projectile object local proj = display.newCircle( event.xStart, event.yStart, 24 ) proj:setFillColor( 1,0.2,0.2 ) -- Add physical body to object physics.addBody( proj, { bounce=0.2, density=1.0, radius=24 } ) -- Apply velocity values to object local vx, vy = event.x-event.xStart, event.y-event.yStart proj:setLinearVelocity( vx, vy ) end
Now save the project, refresh/reload it, and touch/drag around the screen to draw a predicted trajectory path. Upon release, a projectile (red circle) should generate at the starting point and it should perfectly follow the predicted path!
Hopefully this tutorial illustrates that predictive trajectory is not an insurmountable task, nor one that requires a "best guess" sort of operation. With these calculations, accurate trajectory paths are easily accomplished in Corona!