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 )