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

Corona – Tower Defense Game Experiments 5

Destroying Aliens with bullets

In the last post I created bullets that fire from a defense at advancing enemies. I still need to check if an enemy is hit, and if so reduce it’s life, and remove it if it has reached 0 life.

I see many example that would use physics for the collision detection for this. I feel that physics may be over kill for what I am doing here. it’s also my guess that using physics would add a lot of processing overhead. Physics also brings up other problems that need to be solved. All in all I physics, in this case will create more problems than it solves. So, I’m going to opt to use a hit test and manage bullets and alien enemies in arrays.

A few notes on code styles. The code is now getting longer and so I decided to make a few style changes to help me keep organized. Constants, these are variables that will not change value. These will be UPPER_CASE. Lua doesn’t have actual constants, I’m using upper case as a reminder.

Hit Test – Bullets vs Aliens

For each bullet fired, I will need to check if that bullet hits an enemy. To determine a hit take the x and y location of the bullet and check if it is inside the bounds of the enemy. The bounds of an object is represented by four values that define the edges of the rectangle surrounding the object. Use display.contentBounds to get the bounds object. This object will have properties of:

  • xMin (defines the left edge)
  • xMax (defines the right edge)
  • yMin (defines the top edge)
  • yMax (defines the bottom edge)

To determine if a point, an x and y pair, is within the bounds of an object use an if statement to compare the bounds properties and the x and y of the point. Check if the x value is greater than xMin and less than xMax, while the y value is greater than yMin and less than yMax. Something like this:

if x > bounds.xMin and x < bounds.xMax and y > bounds.yMin and y < bounds.yMax then
... HIT! ...
end

Checking for a single hit is simple and straight forward. With multiple bullets and enemies on the screen at the same time the problem is a more complicated. For each bullet we need to check for a hit against each enemy. To organize this, all bullets need to be in an array and all enemies in another array. In the future I may want enemies to attack defenses so, I made an array to hold defenses also.

local defense_array = {}
local alien_array = {}
local bullet_array = {}

With the bullets in an array and the enemies in another array we can look at each bullet and perform a hit test against each enemy with that bullet.

Making Bullets

I used the same strategy for bullets that was used for other display elements tiles, aliens and defenses. I need a function to make bullets and another to remove them.

BULLET_SPEED

I used a transition to move the bullets up the screen. The time it takes a bullet to move up the screen will vary by the distance the bullet has to travel. To get the bullets to move at a consistent rate I created the variable BULLET_SPEED. This is a “constant”, that is it’s value will NOT change while the program runs. Corona and Lua do not support actual constants, but this is the intent for this variable, so I will use a style to mark it as such.

I set the speed in pixels per millisecond (1/1000 of a second). I decided on a speed of 400 pixels per second. In milliseconds this becomes 1000 / 400.

local BULLET_SPEED = 1000 / 400

To create transition that moves at this constant rate multiply the distance by BULLET_SPEED and set this value as the time of the transition.

Bullets will be black circles, for now.

Here are two functions to make and remove bullets.

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

To detect a hit I need to look at the position of each bullet and alien and apply a hit test each frame. I added an enterFrame event and handler. I also separated the hit test into it’s own function since it might be useful for other purposes in the future. Note the hit test returns true on a hit or false. This way you can place a hit test inside of an if statement, as in the code below:

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 on_enterframe( event )
	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

Runtime:addEventListener( "enterFrame", on_enterframe )

Here’s a full code listing so far.
Continue reading Corona – Tower Defense Game Experiments 5

Corona – Tower Defense Game Experiments 4

Bullets

In the last couple posts I modeled some simple ideas for creating a Tower Defense game like Plants vs Zombies. So far I have a tiled background, enemies that advance down the screen, and the ability to add a defense at any particular tile.

At this point the defense and the enemies still do not interact. The next step is to give the defense elements the ability to fire bullets at the advancing enemies.

Bullet Factory

Throughout the process I have been using a system based on “factory functions” to create new objects. I like this method it keeps me organized. I’ll use it again to create bullets. Though Lua is not an OOP language factory functions have some the feeling of using new to create an instance.

The bullets will be simple circles. I will use a transition.to() to move the bullets up the screen.

The make_bullet() function takes the starting x and y position of the bullet.

local function remove_bullet( bullet )
	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

	bullet.transition = transition.to( bullet, {y=0, time=1000, onComplete=remove_bullet} )
end

Calling functions from timer.perfromWithDelay()

Now it’s time to send the bullets up the screen. To make this happen I’ll use a timer. Each defense element will need a timer. I’m adding a reference to each timer as it is created, to the defense element the timer is created with. This way if a defense element is destroyed in the future that element will be able to cancel it’s timer.

defense.timer = timer.performWithDelay( 1000, function() make_bullet( defense.x, defense.y ) end, -1 )

The second parameter is a function, that is called with each timer event. We need to call make_bullet(x,y), this function requires the x and y location of the bullet. We can’t write this as:

-- WRONG!
defense.timer = timer.performWithDelay( 1000, make_bullet( defense.x, defense.y ), -1 )

Here we would be invoking make_bullet( defense.x, defense.y ) at the time we created he timer. The effect would be one bullet fired, rather than firing a bullet with each timer event. The timer in this case would act on what was RETURNED from make_bullet(), which would be nil.

In the correct format above I wrapped the call to make_bullet( defense.x, defense.y ) in a function. This is interesting because defense is a local variable and lost when make_defense() ends. The handler function defined inside the timer function encloses the variable defense (defense.x and defense.y) effectively preserving the values they had at the time that this function was defined.

Since this project has gone through a few changes, I’ll post the full source code I have been using up to this point for reference.

Continue reading Corona – Tower Defense Game Experiments 4