This series of posts has been going on for some time. I’m up to 13! I’m not done with the game yet, but it does have some interesting features.
In this post I’d like to add some “explosions”. What I really want is a small animated effect to show that an enemy has been hit by a bullet. For simplicity I’ll stick with the shapes and use a circle for the explosion. This could be replaced in the future.
The strategy here will be the same as was used with everything else, use factory function to create objects, and another function to remove the object when the explosion is finished.
Explosions will be animated with a transition animating the width and height. The onComplete will be used to remove the explosion when the animation finished.
-- Use this function to remove explosions
local function remove_explosion( explosion )
display.remove( explosion )
end
-- Use this function to make explosions
local function make_explosion( x, y )
local explosion = display.newCircle( 0, 0, 1 )
explosion.x = x
explosion.y = y
explosion:setFillColor( 0, 0, 255, 66 )
explosion.strokeWidth = 3
explosion:setStrokeColor( 0, 0, 255, 128 )
alien_group:insert( explosion )
transition.to( explosion, {width=20, height=20, time=500, onComplete=remove_explosion} )
end
Above are the two functions. These are pretty simple. I made the explosions blue to separate them from the enemies (green), and the defenses (red).
The explosion is added to the alien_group. This keeps them inside the game_group, since alien_group is a child of game_group, and should keep new explosions on top of existing enemies.
All that’s need now is to call make_explosion( x, y ) and pass the x and y location where the explosion should occur. Bullets hit enemies in the check_bullets() function.
local function check_bullets()
for b = 1, #bullet_array, 1 do
local bullet = bullet_array[b]
if b > #bullet_array then
return
end
for a = 1, #alien_array, 1 do
local alien = alien_array[a]
if hit_test( bullet.x, bullet.y, alien.contentBounds ) then
if alien.life > 0 then
make_explosion( alien.x, alien.y ) -- Add an explosion on a hit
alien.life = alien.life - bullet.damage
local defense_name = bullet.defense_name
if defense_name == "Stun" then
transition.cancel( alien.transition )
local t = ( alien_target_y - alien.y ) * ALIEN_SPEED
alien.transition = transition.to( alien, { y=alien_target_y,
time=t,
delay=300,
onComplete=remove_alien } )
end
else
remove_alien( alien )
end
remove_bullet( bullet )
break
end
end
end
end
Find the comment in the function above. Here you can see a call to make_explosion( alien.x, alien.y ). This creates a new explosion at the location of the enemy. You might have also placed the explosion at the location of the bullet instead.
Here’s full listing:
-----------------------------------------------------------------------------------------
--
-- main.lua
--
-----------------------------------------------------------------------------------------
-- This example added a small explosion when a bullet hits an enemy
display.setStatusBar( display.HiddenStatusBar )
local TILE_ROWS = 9
local TILE_COLS = 5
local TILE_SIZE = 48
local TILE_MARGIN = 1
local BULLET_SPEED = 1000 / 400
local ALIEN_SPEED = 1000 / 20
local ENERGY_RECHARGE_RATE = 1
local ENERGY_TIMER_TIME = 150
local alien_timer
local energy_timer
local energy = 0
local current_defense_type = 1
local energy_text
local alien_target_y = ( TILE_SIZE + TILE_MARGIN ) * TILE_ROWS
local defense_array = {}
local alien_array = {}
local bullet_array = {}
local defense_type_array = {
{name="Standard", rof=1000, damage=1, cost=50},
{name="Rapid", rof=800, damage=1, cost=75},
{name="Heavy", rof=2000, damage=3, cost=100},
{name="Stun", rof=1200, damage=0.5, cost=80}
}
local alien_type_array = {
{ name="normal", speed=ALIEN_SPEED, life=5, green=200 },
{ name="fast", speed=ALIEN_SPEED / 2, life=2, green=255 },
{ name="slow", speed=ALIEN_SPEED * 2, life=10, green=165 }}
local defense_button_array = {}
local game_group = display.newGroup()
local defense_group = display.newGroup()
local alien_group = display.newGroup()
local tile_group = display.newGroup()
local control_group = display.newGroup()
game_group:insert( tile_group )
game_group:insert( defense_group )
game_group:insert( alien_group )
local function select_defense_button()
for i = 1, #defense_button_array, 1 do
local button = defense_button_array[i]
if button.index == current_defense_type then
button.shape.strokeWidth = 3
else
button.shape.strokeWidth = 0
end
end
end
local function enable_disable_buttons()
for i = 1, #defense_button_array,1 do
local button = defense_button_array[i]
local cost = button.cost
if cost < energy then
button.enabled = true
button.alpha = 1.0
else
button.enabled = false
button.alpha = 0.5
end
end
end
local function touch_defense_button( event )
local button = event.target
current_defense_type = button.index
select_defense_button()
end
local function make_defense_buttons()
for i = 1, #defense_type_array, 1 do
local button_group = display.newGroup()
local button = display.newRoundedRect( 0, 0, 40, 40, 6 )
local r = 255 * ( i / #defense_type_array )
button:setFillColor( r, 0, 0 )
button:setStrokeColor( 255, 255, 255 )
defense_type_array[i].red = r
button_group.cost = defense_type_array[i].cost
button_group.enabled = false
local button_text = display.newText( "0", 0, 0, native.systemFontBold, 14 )
button_text.text = defense_type_array[i].cost
button_text.x = 20
button_text.y = 30
-- Add a name label to each button
local button_name_text = display.newText( "", 0, 0, native.systemFont, 12 )
button_name_text.text = defense_type_array[i].name
button_name_text.x = 20
button_name_text.y = 10
button_group:insert( button )
button_group:insert( button_text )
button_group:insert( button_name_text ) -- insert button into group
button_group.shape = button
button_group.label = button_text
button_group.index = i
button_group.x = display.contentWidth - 45
button_group.y = 40 + ( i * 50 )
button_group:addEventListener( "touch", touch_defense_button )
table.insert(defense_button_array, button_group )
control_group:insert( button_group )
end
end
make_defense_buttons()
select_defense_button()
enable_disable_buttons()
energy_text = display.newText( energy, 0, 0, native.systemFont, 16 )
control_group:insert( energy_text )
energy_text:setTextColor( 0, 255, 0 )
energy_text.x = 300
energy_text.y = 40
local function update_energy()
energy_text.text = energy
end
local function energy_recharge()
energy = energy + ENERGY_RECHARGE_RATE
enable_disable_buttons()
update_energy()
end
energy_timer = timer.performWithDelay( ENERGY_TIMER_TIME, energy_recharge, -1 )
-- Use this function to remove explosions
local function remove_explosion( explosion )
display.remove( explosion )
end
-- Use this function to make explosions
local function make_explosion( x, y )
local explosion = display.newCircle( 0, 0, 1 )
explosion.x = x
explosion.y = y
explosion:setFillColor( 0, 0, 255, 66 )
explosion.strokeWidth = 3
explosion:setStrokeColor( 0, 0, 255, 128 )
alien_group:insert( explosion )
transition.to( explosion, {width=20, height=20, time=500, onComplete=remove_explosion} )
end
local function remove_bullet( bullet )
local index = table.indexOf( bullet_array, bullet )
transition.cancel( bullet.transition )
table.remove( bullet_array, index )
display.remove( bullet )
end
local function make_bullet( x, y )
local bullet = display.newCircle( 0, 0, 5 )
bullet:setFillColor( 0, 0, 0 )
bullet.x = x
bullet.y = y
table.insert( bullet_array, bullet )
local bt = y * BULLET_SPEED
bullet.transition = transition.to( bullet, {y=0, time=bt, onComplete=remove_bullet} )
return bullet
end
local function defense_defend( defense )
for i = 1, #alien_array, 1 do
local alien = alien_array[i]
if alien.col == defense.col then
local bullet = make_bullet( defense.x, defense.y )
bullet.damage = defense.damage
bullet.defense_name = defense.defense_name
break
end
end
end
local function remove_defense( defense )
local index = table.indexOf( defense_array, defense )
timer.cancel( defense.timer )
table.remove( defense_array, index )
display.remove( defense )
end
local function make_defense( x, y )
local defense = display.newRect( 0, 0, 32, 32 )
defense.rof = defense_type_array[current_defense_type].rof
defense.damage = defense_type_array[current_defense_type].damage
defense.defense_name = defense_type_array[current_defense_type].name
defense.red = defense_type_array[current_defense_type].red
defense:setFillColor( defense.red, 0, 0 )
defense_group:insert( defense )
defense.x = x
defense.y = y
table.insert( defense_array, defense )
defense.timer = timer.performWithDelay( defense.rof, function() defense_defend( defense ) end, -1 )
return defense
end
local function touch_tile( event )
local phase = event.phase
if phase == "began" then
local tile = event.target
local tile_x = tile.x
local tile_y = tile.y
local cost = defense_type_array[current_defense_type].cost
if energy >= cost then
energy = energy - cost
local defense = make_defense( tile_x, tile_y )
defense.col = tile.col
end
end
end
local function make_grid()
for row = 1, TILE_ROWS, 1 do
for col = 1, TILE_COLS, 1 do
local tile = display.newRect( 0, 0, TILE_SIZE, TILE_SIZE )
tile.x = ( TILE_SIZE + TILE_MARGIN ) * col
tile.y = ( TILE_SIZE + TILE_MARGIN ) * row
tile.col = col
tile.has_defense = false
tile:addEventListener( "touch", touch_tile )
tile_group:insert( tile )
end
end
end
local function remove_alien( alien )
local index = table.indexOf( alien_array, alien )
transition.cancel( alien.transition )
table.remove( alien_array, index )
display.remove( alien )
end
local function make_alien()
local alien_type = alien_type_array[ math.random( 1, #alien_type_array ) ]
local alien = display.newRect( 0, 0, 32, 32 )
alien.alien_type = alien_type
alien:setFillColor( 0, alien_type.green, 0 )
local col = math.random( 1, TILE_COLS )
alien.col = col
alien.x = col * ( TILE_SIZE + TILE_MARGIN )
alien.y = 0
alien.life = alien_type.life
local t = alien_target_y * alien_type.speed
alien.transition = transition.to( alien, {y=alien_target_y, time=t, onComplete=remove_alien} )
alien_group:insert( alien )
table.insert( alien_array, alien )
end
local function hit_test( x, y, bounds )
return x > bounds.xMin
and x < bounds.xMax
and y > bounds.yMin
and y < bounds.yMax
end
local function hit_test_bounds( bounds1, bounds2 )
return bounds1.xMin < bounds2.xMax
and bounds1.xMax > bounds2.xMin
and bounds1.yMin < bounds2.yMax
and bounds1.yMax > bounds2.yMin
end
local function check_bullets()
for b = 1, #bullet_array, 1 do
local bullet = bullet_array[b]
if b > #bullet_array then
return
end
for a = 1, #alien_array, 1 do
local alien = alien_array[a]
if hit_test( bullet.x, bullet.y, alien.contentBounds ) then
if alien.life > 0 then
make_explosion( alien.x, alien.y ) -- Add an explosion on a hit
alien.life = alien.life - bullet.damage
local defense_name = bullet.defense_name
if defense_name == "Stun" then
transition.cancel( alien.transition )
local t = ( alien_target_y - alien.y ) * ALIEN_SPEED
alien.transition = transition.to( alien, { y=alien_target_y,
time=t,
delay=300,
onComplete=remove_alien } )
end
else
remove_alien( alien )
end
remove_bullet( bullet )
break
end
end
end
end
local function check_enemies()
for a = 1, #alien_array, 1 do
local alien = alien_array[a]
for d = 1, #defense_array, 1 do
local defense = defense_array[d]
if hit_test_bounds( alien.contentBounds, defense.contentBounds ) then
remove_defense( defense )
break
end
end
end
end
local function on_enterframe( event )
check_bullets()
check_enemies()
end
Runtime:addEventListener( "enterFrame", on_enterframe )
make_grid()
alien_timer = timer.performWithDelay( 5300, make_alien, -1 )
-------------------------------------------------------------------------------------
local memory_text = display.newText( "Hello", 5, 5, native.systemFont, 16 )
memory_text:setTextColor( 255, 0, 0 )
memory_text.x = display.contentCenterX
local monitorMem = function()
collectgarbage()
local textMem = math.round( system.getInfo( "textureMemoryUsed" ) / 1000000 )
memory_text.text = "Mem:"..math.round( collectgarbage("count")) .. " tex:".. textMem
end
Runtime:addEventListener( "enterFrame", monitorMem )