Setting Up a Stage in Tower Defense Template
Now that we have created towers and enemy Agents with a variety of effects, the next step is to set up a stage where players can build towers to defend a location from those enemies.
When creating a game, it is often worthwhile to create placeholder art assets to playtest early design work. This allows us to ensure that systems and level design work as intended before committing time and effort to making high quality art that needs to be changed in the event that the design needs to change.
To show this stage of the development process, the starter kit includes a prototype version of stage 1. The prototype stage is roughly the same as stage 1, but the environment art is much simpler than the final version that is playable in the game. Instead of the complex world geometry of the final stage, the prototype is made up entirely of 3D primitives available in the Unity editor.
When going from the prototype to the final stage, the following technical considerations were made to produce a release-quality stage:
The stage mesh was extended to ensure that players would never be able to see outside of the bounds of the playing area.
World Mesh Combination
The stage mesh was broken up into chunks to make sure that it is not too large. Meshes much larger than the camera frustum are inefficient because many verts are drawn offscreen. It is best to try to avoid this while keeping as much of the world combined into fewer meshes to minimize draw calls and frustum culling costs.
A single approximate collision mesh was created. This is so that projectiles will hit the world objects, as well as to project the cursor into the world when placing towers.
Removing Non-Visible Faces
Non-visible faces are removed from world objects were removed. For the most part, this includes faces at the bottom of objects. Removing these faces reduces overdraw which can reduce performance, especially on lower-end devices.
The above steps should be considered when transitioning from prototype art assets to final, higher fidelity assets. Following them will result in a stage that is performant and easy to manage. A stage made using these steps will contain all required objects and components to ensure that the stage works correctly.
The rest of this tutorial focuses on the step-by step creation of a stage from a blank Unity scene, and does not extensively cover the process of creating high quality art assets.
If we want our stage to be accessible to players through the menu systems, we will need to create a level list and add the stage to it. We can achieve this by following these steps:
From the Project window select Create > Starter Kit > Level List
Add the details of the stage to the LevelList ScriptableObject
The stage will now appear in the Level Select menu in the game.
Understanding the LevelList ScriptableObject
The level list allows us to set the number of stage to be shown in the main menu, and we can set the following information for each stage:
The order in which the stage will appear in the menu.
A title for the stage.
A brief string to give players an idea of what to expect from the stage.
The name of the scene that contains the stage.
Setting Up Geometry
Add a large plane to the blank scene created earlier. From the Hierarchy window, Create > 3D Object > Plane
Ensure that the plane’s transform position is set to (0, 0, 0)
Add 3D objects to the stage to establish the layout. Cubes and cylinders are best for this. From the Hierarchy window, Create > 3D Object > Cube/Cylinder
If we already have a more complex mesh created, we can bypass this step and use that instead.
In order to make the stage geometry navigable, we will need to bake the NavMesh.
Select the plane or stage mesh
Open the Object tab in the Navigation window
Check the Navigation Static option
Set the Navigation Area to Walkable
Select the 3D objects that Agents shouldn’t pass through
Open the Object tab in the Navigation window
Check the Navigation Static option
Set the Navigation Area to Non-Walkable
Bake the NavMesh
For enemies to move around the stage, we will need to set up nodes for them to be able to go from point to point. Nodes are also used to define spawn points and points at which the player home base will take damage, should an agent reach it.
We can create a node by adding a Node component to an empty GameObject. This lets us create a mesh which defines where agents will spawn and provides the agents with a way to select a random point within the Node area to navigate to. Being able to select a random point helps ensure that agents do not cluster to a single point.
Create an empty GameObject and name it Navigation Nodes or something similar
Create an empty GameObject and make it a child of Navigation Nodes, name it StartNode
Add a Node component to the Start GameObject
Click the Select/Add Mesh button
Adjust the child GameObject mesh to be the desired shape
Add a Sphere or Capsule Collider component to the StartNode GameObject
Set the Is Collider checkbox to true
Ensure that the Collider encapsulates the node mesh
The Node component allows us to add a new mesh, which will create a child object with an Area Mesh Creator component, where we can define preset shapes or add points to the mesh by clicking between its corners in the Scene view. Points can be removed by holding shift and clicking the points.
In order to define the next node in the sequence, we will need to add a node selector component, either a Fixed Node Selector, which sends the enemies to a specific node, or a Random Node Selector, which will select from a weighted list of possible nodes.
Create another node using the steps listed above
On the StartNode GameObject, add a FixedNodeSelector component
Add an element to Linked Nodes and drag the new node into the created field
The above steps can be repeated as necessary, linking nodes in the order Agents should travel to them, to make sure that they will follow the desired path through the level.
Tower Placement Areas
Towers in the Starter Kit require IPlacementAreas for a player to be able to place them within a stage. After adding an implementation of IPlacementArea to an empty GameObject, we will be able to define places where towers can be placed.
The starter kit contains two implementations of IPlacementArea:
SingleTowerPlacementArea is a IPlacementArea that can contain only one tower.
Create a new GameObject and call it TowerPlacementArea
Add a Sphere Collider to the TowerPlacementArea object
Add a SingleTowerPlacementArea component to the TowerPlacementArea object
Drag the SinglePlacementTile prefab from Prefabs/UI to the Placement Tile Prefab field
Set the TowerPlacementArea’s layer to PlacementLayer
A TowerPlacementGrid represents a gridded area where the player can place multiple towers.
Create an empty GameObject and name it PlacementGrid
Add a TowerPlacementGrid component
Set the desired dimensions for the grid
Drag the PlacementTile prefab from Prefabs/UI to the Placement Tile Prefab field
Unlike the SingleTowerPlacementArea, automatically creates a collider for itself set to the correct size, for input handling.
Adding a Home Base
Enemies do not damage the player’s health when reaching the final node. To remedy this, we’ll need to make some modifications to the final navigation node.
Add a PlayerHomeBase component to the final node GameObject
Set the home base’s Max and Starting Health
Drag the Player SimpleAlignment ScriptableObject into the Alignment field
The Player Home Base has fields to assign Particle Systems. One for a charge effect and another for the attack effect. These are not required.
Understanding the PlayerHomeBase component
The maximum health of the player’s home base.
The amount of health the home base will start with.
Refers to the ScriptableObject that defines which objects can target and damage the home base.
The Particle System will play while an Agent is charging its attack on the home base.
The sound will play while an Agent is charging its attack on the home base.
The Particle System will play while an Agent is done charging and attacks the home base.
The sound will play while an Agent is done charging and attacks the home base.
The attack effect will be played at the conclusion of the charge, when damage is applied, if assigned.
Adding a Tower Library
Now that we have created areas where towers can be placed, we need to set up which towers are available in our stage.
From the Project window: Create > Tower Defense > Tower Library
Drag in Tower prefabs that will be available to the player in this stage
Setting Up Waves
For enemies to spawn, we’ll need to set up waves for them to appear in.
Create an empty GameObject and name it Wave Manager
Add a WaveManager component to to the Wave Manager object
Set the Size field to the number of waves we plan on including.
In order to create waves:
Create an empty GameObject and name it Wave1 (replace the number as necessary)
Add a Wave component to Wave1
Set the Size field under Spawn Instructions to the number of Agents that should appear in the wave
For each Agent:
Drag the AgentConfiguration ScriptableObject for the type of enemy into the Agent Configuration field
Set the Delay To Spawn field to how many seconds should pass after spawning the previous agent before spawning the current one
Drag the node that the Agent should spawn at into the Starting Node field
The regular Wave component will only complete the wave once all Agents are destroyed. We can also use a TimedWave component, which provides a Time To Next Wave field, where we can set a fixed time before Agents from the next wave start spawning.
After the waves have been created, add them to elements under Waves in the WaveManager.
Adding a Game Manager
A stage requires a GameObject with a GameManager component in order to end the stage when the player has destroyed all enemies or had their base health depleted.
Drag the default Starter Kit GameManager prefab into the Hierarchy view from Prefabs/Managers.
Drag the LevelList ScriptableObject into the Level List field
Understanding the GameManager component
This contains a reference to the audio mixer that the game uses.
Master Volume Parameter
The name of the parameter in GameMixer that controls the overall volume of the game.
Sfx Volume Parameter
The name of the parameter in GameMixer that controls the volume of the sound effects in the game.
Music Volume Parameter
The name of the parameter in GameMixer that controls the volume of the music in the game.
Here we can set a reference to the list of stages that are to be included in our game.
Adding a Level Manager
The LevelManager manages the state of the game, and is where we can set up some other important features of our stage, such as how much currency the player is given at the start. To add one:
Add a LevelManager component to the Wave Manager GameObject made earlier
Set the Starting Currency field
Drag the TowerLibrary ScriptableObject to the Tower Library field
Understanding the LevelManager component
The LevelManager can optionally enter a special state before the game starts. For example, this could be used to display an establishing cutscene for the stage.
The Starter Kit includes TimedLevelIntro, a very simple timed implementation of the intro that waits for a period of time before beginning the game.
To implement custom behaviour, create a new script that extends from Intro and perform our logic in Start. For example, we might launch a cutscene created using Timeline. Once our intro is complete, we can call SafelyCallIntroCompleted to instruct the LevelManager to continue.
Here we can assign the TowerLibrary scriptable object we made earlier to the stage. Doing this will set the available towers for the stage.
The Starting Currency field defines the amount of in-game currency the player starts with to spend on towers.
We may want the player to be able to earn currency over time while playing the game. The fields in the Currency Gainer are where we can set this up.
Constant Currency Gain Addition
Constant Currency Gain Addition is how much currency will be added to the player’s total each time an addition is made.
Constant Currency Gain Rate
Constant Currency Gain Rate is how many times per second the currency addition occurs.
This field defines the player home base. Enemies will reduce the player’s health if they reach this point. This needs to be set to a reference to the final navigation node, which has a PlayerHomeBase component on it. More on this below.
Any colliders set here will be ignored by ballistic projectiles briefly. This can be useful to ensure that they do not collide with small parts of the environment between a Tower and an Agent and explode without hitting an enemy.
Constantly creating and destroying instances of enemies, projectiles, and passive effects is an expensive operation. Instead of doing this, we can use the Starter Kit’s pool manager. The pool manager is a class that will create and store duplicates of objects so that they can be reused.
Add a Poolable component to the Projectiles, Particle Systems, and Agents
Create an empty GameObject and name it Pool Manager
Add a PoolManager component to the Pool Manager GameObject
Drag Prefabs for Projectiles, Particle Systems, and Enemies into the Poolables fields if they will appear in the stage
Ensure that the Transform scale of the Pool Manager is set to (1, 1, 1)
Note that spawned enemies are made children of the GameObject with the PoolManager, which means they will inherit its transform values. It is likely that we will want the scale to remain (1, 1, 1), but this can be used should we wish to increase the scale of all enemies.