Setting Up a Stage in Tower Defense Template

Vérifié avec version: 2017.2

-

Difficulté: Intermédiaire

Introduction

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.

Prototype Level

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:

Mesh Extension

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.

Collision Mesh

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.

Level List

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:

ID

The order in which the stage will appear in the menu.

Name

A title for the stage.

Description

A brief string to give players an idea of what to expect from the stage.

Scene Name

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.

NavMesh Setup

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

Navigation Nodes

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

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

TowerPlacementGrid

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

Configuration

Max Health

The maximum health of the player’s home base.

Starting Health

The amount of health the home base will start with.

Alignment

Refers to the ScriptableObject that defines which objects can target and damage the home base.

Charge Pfx

The Particle System will play while an Agent is charging its attack on the home base.

Charge Sound

The sound will play while an Agent is charging its attack on the home base.

Attack Pfx

The Particle System will play while an Agent is done charging and attacks the home base.

Attack Sound

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

Game Mixer

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.

Level List

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

Intro

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.

Tower Library

Here we can assign the TowerLibrary scriptable object we made earlier to the stage. Doing this will set the available towers for the stage.

Starting Currency

The Starting Currency field defines the amount of in-game currency the player starts with to spend on towers.

Currency Gainer

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.

Home Bases

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.

Environment Colliders

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.

Pool Manager

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.