How to Create a Tower Defense Game in Unity – Part 1
In this tutorial, you’ll build a 2D tower defense game using the latest Unity engine. By Jeff Fisher.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How to Create a Tower Defense Game in Unity – Part 1
30 mins
- A View from the Ivory Tower
- Getting Started
- X Marks the Spot: Placement
- Place Monsters
- One Monster Per Location
- Use The Right Prefab
- Level Up Those Monsters
- Define Monster Levels
- Define the Current Level
- Upgrade Those Monsters
- Test Upgrade Capability
- Enable Upgrading With Gold
- Pay Gold - Game Manager
- Assign the Label Object to the Script
- Check the Player's "Wallet"
- Get the Money!
- Require Gold for Monsters
- Tower Politics: Enemies, Waves and Waypoints
- Create a Road With Waypoints
- Spawn the Enemies
- Move Monsters Down the Road
- Give the Enemies A Sense of Direction
- Check That It All Works
- Where To Go From Here?
Upgrade Those Monsters
Switch back to your IDE and add the following method to MonsterData
:
public MonsterLevel GetNextLevel()
{
int currentLevelIndex = levels.IndexOf (currentLevel);
int maxLevelIndex = levels.Count - 1;
if (currentLevelIndex < maxLevelIndex)
{
return levels[currentLevelIndex+1];
}
else
{
return null;
}
}
In GetNextLevel
you get the index of currentLevel
and the index of the highest level provided the monster did not reach the maximal level to return the next level. Otherwise, return null
.
You can use this method to figure out whether upgrading the monster is possible.
Add the following method to increase a monster's level:
public void IncreaseLevel()
{
int currentLevelIndex = levels.IndexOf(currentLevel);
if (currentLevelIndex < levels.Count - 1)
{
CurrentLevel = levels[currentLevelIndex + 1];
}
}
Here you get the index of the current level, and then you make sure it’s not the maximum level by checking if it’s smaller than levels.Count - 1
. If so, set CurrentLevel
to the next level.
Test Upgrade Capability
Save the file and then switch to PlaceMonster.cs in your IDE and add this new method:
private bool CanUpgradeMonster()
{
if (monster != null)
{
MonsterData monsterData = monster.GetComponent<MonsterData>();
MonsterLevel nextLevel = monsterData.GetNextLevel();
if (nextLevel != null)
{
return true;
}
}
return false;
}
First check whether there is a monster that you can upgrade by checking the monster
variable for null
. If this is the case, you get the current level of the monster from its MonsterData
.
Then you test whether a higher level is available, which is when GetNextLevel()
doesn’t return null
. If up-leveling is possible, you return true
, otherwise, you return false
.
Enable Upgrading With Gold
To enable the upgrade option, add an else if
branch to OnMouseUp
:
if (CanPlaceMonster())
{
// Your code here stays the same as before
}
else if (CanUpgradeMonster())
{
monster.GetComponent<MonsterData>().IncreaseLevel();
AudioSource audioSource = gameObject.GetComponent<AudioSource>();
audioSource.PlayOneShot(audioSource.clip);
// TODO: Deduct gold
}
Check whether an upgrade is possible with CanUpgradeMonster()
. If yes, you access the MonsterData
component with GetComponent()
and call IncreaseLevel()
, which increases the level of the monster. Lastly, you trigger the monster's AudioSource.
Save the file and switch back to Unity. Run the game, place and upgrade as many monsters as you like...for now.
Pay Gold - Game Manager
Right now it’s possible to build and upgrade all the monsters immediately, but where's the challenge in that?
Let's drill down into the issue of the gold. The problem with keeping track of it is that you need to share information between different game objects.
The following image shows all the objects that want a piece of the action.
You'll use a shared object that's accessible to other objects to store this data.
Right-click in the Hierarchy and select Create Empty. Name the new game object GameManager.
Add a C# script named GameManagerBehavior to GameManager, then open the new script in your IDE. You'll display the player's total gold in a label, so add the following line to the top of the file:
using UnityEngine.UI;
This lets you access UI-specific classes like Text
, which the project uses for the labels. Now add the following variable to the class:
public Text goldLabel;
This will store a reference to the Text
component used to display how much gold the player owns.
Now that GameManager
knows about the label, how can you ensure the amount of gold stored in your variable and the amount displayed on the label are in sync? You'll create a property.
Add the following code to GameManagerBehavior
:
private int gold;
public int Gold {
get
{
return gold;
}
set
{
gold = value;
goldLabel.GetComponent<Text>().text = "GOLD: " + gold;
}
}
Seem familiar? It’s similar to the CurrentLevel
you defined in Monster
. At first, you create a private variable, gold
, to store the current gold total. Then you define a property named Gold
-- creative, right? -- and implement a getter and setter.
The getter simply returns the value of gold
. The setter is more interesting. In addition to setting the variable's value, it also sets the text
field on goldLabel
to display the new amount of gold.
How generous do you feel? Add the following line to Start()
to give the player 1000 gold, or less if you feel miserly:
Gold = 1000;
Assign the Label Object to the Script
Save the file and switch to Unity.
In the Hierarchy, select GameManager. In the Inspector, click on the circle to the right of Gold Label. In the Select Text dialog, select the Scene tab and select GoldLabel.
Run the scene and the label displays Gold: 1000.
Check the Player's "Wallet"
Open PlaceMonster.cs in your IDE, and add the following instance variable:
private GameManagerBehavior gameManager;
You'll use gameManager
to access the GameManagerBehavior
component of the scene's GameManager. To assign it, add the following to Start()
:
gameManager = GameObject.Find("GameManager").GetComponent<GameManagerBehavior>();
You get the GameObject named GameManager using GameObject.Find()
, which returns the first game object it finds with the given name. Then, retrieve its GameManagerBehavior
component and store it for later.
Note: You could have accomplished this by setting the field in Unity's editor, or by adding a static method to GameManager
that returns a singleton instance from which you could get the GameManagerBehavior
.
However, there's a dark horse method in the block above: Find
, which is slower at runtime but convenient and ok to use sparingly.
Note: You could have accomplished this by setting the field in Unity's editor, or by adding a static method to GameManager
that returns a singleton instance from which you could get the GameManagerBehavior
.
However, there's a dark horse method in the block above: Find
, which is slower at runtime but convenient and ok to use sparingly.
Get the Money!
You don't yet deduct gold, so add this line twice inside OnMouseUp()
, replacing each of the comments that read // TODO: Deduct gold
:
gameManager.Gold -= monster.GetComponent<MonsterData>().CurrentLevel.cost;
Save the file and switch to Unity, upgrade some monsters and watch the Gold readout update. Now you deduct gold, but players can build monsters as long as there is space; they just get into debt.
Infinite credit? Awesome! But you can't allow this. Monsters should only be placed when the player has enough gold.