Corona – Tower Defense Game Experiments 7

Time for the enemies to strike back. So far the defenses have it easy, there is no way to remove a defense. There are a lot of things we can do here. In this example I’ll just take a simple step and remove a defense if an enemy intersects it. This will let the game play and tutorial advance slowly. I find I get better results if I build things from smaller steps.

The first step here will be to check if an enemy is intersecting a defense. The earlier hit_test(x,y, bounds) function works well with bullets which are small size. With larger elements the center point of an object may be outside of another object, but the two objects might be overlapping still. To solve this we can use a different type of hit test comparing the bounds of both objects.

Here’s a simple function that tests for overlapping boundaries. This was posted here by jhocking: http://developer.anscamobile.com/code/flashs-hittestobject-emulated-using-contentbounds. I modified the function slightly but it is essentially the same.

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 

This uses the boundaries of an object. imagine the boundary as a rectangle that just fits the object.

Now we need check for overlap between aliens and defense elements. For this we’ll need to loop through the alien_array and use the function above to check for an overlap with each of the defenses in defense_array. On a true from hit_test_bounds() remove that defense and break the loop.

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]
			-- Here we're use the new hit_test_bounds() function to check if the defense and alien overlap
			if hit_test_bounds( alien.contentBounds, defense.contentBounds ) then -- Check for collision
				remove_defense( defense )	-- Remove this defense on a collision
				break
			end 
		end 
	end 
end

Add a call to this function in the enterFrame. Have to check this each frame.

local function on_enterframe( event ) 
	check_bullets()
	check_enemies()
end

Remember, when creating a defense a timer is added which fires a bullet from the position of the defense. If the defense is removed it’s timer should also be removed. Update remove_defense():

local function remove_defense( defense )
	local index = table.indexOf( defense_array, defense ) 
	timer.cancel( defense.timer )	-- Must remove the timer! 
	table.remove( defense_array, index )				  
	display.remove( defense )
end 

-----------------------------------------------------------------------------------------
--
-- main.lua
--
-----------------------------------------------------------------------------------------

-- This example will try ad show enemies that can damage defenses. 
-- Here a defense that is intersected by an enemy will be removed. 

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 defense_array = {} 	
local alien_array = {}		
local bullet_array = {}		

local game_group = display.newGroup()
local defense_group = display.newGroup() 	
local alien_group = display.newGroup()	
local tile_group = display.newGroup()

game_group:insert( tile_group )		
game_group:insert( defense_group )	
game_group:insert( alien_group )

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} )
end

local function defense_defend( defense )
	for i = 1, #alien_array, 1 do 		
		local alien = alien_array[i]	
		if alien.col == defense.col then 
			make_bullet( defense.x, defense.y )
			break
		end 
	end 
end

local function remove_defense( defense )
	local index = table.indexOf( defense_array, defense ) 
	timer.cancel( defense.timer )	-- Must remove the 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:setFillColor( 200, 0, 0 )
	
	defense_group:insert( defense ) 
	
	defense.x = x 
	defense.y = y
	
	table.insert( defense_array, defense ) 
	
	defense.timer = timer.performWithDelay( 1000, 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 defense = make_defense( tile_x, tile_y ) 
		defense.col = tile.col 						  
	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 = display.newRect( 0, 0, 32, 32 )
	alien:setFillColor( 0, 200, 0 )
	 
	local col = math.random( 1, TILE_COLS )
	alien.col = col
	alien.x = col * ( TILE_SIZE  + TILE_MARGIN ) 
	alien.y = 0
	alien.life = 5 
		
	local target_y = ( TILE_SIZE + TILE_MARGIN ) * TILE_ROWS
	local t = ( TILE_ROWS + 1 ) * 2000
	alien.transition = transition.to( alien, {y=target_y, time=t, onComplete=remove_alien} )
	alien_group:insert( alien ) 
	table.insert( alien_array, alien )
end 

local function hit_test( x, y, bounds )
	if x > bounds.xMin and x < bounds.xMax and y > bounds.yMin and y < bounds.yMax then
		return true
	else 
		return false
	end
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 
					alien.life = alien.life - 1 
				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]
			-- Here we're use the new hit_test_bounds() function to check if the defense and alien overlap
			if hit_test_bounds( alien.contentBounds, defense.contentBounds ) then -- Check for collision
				remove_defense( defense )	-- Remove this defense on a collision
				break
			end 
		end 
	end 
end


local function on_enterframe( event ) 
	check_bullets()
	check_enemies()
end

Runtime:addEventListener( "enterFrame", on_enterframe )

make_grid()

local 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 = system.getInfo( "textureMemoryUsed" ) / 1000000
	memory_text.text = "Mem:"..collectgarbage("count") .. " tex:".. textMem
end

Runtime:addEventListener( "enterFrame", monitorMem )

Leave a Reply

Your email address will not be published. Required fields are marked *