Waves, Gold, and Game Over
The last pieces turn "towers that shoot" into an actual game: enemies arrive in waves, killing them earns gold to afford more towers, and letting too many through ends the game.
Spawning a wave over time
struct WaveState {
int enemiesRemaining = 10;
float spawnTimer = 0.f;
float spawnInterval = 0.8f; // seconds between spawns
};
void updateWaveSpawning(WaveState& wave, std::vector<Enemy>& enemies, Vector2 startPixel, float deltaTime) {
if (wave.enemiesRemaining <= 0) return;
wave.spawnTimer -= deltaTime;
if (wave.spawnTimer <= 0.f) {
Enemy enemy;
enemy.position = startPixel;
enemies.push_back(enemy);
wave.enemiesRemaining--;
wave.spawnTimer = wave.spawnInterval;
}
}
Spawning every enemy on frame one would make them all overlap and march in a single clump, staggering them with spawnInterval (the same countdown-timer pattern from the tower's cooldown) spreads them into a readable line instead.
Gold and lives
int gold = 100;
int lives = 20;
const int TOWER_COST = 25;
const int KILL_REWARD = 5;
// in updateTower, right after an enemy's health drops to 0 or below:
if (enemy.health <= 0.f) {
gold += KILL_REWARD;
}
// in the main loop, checking every enemy each frame:
if (enemy.reachedGoal) {
lives--;
}
tryPlaceTower from the last lesson should also check gold >= TOWER_COST before allowing a placement, and subtract TOWER_COST on success, a player with 0 gold shouldn't be able to place a free tower just because a cell happens to be empty and a path would still exist.
Removing enemies that die or finish
enemies.erase(
std::remove_if(enemies.begin(), enemies.end(),
[](const Enemy& e) { return e.health <= 0.f || e.reachedGoal; }),
enemies.end()
);
The erase-remove idiom: std::remove_if doesn't actually shrink the vector, it shuffles the elements that should be kept to the front and returns an iterator marking where the "removed" ones now start, .erase(...) is what actually shrinks the vector down to just the kept elements. Doing this once per frame, after health and reachedGoal have been updated, keeps enemies from growing forever.
Game over
bool gameOver = lives <= 0;
bool waveCleared = wave.enemiesRemaining <= 0 && enemies.empty();
waveCleared needs both conditions, no more enemies left to spawn, and none currently alive on the board, checking enemiesRemaining alone would declare victory while enemies are still mid-path.
TIP
Once gameOver is true, stop calling updateEnemy/updateTower/updateWaveSpawning (freeze the board, same idea as the horse race course's finish-line freeze), but keep drawing and keep the window's own event loop running, so the final state and a "GAME OVER" message stay visible.