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?
Require Gold for Monsters
Switch to PlaceMonster.cs in your IDE, and replace the contents of CanPlaceMonster()
with the following:
int cost = monsterPrefab.GetComponent<MonsterData>().levels[0].cost;
return monster == null && gameManager.Gold >= cost;
Retrieve the cost for placing the monster from levels
in its MonsterData
. You then check that monster
is not null
and that gameManager.Gold
is greater than the cost.
Challenge: Add the check for whether a player has enough gold in CanUpgradeMonster()
by yourself.
[spoiler]
Replace this line:
return true;
with this one:
return gameManager.Gold >= nextLevel.cost;
This checks if the player has more Gold than the cost of the upgrade.
[/spoiler]
Save and run the scene in Unity. Go ahead, just try to place unlimited monsters!
Tower Politics: Enemies, Waves and Waypoints
Time to "pave the road" for your enemies. Enemies appear at the first waypoint, move towards the next and repeat until they reach your cookie.
You’ll get the enemies marching by:
- Defining a road for the enemies to follow
- Moving the enemy along the road
- Rotating the enemy so it looks forward
Create a Road With Waypoints
Right-click in the Hierarchy and select Create Empty to make a new empty game object. Name it Road, and make sure it’s at position (0, 0, 0).
Now, right-click on Road in the hierarchy and create another empty game object as a child of Road. Name it Waypoint0 and set its position to (-12, 2, 0) -- this is where enemies start their assault.
Create five more waypoints the same way with the following names and positions:
- Waypoint1: (X:7, Y:2, Z:0)
- Waypoint2: (X:7, Y:-1, Z:0)
- Waypoint3: (X:-7.3, Y:-1, Z:0)
- Waypoint4: (X:-7.3, Y:-4.5, Z:0)
- Waypoint5: (X:7, Y:-4.5, Z:0)
The following screenshot highlights the waypoint locations and the resulting path.
Spawn the Enemies
Now to make some enemies to follow the road. The Prefabs folder contains an Enemy prefab. Its position is (-20, 0, 0), so new instances will spawn off screen.
Otherwise, it's set up much like the Monster prefab, with an AudioSource
and a child Sprite
, and it’s a sprite so you can rotate it later without rotating the forthcoming health bar.
Move Monsters Down the Road
Add a new C# script named MoveEnemy to the Prefabs\Enemy prefab. Open the script in your IDE, and add the following variables:
[HideInInspector]
public GameObject[] waypoints;
private int currentWaypoint = 0;
private float lastWaypointSwitchTime;
public float speed = 1.0f;
waypoints
stores a copy of the waypoints in an array, while [HideIninspector]
above waypoints
ensures you cannot accidentally change the field in the inspector, but you can still access it from other scripts.
currentWaypoint
tracks which waypoint the enemy is currently walking away from, and lastWaypointSwitchTime
stores the time when the enemy passed over it. Finally, you store the enemy's speed
.
Add this line in Start()
:
lastWaypointSwitchTime = Time.time;
This initializes lastWaypointSwitchTime
to the current time.
To make the enemy move along the path, add the following code to Update()
:
// 1
Vector3 startPosition = waypoints [currentWaypoint].transform.position;
Vector3 endPosition = waypoints [currentWaypoint + 1].transform.position;
// 2
float pathLength = Vector3.Distance (startPosition, endPosition);
float totalTimeForPath = pathLength / speed;
float currentTimeOnPath = Time.time - lastWaypointSwitchTime;
gameObject.transform.position = Vector2.Lerp (startPosition, endPosition, currentTimeOnPath / totalTimeForPath);
// 3
if (gameObject.transform.position.Equals(endPosition))
{
if (currentWaypoint < waypoints.Length - 2)
{
// 3.a
currentWaypoint++;
lastWaypointSwitchTime = Time.time;
// TODO: Rotate into move direction
}
else
{
// 3.b
Destroy(gameObject);
AudioSource audioSource = gameObject.GetComponent<AudioSource>();
AudioSource.PlayClipAtPoint(audioSource.clip, transform.position);
// TODO: deduct health
}
}
Step by step:
- From the waypoints array, you retrieve the start and end position for the current path segment.
- Calculate the time needed for the whole distance with the formula time = distance / speed, then determine the current time on the path. Using
Vector2.Lerp
, you interpolate the current position of the enemy between the segment's start and end positions. - Check whether the enemy has reached the
endPosition
. If yes, handle these two possible scenarios: - The enemy is not yet at the last waypoint, so increase
currentWaypoint
and updatelastWaypointSwitchTime
. Later, you'll add code to rotate the enemy so it points in the direction it's moving, too. - The enemy reached the last waypoint, so this destroys it and triggers a sound effect. Later you'll add code to decrease the player's
health
, too.
- The enemy is not yet at the last waypoint, so increase
currentWaypoint
and updatelastWaypointSwitchTime
. Later, you'll add code to rotate the enemy so it points in the direction it's moving, too. - The enemy reached the last waypoint, so this destroys it and triggers a sound effect. Later you'll add code to decrease the player's
health
, too.
Save the file and switch to Unity.
Give the Enemies A Sense of Direction
In its current state, the enemies don’t know the order of the waypoints.
Select Road in the Hierarchy, and add a new C# script named SpawnEnemy. Then open it in your IDE, and add the following variable:
public GameObject[] waypoints;
You'll use waypoints
to store references to the waypoint in the scene in the proper order.
Save the file and switch to Unity. Select Road in the Hierarchy and set the Size of the Waypoints array to 6.
Drag each of Road's children into the fields, putting Waypoint0 into Element 0, Waypoint1 into Element 1, and so on.
Now you have an array that contains neatly ordered waypoints so there’s a path – note that they never retreat; they will die trying to get a sugar fix.
Check That It All Works
Head to SpawnEnemy in your IDE, and add the following variable:
public GameObject testEnemyPrefab;
This keeps a reference to the Enemy prefab in testEnemyPrefab
.
To create an enemy when the script starts, add the following code to Start()
:
Instantiate(testEnemyPrefab).GetComponent<MoveEnemy>().waypoints = waypoints;
This instantiates a new copy of the prefab stored in testEnemy
and assigns it waypoints to follow.
Save the file and switch to Unity. Select Road in the Hierarchy and set its Test Enemy to the Enemy prefab.
Run the project to see the enemy follow the road.
Did you notice they aren’t always looking where they're going? Funny! But you’re trying to be a professional here, yes? Continue with part two to learn how to get them to put their best faces forward.