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.

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 5 of this article. Click here to view the first page.

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!

Now you can only build a limited amount of monsters.

Limited gold.

Now you can only build a limited amount of 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:

  1. Defining a road for the enemies to follow
  2. Moving the enemy along the road
  3. 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.

Road - waypoint hierarchy

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.

Screen Shot 2015-07-24 at 12.09.11 PM

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:

  1. From the waypoints array, you retrieve the start and end position for the current path segment.
  2. 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.
  3. Check whether the enemy has reached the endPosition. If yes, handle these two possible scenarios:
    1. The enemy is not yet at the last waypoint, so increase currentWaypoint and update lastWaypointSwitchTime. Later, you'll add code to rotate the enemy so it points in the direction it's moving, too.
    2. 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.
  1. The enemy is not yet at the last waypoint, so increase currentWaypoint and update lastWaypointSwitchTime. Later, you'll add code to rotate the enemy so it points in the direction it's moving, too.
  2. 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.

waypoints2

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.

certaindeath

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.

BugFollowsRoadWithoutRotating

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.

Jeff Fisher

Contributors

Jeff Fisher

Author

Barbara Reichart

Author

Mitch Allen

Tech Editor

Sean Duffy

Final Pass Editor

Over 300 content creators. Join our team.