Corona – Tower Defense Game Experiments 14

Here’s a few thoughts on setting up waves of enemies. Plants vs Zombies makes waves of zombies and puts the waves to good use. Game play gets much more exciting after the message “A huge wave of Zombies is approaching.”

The game requires that you either a starting reserve of energy or you have to start with a fewer enemies. After building your defense for a while it becomes very easy to take on the enemies. This means that a larger wave of enemies is required to make the game more interesting at this point.

There are a lot approaches you could take to creating wave of enemies. I’m going to define a wave of enemies by describing the wave in terms of time between enemies, number of enemies in wave, and pool of enemies.

Time between enemies will be the amount of time between the creation of each new enemy.

Number of enemies in wave, will be the count of total enemies in this wave.

Pool of enemies will be a listing of the types of enemies in the wave. This is the most abstract idea here. I’m going to use the pool as a list of possible enemy types that might appear in the wave. During the wave each new enemy created will be a random type chosen from the pool. The pool will be an array. By adding the same type more than once the pool can weighted.

Each wave will be initiated by a timer.

Use three variables to keep track of the wave. One and array the other an integer setting the index of the current wave in the array. The last variable will keep track of a timer that tracks the wave.

local wave_timer -- add a timer to hold the wave
local alien_wave_index = 1 -- Use this to keep track of the current wave
-- Add an array to hold a wave of enemies	 
local alien_wave_array = { 
							{ time=2300, 		-- Time between enemies
							count=10, 			-- Number of enemies in wave
							pool={1,1,1,2,3}, 	-- Number and type of enemies in wave
							delay=3000
							} ,
							
							{ time=1500, 		-- Time between enemies
							count=20, 			-- Number of enemies in wave
							pool={1,2,3}, 		-- Number and type of enemies in wave
							delay=2000
							} 
						}

The array above holds a list of tables. Each of these tables describes a wave of enemies. Notice each table has properties of time, count, pool, and delay.

  • time – sets the time in ms between enemies during wave
  • count – total number of enemies in wave
  • pool – an array of alien types represented in wave
  • delay – time to wait after wave before starting the net wave

I added the extra delay property here as thought it might be useful to control the flow of things.

Next I needed two functions to control the waves of enemies. The make_wave() function sets up timers to run the wave. Here I create two timers. One called make_alien(). This timer takes the time and count from the current wave table. The second timer is set to total length of the current wave and is used to start the next wave.

I need to include a forward declaration here for next_wav, since this function calls make_wave and make wave calls it.

-- Need a forward declaration
local next_wave

-- Add this function to mange creating enemies that attack in waves. 
local function make_wave()
	local wave_table = alien_wave_array[ alien_wave_index ]
	local wave_time = wave_table.time
	local wave_count = wave_table.count 
	local delay_after_wave = wave_table.delay
	
	alien_timer = timer.performWithDelay( wave_time, make_alien, wave_count )
	local time_til_next_wave = ( wave_time * wave_count ) + delay_after_wave
	wave_timer = timer.performWithDelay( time_til_next_wave, next_wave, 1 )
end 

-- Called at the end of each wave
function next_wave()
	alien_wave_index = alien_wave_index + 1
	if alien_wave_index > #alien_wave_array then 
		print( "ALL WAVES COMPLETE" )
	else 
		print( "next wave" ) 
		make_wave()
	end 
end

make_wave()

Here’s a full listing of the code in this example:
Continue reading Corona – Tower Defense Game Experiments 14

Corona – Tower Defense Game Experiments 13

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:
Continue reading Corona – Tower Defense Game Experiments 13

Corona – Tower Defense Game Experiments 12

One aspect that will make the game more interesting is the addition of different types of enemies.

Let’s refer to Plants vs Zombies as our model. Here all of the enemies are zombies but there are different types with different capabilities. Besides the standard there are zombies that move faster, zombies that withstand more damage, zombies that jump over a defense, and more.

The variety of enemies adds a lot to strategies that players can employ, and creativity that developers can employ when creating levels.

Enemy features

Enemies can have as may features as you can imagine. Here’s a few ideas that fit into the game as I have been envisioning it so far.

  • speed – Some enemies move faster than others
  • Toughness – some enemies take more damage to destroy than others
  • Special – some enemies may have special features or attacks

To keep track of the enemy types I created an array similar to the array used for organizing defense types. Each enemy type will be described in a table with properties:

  • name – name of feature
  • speed – sets speed in px per second
  • life – sets the starting life value for this enemy type
  • green – color of enemy type

The last property, green, is just used for this example. Later we will have graphics to replace the shapes.

All of the enemy type tables will be held in an array for easy access and organization.

-- Add an array to manage enemy types 	
local alien_type_array = {
			{ name="normal", speed=ALIEN_SPEED, 	life=5, green=200 }, 	-- Normal enemy
			{ name="fast", 	 speed=ALIEN_SPEED / 2, life=2, green=255 },	-- Fast enemy weaker
			{ name="slow",   speed=ALIEN_SPEED * 2, life=10, green=165 }}	-- slow enemy tougher

make_alien()

You might make different enemy types based on different criteria or different situations. In this example to keep things simple I will make the choice of enemy type random. Each time an enemy is created I will choose a random type and configure the new enemy based on the information in the tables created above.

-- Modify this function to select different types of aliens
local function make_alien()
	local alien_type = alien_type_array[ math.random( 1, #alien_type_array ) ] -- Choose a random alien type
	
	local alien = display.newRect( 0, 0, 32, 32 )
	alien.alien_type = alien_type					-- Save the type
	alien:setFillColor( 0, alien_type.green, 0 )	-- get the green value
	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 -- Get the life foe this type
	alien.speed = alien_type.speed -- Save the speed 
	
	local t = alien_target_y * alien_type.speed -- Get the speed for this type
	alien.transition = transition.to( alien, {y=alien_target_y, time=t, onComplete=remove_alien} )
	alien_group:insert( alien ) 
	table.insert( alien_array, alien )
end 

Here we’re creating the enemies as before. The difference this time is that property values are set to starting values based on values found in the tables in the alien_type_array.

Last, since the speed can be set after an alien gets hit with a stun weapon, we should make sure the alien moves at it’s types speed after it is hit with a stun weapon. Look at 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 
					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 -- Use this aliens speed here 
						
						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

Here’s the full listing

Continue reading Corona – Tower Defense Game Experiments 12

Corona – Tower Defense Game Experiments 11

So far we have the makings of a game but, there are more than a few that need to be taken care of. For example, the buttons do not show the energy cost. This makes it hard to play and test, since it’s hard to tell what the cost is for defenses.

Groups as layers again

The buttons that I had created earlier were simple shapes. To show both text and a shape we need to create a group and add both elements to the group. Groups in Corona have all of the common display properties available to other display objects.

To make this work out I created a group for each button and used this to replace the original shape I had used for the button. Then I added the original rounded rectangle to the group. Then created a new Text shape and added that to the new group also.

I need to keep a reference to the rounded rectangle shape and text field. So I added properties to the group to hold these.

From here I retooled the rest of the script so all of the properties that had been attached to the button shape were now attached to the button_group. I also inserted the button_group into the defense_button_array in place of the original button shape. I also added the button_group into the control_group.

To get the costs to chose up in the text field I needed to get the cost from the defense_type_array for the current defense type and set it as the text of button_text.

Last I needed to adjust the positions of all three objects: button_group, button, and button_text.

local function make_defense_buttons()
	for i = 1, #defense_type_array, 1 do
		local button_group = display.newGroup() -- Make a group to hold all button elements

		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 

		local button_text = display.newText( "0", 0, 0, native.systemFontBold, 14 ) -- make a text field
		button_text.text = defense_type_array[i].cost -- Display the cost
		button_text.x = 20	-- Set the position of the label
		button_text.y = 20	-- Set the position of the label

		button_group:insert( button ) 		-- Add button shape to group
		button_group:insert( button_text ) 	-- Add text to group

		button_group.shape = button -- add button shape as a property to group
		button_group.label = button_text -- add button text as property of group

		-- Assign these properties to button group
		button_group.index = i
		button_group.x = display.contentWidth - 45 -- adjust the position
		button_group.y = 40 + ( i * 50 )
		button_group:addEventListener( "touch", touch_defense_button )

		table.insert(defense_button_array, button_group ) -- Add button group to array
		control_group:insert( button_group ) -- add button group to control group
	end
end

The select_defense_button() function needs a little work also. This function needs to now set the stroke of the shape rather than the button. After the changes above the button will be a group rather than a shape.

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 -- set the stroke of the shape
		else
			button.shape.strokeWidth = 0 -- set the stroke of the shape
		end
	end
end

Showing disabled buttons

If a defense is not available to a player, due to not having enough energy to meet the cost, it should display as disabled. For this example I’ll make buttons that are disabled appear transparent. When a button is available it will be opaque.

Add a new function to handle this above touch_defense_button(). This new function will loop through each button in the defense_button_array and compare it’s cost to the current energy value and set it’s alpha accordingly.

-- *** Add a new function to enable and disable buttons based on energy available
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 

To set up the initial state for these buttons we should call on this function when the game begins. Place a call below make_defense_buttons().

enable_disable_buttons()

Last, when the energy value changes we need to update the buttons to show a new state. Find the energy_recharge() function and add a call to enable_disable_buttons() there.

local function energy_recharge()
	energy = energy + ENERGY_RECHARGE_RATE
	enable_disable_buttons() -- *** Update buttons 
	update_energy()
end>

This works here but might need to be rethought if the recharge rate were slower.

Here's a full listing of the code

Continue reading Corona – Tower Defense Game Experiments 11

Corona – Tower Defense Game Experiments 10

Giving the player the ability to place different types of defenses adds interest to the game. It opens up different types of strategies and different types of game play.

Looking at Plants vs Zombies you will see a wide variety of defense types. Here’s a list of the types of plants available: http://plantsvszombies.wikia.com/wiki/Gallery_of_Plants

Defenses can have a variety of features. Core ideas might be:

  • Damage – how much damage the defense applies to an enemy
  • Rate of fire – how often the defense fires
  • Special effect – A special ability only applied to this type of defense
  • Cost – The energy cost to place the defense

Really you could boil the list down cost vs power. This is the essential formula that creates balance and inspires game play. All of the features, except cost, are a measure of power for a defense. Cost is the balancing factor. Energy is collected over time, gives players the choice to build fewer more expensive defenses, or more less powerful defenses, or build a mix of each.

The key factor to making the game playable is a balance of cost vs power. Adding a mix of special features add a lot of interest to the game, provided the special features work well in cost vs power balance. As the programmer you can make anything happen. You can make the most powerful weapons cost very little. But this makes for an uninteresting playing experience. As a game design this is where you your real work. Finding the right balance of cost vs power for each element while providing some interesting effects.

In the example here I’ll keep the features simple. each defense will have a rate of fire, damage, and a cost.

Rate of fire will set the interval, in milliseconds, between shots for a defense.

Damage will determine how much subtracted from an enemies life property with each hit.

Cost sets the amount of energy that is paid to create this type of defense.

I’ve also added one special feature. One type of defense will also momentarily stop an approaching alien.

defense_type_array

This array will hold a description of each defense type. With properties holding values for each of the features above. Here’s an example:

{name="Standard",	rof=1000,	damage=1,	cost=50}

This is standard defense, it all of the features of the original defense. It fires once every 1000 ms, it subtracts 1 from the life of an enemy with each hit, and costs 50 energy to create.

The name property will be used for special abilities.

I created an array of these tables named defense_type_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}
							}

There will be one button for each of items in this array.

We need to be able to identify each defense placed on the board. So far I haven’t used any graphics, so there isn’t a picture for each, yet. For now I will match the color of the button to the color of the defense. When the buttons are created I generate a color for each. As a temporary measure I will put the color in the defense type tables for easy reference.

In the make_defense_button() function add the following:

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 )

		-- For this example I'll put the red values in each of these tables
		-- this way we can color each of the defense elements to match the buttons.
		defense_type_array[i].red = r 

		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

touch_tile()

Touching a tile creates a new defense. This is also where we check the available energy and pay the cost for the defense.

local function touch_tile( event ) -- Check the type of defense
	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 -- Get the cost for this defense type

		if energy >= cost then -- Use cost here
			energy = energy - cost -- and here
			local defense = make_defense( tile_x, tile_y )
			defense.col = tile.col
		end
	end
end

make_defense()

Each defense will now have a type and each will have different abilities. Each defense should keep track of it’s features: roy, damage and defense_name.

As an alternative, each defense could hold a reference to the table describing the defense features. Here I chose the first option. If there were many defense features the second option might be better.

local function make_defense( x, y )
	local defense = display.newRect( 0, 0, 32, 32 )

	-- Get all of the properties describing this weapon and store them in the new defense
	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

Here I just transferred the features across and set them as properties of the defense that was created. Notice that I also got the red property and used this to set the color of the defense. The defense should now match the color of button. Later when images are added we can dump the color.

make_bullet()

Bullets created earlier were all the same. Now we want them to have unique properties described by the defense that fired them. The make_bullet() function doesn’t have a reference to the defense. But, it is called from the defense_defend() function that does have a reference to the defense. To share information between the two I’ll have make_bullet() return a reference to the bullet.

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 -- better return the bullet so the defense_defend() function can work with it
end

defense_defend()

The defense_defend() function creates bullets. Each bullet will need to know it’s damage value. To get the damage we need to have a reference to the defense. Here we get the bullet that was returned and assign it the damage and the name of the defense type that fired it. The name is important since I’ll use it for special effects.

Damage is easy, instead of subtracting 1 from alien.life. Here we replace this with bullet.damage. In this way each bullet can subtract it’s own damage value.

Next we need to check the defense_name. If the name is “Stun” I need to stop the enemy, and then start again after a short delay. Since the movement is handled by a transition we need to cancel the previous transition. Then add a new transition with a delay. Here I used the ALIEN_SPEED constant to make sure the enemy is moving at the same speed.

To get the speed right I needed to know the distance. To get the remaining distance to move the enemy I subtracted the current y from the alien_target_y value.

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 ) -- Get the bullet
			bullet.damage = defense.damage 	-- assign a damage value to the bullet
			bullet.defense_name = defense.defense_name 		-- get the name of the defense
			break
		end
	end
end

make_alien()

I want to add a weapon with a special feature. One of the weapons will “Stun” an enemy. This will have the effect of stopping the enemy momentarily. To make this effect happen the current transition will have to be removed from the enemy and replaced with a new transition. The new transition will have a delay.

The challenge here is making the enemy continue moving at the same speed to the same end point as before it was stopped. To make this happen I need to know the end point, and know the rate of movement.

The end point for enemies was calculated in the make_alien() function previously. To make things more organized and efficient I defined a variable to hold the ending y value for enemies.

local alien_target_y = ( TILE_SIZE + TILE_MARGIN ) * TILE_ROWS -- Ending y value for aliens

This would need to be defined after TILE_SIZE, TILE_MARGIN and TILE_ROWS are defined. This value will probably not change, so it could have be declared as a constant I suppose.

Enemies will also need a rate of movement in px per sec. I defined this at the top as a constant.

local ALIEN_SPEED = 1000 / 20 	-- Assign an alien speed 20px per sec

Multiplying this by the distance will give us the time in ms that enemies will move at a rate of 20 px per sec.

Now we need to retool the make_alien function() to make use of these new variables.

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
	-- Change the alien movement
	local t = alien_target_y * ALIEN_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

Since enemies start at y of 0. The distance then move is alien_target_y. Multiply this by the ALIEN_SPEED and we get the time in ms for the transition. Notice that I removed some of the other code that was here from the previous example.

check_bullet()

This function checks for bullets hitting enemies. We need to add two new features here: apply damage for specific bullet/defense types, and any special features for any specific bullet type. In this example only the “Stun” type defense will have a special effect.

The

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 - bullet.damage -- Use the damage value of the bullet
					local defense_name = bullet.defense_name -- Get the name of the defense
					-- Check the defense name and apply special stuff
					if defense_name == "Stun" then
						transition.cancel( alien.transition ) 				 -- Cancel the current transition
						local t = ( alien_target_y - alien.y ) * ALIEN_SPEED -- Calculate a new speed
						-- Add a new transition with a delay
						alien.transition = transition.to( alien, { y=alien_target_y,
																	time=t,
																	delay=300,	-- Delay
																	onComplete=remove_alien } )
					end 

				else
					remove_alien( alien )
				end
				remove_bullet( bullet )
				break
			end
		end
	end
end

Here’s a listing of the entire code used so far.

Continue reading Corona – Tower Defense Game Experiments 10

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:
Continue reading Corona – Tower Defense Game Experiments 9

Corona – Tower Defense Game Experiments 8

A key factor to the tower defense game is the economy. If there is no limiting factor to how fast defenses can be created there isn’t much challenge since a player can quickly fill the board with defenses.

There needs to be a limiting factor in how often a player can place a defense. Here I will set up a simple system with an energy value that increases over time and incur an energy cost for placing defenses.

The key to making this system form an interesting game is balancing all of the factors. All of the factors need to be considered. Here’s a list:

  • Speed of enemies – Faster enemies are harder to deal with
  • Speed of bullets – Faster bullets can take care of closer range enemies before a defense is destroyed
  • Defense rate of fire – Defenses that fire faster are more powerful
  • Energy cost to place a defense – Lower cost allows defense to be placed more often
  • Energy Recharge rate – The faster your energy recharges the more often you can place a defense

Note: Setting a cap on total available energy would be another idea to interest to game play. I didn’t do that here but it would not be hard to add.

This example will add a variable to track energy. Energy will recharge over time at a fixed rate. Placing a defense incurs an energy cost.

First I added a few constants at the top of the script to set fixed values.

Energy recharge rate sets the amount of energy is added each timer the energy timer updates. Energy defense cost sets the value subtracted from energy each time a defense is placed. Energy timer time sets the time in milliseconds that energy is recharged, added to energy.

local ENERGY_RECHARGE_RATE = 1 -- Set the rate to recharge
local DEFENSE_ENERGY_COST = 50 -- Set the cost for a defense
local ENERGY_TIMER_TIME = 150	-- Timer to recharge energy

We also need a few variables to keep track of energy and timers. Energy holds your current energy. Energy timer holds a reference to the timer that updates energy.

Note, I moved the alien timer to the top of my script to keep all of the timers together. This variable is not new.

local alien_timer 	-- Moved this to the top
local energy_timer 		-- reference the energy timer
local energy = 0 		-- set the starting energy

I needed a text field to display the current energy value. I defined this at the top since it might be accessed from any where.

local energy_text -- Declare a text field to display energy

I thought it might be good to have the energy display and other UI elements in their own group. So I added a group to hold these:

local control_group = display.newGroup() -- Add a group for UI stuff

Next I need to add a function to update the energy display, and a function to increase the energy value. I separated these into two functions thinking it might be more flexible in the future if I need to include code to position and adjust the position of the text field with each update.

The update_energy() function displays the current energy value in the text field.

The energy_recharge() function increase the energy value, then calls update_energy() to refresh the display.

In this arrangement the energy display is only updated when energy is increased. This works if the energy recharge rate is not too slow. If the recharge rate is slow you you could place a defense and not see the energy value update until the recharge timer updates energy. If this is effecting you, add a call to update_energy() to make_defense().

Last I added create the energy timer. Here we create a new timer that updates at the rate set by ENERGY_TIMER_TIME. With each update it calls energy_recharge(), and loops forever.

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 ) 

Last, I need to incur the energy cost for placing a defense element. Basically this means checking that there is sufficient energy to pay for a defense. If there is sufficient energy, subtract the cost from energy and create the defense.

New defense elements are created in the touch_tile() handler. Here I added a little logic to check the energy, subtract the energy cost and place the new defense.

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 		-- Check the energy
			energy = energy - DEFENSE_ENERGY_COST	-- Pay energy for defense
			local defense = make_defense( tile_x, tile_y ) 
			defense.col = tile.col 
		end 						  
	end
end

Still the game is lacking many features that would make it truly interesting to play. That said, what we have here is an outline of the mechanics that cover many of the elements that make the game function. Building on these ideas, adding compelling art, and a theme is what will make the game interesting.

Here’s the full source code

Continue reading Corona – Tower Defense Game Experiments 8

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 

Continue reading Corona – Tower Defense Game Experiments 7

Corona – Tower Defense Game Experiments 6.2

plants vs Zombies works on a strict grid with enemies advancing in rows, and defenses firing only on their row. Other tower defense games allow defenses to fire at any enemy within range. The last post tested an example that had defenses firing only within their row. In this example I will create defenses that fire at any enemy within range.

Get Distance to enemy

To get the distance to an enemy in pixels we can take the Pythagorean formula: a2 + b2 = c2. In Lua terms this might look like:

c = math.sqrt((a*a)+(b*b))

To make this more useful and easier to work with we can wrap it in a function that returns the distance between two points. The example below takes two sets of x and y values and returns the distance between these points.

local function get_distance( x1, y1, x2, y2 )
	local dx = x1 - x2
	local dy = y1 - y2
	return math.sqrt( (dx*dx) + (dy*dy) ) -- Return the distance
end

If you’re feeling Object Oriented, you could make a a point object and pass that to the function. Lua is not an Object Oriented language so we would need to use a table and factory function, but this has a very similar arrangement.

local function Point( x, y )
	return {x=x, y=y}
end

local function get_distance( point_1, point_2 )
	local dx = point_1.x - point_2.x
	local dy = point_1.y - point_2.y
	return math.sqrt( (dx*dx) + (dy*dy) ) -- Return the distance
end

With this method, using either of the two functions above, a defense can make the decision to fire on an enemy when that enemy is within range.


Deciding when to fire

The defense_defend( defense ) function is called from the timer assigned to a defense. Here we need to examine each enemy and measure the distance between the enemy and the defense. If the range is less a specified range we have the defense fire at that enemy.

I set the range of the defense as a “constant” at the top of script for easy use.

local DEFENSE_RANGE = 250

Next I need to modify defense_defend(defense) to work with the get_distance(x1,y1,x2,y2) function above. I used the first function in this example. Here we loop through the alien_array and look at the distance to each alien. If there is an alien within range we fir on it and break the loop.

-- This new function will look at the board and find eligible targets
local function defense_defend( defense )
	for i = 1, #alien_array, 1 do 		-- Loop through alien_array
		local alien = alien_array[i]	-- get the alien

		if get_distance( defense.x, defense.y, alien.x, alien.y ) < DEFENSE_RANGE then -- Check the distance
			make_bullet( defense.x, defense.y, alien.x, alien.y )
			break
		end
	end
end

Since get_distance(x1,y1,x2,y2) returns the distance as a number we can place this function call inside the if expression:

if get_distance( defense.x, defense.y, alien.x, alien.y ) < DEFENSE_RANGE then ...

I will also have to modify the make_bullet() function to allow it fire on a target anywhere on the screen. You’ll notice this function has been modified from make_bullet(x,y) to make_bullet(x1,y1,x2,y2).

Sending bullet toward the target

Using this method bullets will be fired across columns. This means I will have to animate both the x and the y of the bullet. In the previous versions I had only animated the y value of the bullet. In these versions bullets were animated to a y of 0, targeting the top edge of the screen.

To make this happen I need to modify the make_bullet( x, y ) function. Before the function only had to send a bullet to the top of the screen, in which case all that was needed was a starting position. Now it needs to know the position of the target also. So here we’ll modify make_bullet(x,y) to make_bullet( start_x, start_y, target_x, target_y ).

Since the distance to the target will always be different we’ll need to get the distance again and multiply by the constant BULLET_SPEED, thus making sure the bullets move at a consistent speed no matter what the distance.

Here’s a new version of the make_bullet() function

local function make_bullet( start_x, start_y, target_x, target_y )
	local bullet = display.newCircle( 0, 0, 5 )
	bullet:setFillColor( 0, 0, 0 )
	bullet.x = start_x
	bullet.y = start_y
	table.insert( bullet_array, bullet )

	-- The bullet speed and transition need to be retooled
	-- We will end up calling get_distance twice, we could optimize by passing the distance from defense_defend
	local d = get_distance( start_x, start_y, target_x, target_y )  -- Get the distance to the target
	local bt = d * BULLET_SPEED 

	bullet.transition = transition.to( bullet, {y=target_y, x=target_x, time=bt, onComplete=remove_bullet} )
end

Following the flow of code here, the defense_defend() function calls on get_distance(). If an enemy is within the distance we move on to make_bullet(). Where make_bullet() calls on get_distance() again. There’s room for a small performance optimization here. We could pass the distance to make_bullet() and not have to do the math again. I’ll things as they are for now, and may be address this in a future update.

Here’s a full listing of the code for this example:
Continue reading Corona – Tower Defense Game Experiments 6.2

Corona – Tower Defense Game Experiments 6.1

Defensive Firing Habits

This time the goal will be to control the firing habits of defense elements. There are several factors that can be used to control the firing habits of defenses.

Distance to enemies

The distance to an enemy can be set for defense. This gives the option choosing defenses with greater or lesser range.

Column position

In Plants vs Zombies defenses only fire at enemies in the same column and distance is not a factor. Though, this could be combined with distance for more variation.

Firing angle

In the original experiments the defenses only fire straight up the screen. There’s nothing to stop us from having defenses fire at an enemy in any column, except a few lines of code.

Firing at enemies in same column

In this experiment we’ll make the defenses fire only when there is an enemy in their column. To make this happen defenses and enemies will have to know their column number.

make_alien()

When enemies are created I choose a random column and position the enemy in that column. At this time we need to add a property variable to the enemy and set it to the column number. The place for this to happen is make_alien().

NOTE: I realized when I created the earlier version I used the name row, when I setting the column! So I changed this variable name to col.

local function make_alien()
	local alien = display.newRect( 0, 0, 32, 32 )
	alien:setFillColor( 0, 200, 0 )

	local col = math.random( 1, TILE_COLS ) -- Oops! I had previously used row instead of col here!
	alien.col = col 			-- Assign this alien his col position
	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

make_grid()

When creating defense elements we tap on a tile. The tile positions the new defense at the tile’s location. Since defense will no need to know their column number, the tile will also need to know it’s column number so it can pass it along to the defense that is created. Tiles are created in the make_grid() function. Here I added one line of code assigning a new property variable, col, to each tile.

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 -- Tiles need to know col

		 	tile.has_defense = false  

		 	tile:addEventListener( "touch", touch_tile )
		 	tile_group:insert( tile )
		 end
	end
end

touch_tile()

Touching a tile creates a defense. Touch events on tiles are handled in the touch_tile() handler. Here we need to get the column number from the tile and assign it to the new defense that is created. Earlier I had just made a defense with getting a reference to the defense. Note that the make_defense() function does return the new defense created! I had ignored the return value previously. We’ll use it now. Here we get a reference to the new defense and assign it a new property variable.

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 ) -- Get a reference to the new defense defense.col = tile.col -- assign the col to the new defense
	end
end

make_defense()

Next we need defenses to think before they shoot. Each defense will need to look at all of the enemies on the screen and check their col agains the col of the defense. If cols match the defense fires, others not. To do this I added an intermediate function call from the timer, rather than call make_bullet() directly from the timer.

The new function, defense_defend( defense ), takes the defense as a parameter.

The defense timer is created when the defense is created, in make_defense(). Here’s an updated version of make_defense():

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 ) 

	-- Change the timer handler to call defense_defend( defense )
	defense.timer = timer.performWithDelay( 1000, function() defense_defend( defense ) end, -1 )

	return defense
end

defene_defend()

The defense_defend( defense ) function needs to examine all of the enemies and check their col. Here I will use a loop to look at each of the enemies in the alien_array. Since defenses can only fire once, if we find a target we break the loop, and stop checking for new enemies.

-- This new function will look at the board and find eligible targets
local function defense_defend( defense )
	for i = 1, #alien_array, 1 do 		-- Loop through alien_array
		local alien = alien_array[i]	-- get the alien
		if alien.col == defense.col then -- check column
			make_bullet( defense.x, defense.y )
			break
		end
	end
end

Time to test! If everything is working correctly defense should now only fire when there is an enemy in their column.

Here’s a listing the entire code for this version:

Continue reading Corona – Tower Defense Game Experiments 6.1