Corona – Tower Defense Game Experiments 9

With one type of defense the only choice a player has is where to place a defense. Providing a choice of defense types gives players a chance to try different strategies. Plants vs Zombies provides a wide range of defense types each with a range of effects and cost.

As a first step to getting different types of defenses to work I need to have an interface to choose the type of defense to place. I don’t want to get into a long discussion what the best type of interface might be. The goal for this post will be to make a simple set of buttons that will act like a radio button group. Tapping one button will select it and deselect the currently selected button. Only one button can be selected at a time.

This idea could be applied to many other projects!

The buttons

The buttons will be rounded rectangles filled with a color. I’ll use a white stroke to show which is selected.

List types of defenses

The types of defense will be stored in an array. Knowing how many different types of defenses are available will tell us how many buttons to make.

For now the types of defenses can be anything. Later I will look at the features of each defense type and we can store that information. For now this array can store anything as long as there one element for each type. in the example I used the letters: “A”, “B”, “C” etc.

Keeping track of the current defense type

I added a new variable: current_defense_type. This variable holds the index of the current defense type in the defense_type_array. For example if the current defense type is “B”, then current_defense_type would be 2.

local current_defense_type = 1 -- marks the current defense type, matches the index of the defense type in the defense_type_array

List of buttons

The buttons will also be stored in an array. This is essential for keeping track of the buttons. Having an array of the buttons will make it easy to select and deselect any of the buttons in the array.

local defense_type_array = {"A","B","C","D"} -- Make an array to keep track of defense buttons 
local defense_button_array = {} -- use an array to keep track of buttons

The functions

I used three functions make_defense_buttons(), touch_defense_button(), and select_defense_button().

make_defense_buttons()

This function makes the defense buttons. It makes one button for each item in the defense_type_array. Each button is given a different shade of red, and a white stroke. The buttons are also assigned the property index. This property is the index of the defense type in the defense_type_array that the button corresponds to. I also added a touch event to each button that is handled by touch_defense_button().

touch_defense_button()

This function gets the index from the button and sets current_defense_type to this value. Then it calls select_defense_button().

select_defense_button()

This function highlights the current button by setting the stroke width to 3 for that button and the stroke width of the other buttons to 0.

-- Select the current button
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.strokeWidth = 3
		else
			button.strokeWidth = 0
		end 
	end
end

-- Handle button events 
local function touch_defense_button( event )
	local button = event.target
	current_defense_type = button.index
	select_defense_button()
end

-- Add a function to create defense buttons
local function make_defense_buttons()
	for i = 1, #defense_type_array, 1 do 
		local button = display.newRoundedRect( 0, 0, 40, 40, 6 )
		local r = 255 * ( i / #defense_type_array )
		button.index = i
		button:setFillColor( r, 0, 0 )
		button:setStrokeColor( 255, 255, 255 )
		button.x = display.contentWidth - 26
		button.y = 40 + ( i * 50 )
		button:addEventListener( "touch", touch_defense_button )
		table.insert(defense_button_array, button )
		control_group:insert( button )
	end 
end

-- Call the function to make the buttons
make_defense_buttons()
-- Select the current defense type
select_defense_button()

Here’s a full listing of the code so far:

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

-- This example will an interface for creating different types of defenses

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 ENERGY_RECHARGE_RATE = 1 
local DEFENSE_ENERGY_COST = 50 
local ENERGY_TIMER_TIME = 150

local alien_timer
local energy_timer 
local energy = 0 
local current_defense_type = 1 -- marks the current defense type, matches the index of the defense type in the defense_type_array
local energy_text 

local defense_array = {} 	
local alien_array = {}		
local bullet_array = {}	
local defense_type_array = {"A","B","C","D"} -- Make an array to keep track of defense buttons 
local defense_button_array = {} -- use an array to keep track of buttons

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 )



-- Select the current button
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.strokeWidth = 3
		else
			button.strokeWidth = 0
		end 
	end
end

-- Handle button events 
local function touch_defense_button( event )
	local button = event.target
	current_defense_type = button.index
	select_defense_button()
end

-- Add a function to create defense buttons
local function make_defense_buttons()
	for i = 1, #defense_type_array, 1 do 
		local button = display.newRoundedRect( 0, 0, 40, 40, 6 )
		local r = 255 * ( i / #defense_type_array )
		button.index = i
		button:setFillColor( r, 0, 0 )
		button:setStrokeColor( 255, 255, 255 )
		button.x = display.contentWidth - 26
		button.y = 40 + ( i * 50 )
		button:addEventListener( "touch", touch_defense_button )
		table.insert(defense_button_array, button )
		control_group:insert( button )
	end 
end

-- Call the function to make the buttons
make_defense_buttons()
-- Select the current defense type
select_defense_button()


 
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
	update_energy()
end

energy_timer = timer.performWithDelay( ENERGY_TIMER_TIME, energy_recharge, -1 ) 


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 )	
	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
		if energy >= DEFENSE_ENERGY_COST then 
			energy = energy - DEFENSE_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 = 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 )
	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 
					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]
			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 = 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 *