Make It Yours
Lesson 18 of 18 in Coddy's Unity Game: Tower Defense course.
The game needs its ending. It is lost when the lives reach 0, and won when every wave has been sent and no enemy is left on the board. The spawner knows when the last wave is out, and Enemy.All knows how many enemies are left, so the spawner waits for an empty board and then raises an event that the GameManager listens to. A coroutine waits for a condition with WaitUntil, which checks a lambda once per frame. A puzzle room that opens its exit once every switch is on:
yield return new WaitUntil(() => switchesOn == switches.Length);
exit.SetActive(true);Either ending shows the EndPanel with a message and stops the game with Time.timeScale = 0. Lives never go below 0: a Tank that takes 2 when 1 is left would skip right past a check for exactly 0, so Mathf.Max keeps them at 0 or more.
Play Again loads the scene again. Time.timeScale is not part of the scene and keeps its value through a load, so a game stopped at 0 would start again frozen: set it back to 1 before loading.
Then make the game yours. Everything that balances it is in the Inspector:
- Game's GameManager:
startMoney(150) andstartLives(20). - The Spawner's
waves, eight rows of enemy, count and spacing, andtimeBetweenWaves(8). - The EnemyData assets: speed, health, reward and the lives each kind takes. The Boss has 450 health and takes 10.
- The TowerData assets: cost, damage, range, fire rate, the upgrade numbers, the Cannon's splash and the Frost tower's slow.
The default numbers can be beaten with a mixed defense in the bends of the path, upgraded as the money comes in; a few towers of one kind will not hold. Change one number at a time, and play to feel what it does.
Challenge
Hard- In
WaveSpawner, addpublic event Action AllWavesCleared. After the last wave is sent, wait untilEnemy.Allis empty, then raise it. - In
GameManager, add the serialized fieldsendPanel(a GameObject) andendText(a TMP_Text). Lives never go below 0, and when they reach 0 the game ends withGame Over. When all waves are cleared with lives left, it ends withYou Win!. Ending shows the panel with the message and setsTime.timeScaleto 0. - Write
public void PlayAgain(), which the panel's button calls: time back to normal, and the active scene loaded again.
Press Play and defend the base through all eight waves, then Check. After that, tune the game in the Inspector and play it your way.
Try it yourself
using UnityEngine;
using TMPro;
public class GameManager : MonoBehaviour
{
[SerializeField] private int startMoney = 100;
[SerializeField] private int startLives = 20;
[SerializeField] private WaveSpawner spawner;
[SerializeField] private TMP_Text moneyText;
[SerializeField] private TMP_Text livesText;
[SerializeField] private TMP_Text waveText;
private int money;
private int lives;
void OnEnable()
{
Enemy.ReachedBase += OnReachedBase;
Enemy.Killed += OnKilled;
spawner.WaveStarted += ShowWave;
}
void OnDisable()
{
Enemy.ReachedBase -= OnReachedBase;
Enemy.Killed -= OnKilled;
spawner.WaveStarted -= ShowWave;
}
void Start()
{
money = startMoney;
lives = startLives;
ShowMoney();
ShowLives();
}
public bool TrySpend(int amount)
{
if (amount > money)
{
return false;
}
money -= amount;
ShowMoney();
return true;
}
public void Earn(int amount)
{
money += amount;
ShowMoney();
}
void OnKilled(Enemy enemy)
{
Earn(enemy.data.reward);
}
void OnReachedBase(Enemy enemy)
{
lives -= enemy.data.damage;
ShowLives();
}
void ShowMoney()
{
moneyText.text = "Money: " + money;
}
void ShowLives()
{
livesText.text = "Lives: " + lives;
}
void ShowWave(int wave, int total)
{
waveText.text = "Wave " + wave + "/" + total;
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.