In game design, a common element is object spawning, whether it be spawning a variable number of enemies and placing them randomly about the screen, or spawning items repeatedly on a timer increment. In this tutorial, we'll step through a basic spawning module which includes the following features:
Let's begin with a few initial commands...
local spawnTimer local spawnedObjects = {} -- Seed the random number generator math.randomseed( os.time() )
The first variable, spawnTimer
, is simply a Lua forward reference to the timer object which we'll use to control the spawning process. The second variable, spawnedObjects
, is a basic table into which we'll insert items as they are spawned, providing us with a basic method to track them, count them, loop over them, etc.
The third command sets the "seed" for the
Now, let's set up a table of basic spawning parameters which we can use to adjust/control the spawning:
local spawnParams = { xMin = 20, xMax = 300, yMin = 20, yMax = 460, spawnTime = 200, spawnOnTimer = 12, spawnInitial = 4 }
All of these parameters are optional and will default to various values (details later) but they should usually be set to appropriate values for your game. Essentially, the first four parameters define the rectangular region on the screen in which items will spawn, as defined by a minimum and maximum for both x and y. The next parameter (spawnTime
) defines the timed increment, in milliseconds, upon which new items will spawn, assuming an amount of items is defined by the spawnOnTimer
parameter. Lastly, the spawnInitial
parameter can be used to instantly spawn a certain number of items, if the game design calls for it.
Next, we'll need a basic spawning function which creates the objects:
-- Spawn an item local function spawnItem( bounds ) -- Create an item local item = display.newCircle( 0, 0, 20 ) -- Position item randomly within set bounds item.x = math.random( bounds.xMin, bounds.xMax ) item.y = math.random( bounds.yMin, bounds.yMax ) -- Add item to "spawnedObjects" table for tracking purposes spawnedObjects[#spawnedObjects+1] = item end
The first few lines create a sample item — in this case a basic white vector circle. Obviously these items could be images, animated sprites, objects which you then enable for physics usage, etc.
The next few lines position the item randomly within the declared bounds. Recall that we declared these as the first four parameters in the spawnParams
table.
The final line simply adds the new item to the spawnedObjects
table, providing a simple method to track and manage the spawned objects.
The next step is to create a simple but reasonably powerful "controller" function which allows us to start, stop, pause, and resume the spawning process. This function will accept two arguments: an action
argument which represents the action to perform and a params
argument (this represents the spawnParams
table that we declared above).
local function spawnController( action, params ) -- Cancel timer on "start" or "stop", if it exists if ( spawnTimer and ( action == "start" or action == "stop" ) ) then timer.cancel( spawnTimer ) end -- Start spawning if ( action == "start" ) then -- Gather/set spawning bounds local spawnBounds = {} spawnBounds.xMin = params.xMin or 0 spawnBounds.xMax = params.xMax or display.contentWidth spawnBounds.yMin = params.yMin or 0 spawnBounds.yMax = params.yMax or display.contentHeight -- Gather/set other spawning params local spawnTime = params.spawnTime or 1000 local spawnOnTimer = params.spawnOnTimer or 50 local spawnInitial = params.spawnInitial or 0 -- If "spawnInitial" is greater than 0, spawn that many item(s) instantly if ( spawnInitial > 0 ) then for n = 1,spawnInitial do spawnItem( spawnBounds ) end end -- Start repeating timer to spawn items if ( spawnOnTimer > 0 ) then spawnTimer = timer.performWithDelay( spawnTime, function() spawnItem( spawnBounds ); end, spawnOnTimer ) end -- Pause spawning elseif ( action == "pause" ) then timer.pause( spawnTimer ) -- Resume spawning elseif ( action == "resume" ) then timer.resume( spawnTimer ) end end
Let's examine this function
spawnTimer
) at the start, so we can safely use that reference here.-- Cancel timer on "start" or "stop", if it exists if ( spawnTimer and ( action == "start" or action == "stop" ) ) then timer.cancel( spawnTimer ) end
It's fairly obvious why we cancel the timer on the stop action, but why do we also cancel it on the start action? Basically, it's just a
In the initial lines of this conditional block, we gather/set various parameters based on those defined in the spawnParams
table which was declared earlier. As noted, all of those parameters are optional, so if any are undefined, we set some basic defaults in their place.
-- Start spawning if ( action == "start" ) then -- Gather/set spawning bounds local spawnBounds = {} spawnBounds.xMin = params.xMin or 0 spawnBounds.xMax = params.xMax or display.contentWidth spawnBounds.yMin = params.yMin or 0 spawnBounds.yMax = params.yMax or display.contentHeight -- Gather/set other spawning params local spawnTime = params.spawnTime or 1000 local spawnOnTimer = params.spawnOnTimer or 50 local spawnInitial = params.spawnInitial or 0
Next, we check if the spawnInitial
parameter is greater than 0
and, if so, we instantly spawn that number of items. Note that we pass the spawnBounds
table to the spawnItem()
function so it recognizes the limits in which to spawn the items.
-- Start spawning if ( action == "start" ) then -- Gather/set spawning bounds local spawnBounds = {} spawnBounds.xMin = params.xMin or 0 spawnBounds.xMax = params.xMax or display.contentWidth spawnBounds.yMin = params.yMin or 0 spawnBounds.yMax = params.yMax or display.contentHeight -- Gather/set other spawning params local spawnTime = params.spawnTime or 1000 local spawnOnTimer = params.spawnOnTimer or 50 local spawnInitial = params.spawnInitial or 0 -- If "spawnInitial" is greater than 0, spawn that many item(s) instantly if ( spawnInitial > 0 ) then for n = 1,spawnInitial do spawnItem( spawnBounds ) end end
Following this, we check if the spawnOnTimer
parameter is greater than 0
and, if so, we start a timer which spawns that amount of items on the set spawnTime
increment. Note that we set this timer object to the spawnTimer
reference so we can pause, resume, or stop it later if needed. As above, we pass the spawnBounds
table to the anonymous function so that all items are spawned within the desired bounds.
-- Start spawning if ( action == "start" ) then -- Gather/set spawning bounds local spawnBounds = {} spawnBounds.xMin = params.xMin or 0 spawnBounds.xMax = params.xMax or display.contentWidth spawnBounds.yMin = params.yMin or 0 spawnBounds.yMax = params.yMax or display.contentHeight -- Gather/set other spawning params local spawnTime = params.spawnTime or 1000 local spawnOnTimer = params.spawnOnTimer or 50 local spawnInitial = params.spawnInitial or 0 -- If "spawnInitial" is greater than 0, spawn that many item(s) instantly if ( spawnInitial > 0 ) then for n = 1,spawnInitial do spawnItem( spawnBounds ) end end -- Start repeating timer to spawn items if ( spawnOnTimer > 0 ) then spawnTimer = timer.performWithDelay( spawnTime, function() spawnItem( spawnBounds ); end, spawnOnTimer ) end
-- Pause spawning elseif ( action == "pause" ) then timer.pause( spawnTimer ) -- Resume spawning elseif ( action == "resume" ) then timer.resume( spawnTimer ) end end
From this point, the spawn controller can easily be called with four possible actions. Note that calling the spawnController()
function with "start"
as the action
parameter is the sole use case which requires the spawnParams
table — the other actions assume that the spawning process is already underway.
spawnController( "start", spawnParams )
spawnController( "pause" )
spawnController( "resume" )
spawnController( "stop" )
Hopefully this tutorial gives you a simple base module atop which to build a more comprehensive spawning methodology involving animated sprites, physics objects, variable time increments, and much more.