โš™๏ธ My First C++ Game ยท Episode 6 of 7 ยท See All Episodes
๐Ÿฐ Episode 6 ยท Big Project ยท Tower Defence Part 1 of 2

Hold the Line!
Tower Defence Part 1

The two-part finale: a path-following enemy system, grid-snapped tower placement, gold economy, and range visualisation. Tower defence is systems programming, exactly what C++ is for.

๐Ÿ‘ถ Ages 12+ โฑ๏ธ ~2 Hours โš™๏ธ C++ & SFML โœ“ Free
๐Ÿ›ค๏ธ Waypoint paths ๐Ÿงฎ Vector maths ๐Ÿ—๏ธ Tower placement ๐Ÿ’ฐ Gold economy ๐Ÿ“ Range circles ๐Ÿ–ฑ๏ธ Mouse building
โญ
0 XP
Level 1
๐Ÿ”ฅ0
Your Progress 0 / 6 steps
1
๐Ÿ›ค๏ธ
The Path: Waypoints
Enemies follow a list of corner points
Active
๐ŸŽฏ
Goal for this step

Define the enemy path as waypoints and draw the road.

  • 1New project tower.cpp, 800ร—600. The path is a std::vector<sf::Vector2f> of corner points, left edge to right edge.
  • 2Draw the road: thick line segments between consecutive waypoints (a rotated RectangleShape per segment works fine).
  • 3Sketch your path with 5-7 corners, S-bends give towers more shooting time than straight lines. That is map design!
  • 4Draw grass green, road sandy.
tower.cpp
std::vector<sf::Vector2f> path = {
    {-20, 100}, {300, 100}, {300, 300},
    {120, 300}, {120, 480}, {600, 480},
    {600, 220}, {820, 220}
};

// road drawing (segment between path[i] and path[i+1]):
for (size_t i = 0; i + 1 < path.size(); i++) {
    sf::Vector2f a = path[i], b = path[i+1];
    sf::Vector2f d = b - a;
    float len = std::sqrt(d.x*d.x + d.y*d.y);
    sf::RectangleShape seg(sf::Vector2f(len, 36));
    seg.setOrigin(0, 18);
    seg.setPosition(a);
    seg.setRotation(std::atan2(d.y, d.x) * 57.2958f);
    seg.setFillColor(sf::Color(194, 178, 128));
    window.draw(seg);
}
โœ๏ธ
Fill in the Blanks
+15 XP
The path is a vector of (corner positions). The segment angle comes from std::(d.y, d.x), converted from radians to degrees.
๐Ÿง 
Knowledge Check
+15 XP
Why do S-bend paths make better tower defence maps than straight ones?
AThey render faster
BEnemies spend longer inside tower range near bends, placement choices become interesting
CWaypoints must curve
2
๐Ÿงฎ
Path-Following Enemies
Normalised direction vectors, real vector maths
Locked
๐ŸŽฏ
Goal for this step

Make enemies walk the path corner to corner at constant speed.

  • 1Enemy struct: position, speed, hp, and int wp, which waypoint it is heading for.
  • 2Each frame: direction = target โˆ’ position; normalise it (divide by its length); move by direction ร— speed ร— dt.
  • 3Within 4 px of the target โ†’ wp++. Past the last waypoint โ†’ it escaped (livesโˆ’1 later).
  • 4Normalising is THE fundamental game maths operation, you will use it in every 2D and 3D game forever.
tower.cpp
struct Enemy {
    sf::Vector2f pos;
    float speed = 70;
    int hp = 20;
    int wp = 0;         // next waypoint index
    bool alive = true;
};

void updateEnemy(Enemy& e, float dt) {
    if (e.wp >= (int)path.size()) return;    // escaped
    sf::Vector2f d = path[e.wp] - e.pos;
    float len = std::sqrt(d.x*d.x + d.y*d.y);
    if (len < 4.f) { e.wp++; return; }
    e.pos += (d / len) * e.speed * dt;       // normalised!
}
โŒจ๏ธ
Code Challenge
+20 XP
Move the enemy along its normalised direction:
tower.cpp
sf::Vector2f d = path[e.wp] - e.pos;
float len = std::(d.x*d.x + d.y*d.y);
e.pos += (d / ) * e.speed * ;
๐Ÿ’ก Hint: Pythagoras gives the length; dividing the vector by it makes a unit direction; then scale by speed and delta time.
๐Ÿง 
Knowledge Check
+15 XP
Why normalise the direction before scaling by speed?
AC++ requires unit vectors
BWithout it, enemies far from a waypoint would move faster, normalising makes speed constant regardless of distance
CIt prevents overflow
3
๐Ÿ–ฑ๏ธ
Grid Placement Preview
Snap the mouse to a build grid with a ghost tower
Locked
๐ŸŽฏ
Goal for this step

Show a snapped, colour-coded ghost tower under the cursor.

  • 1The build grid is 40 px: gx = mouseX / 40 * 40 (integer division snaps!).
  • 2Draw a translucent ghost tower at the snapped cell: green tint = buildable, red = blocked.
  • 3Blocked cells: on the road (distance from any path segment < 30, a point-to-segment check) or already occupied.
  • 4This preview-before-commit UI is in every strategy game ever made.
tower.cpp
sf::Vector2i m = sf::Mouse::getPosition(window);
int gx = (m.x / 40) * 40;
int gy = (m.y / 40) * 40;

bool blocked = nearPath((float)gx + 20, (float)gy + 20, 30.f)
            || occupied(gx, gy);

sf::RectangleShape ghost(sf::Vector2f(36, 36));
ghost.setPosition((float)gx + 2, (float)gy + 2);
ghost.setFillColor(blocked
    ? sf::Color(248, 113, 113, 120)
    : sf::Color(6, 214, 160, 120));
window.draw(ghost);
โœ๏ธ
Fill in the Blanks
+15 XP
Integer division then multiplication ((m.x / 40) * ) snaps a coordinate to the grid. The ghostโ€™s fourth colour value (120) is (alpha).
๐Ÿง 
Knowledge Check
+15 XP
With m.x = 137, what is (137 / 40) * 40 in C++?
A137
B120, integer division truncates 137/40 to 3, times 40
C140
4
๐Ÿ—๏ธ
Towers & the Gold Economy
Click to build, if you can afford it
Locked
๐ŸŽฏ
Goal for this step

Place towers on click, spend gold, block double-building.

  • 1Tower struct: position, range (110), damage (5), fire cooldown, stored in a vector.
  • 2Start with 100 gold; a tower costs 50. Click on a valid cell with enough gold โ†’ build and subtract.
  • 3occupied() loops the towers comparing snapped positions.
  • 4Show gold top-left. Try to build on the road or overdraft, both must refuse.
tower.cpp
struct Tower {
    sf::Vector2f pos;      // cell centre
    float range = 110;
    int damage = 5;
    float cooldown = 0;
};

std::vector<Tower> towers;
int gold = 100;
const int TOWER_COST = 50;

// on MouseButtonPressed (left):
if (!blocked && gold >= TOWER_COST) {
    towers.push_back({ {(float)gx + 20, (float)gy + 20} });
    gold -= TOWER_COST;
}
โŒจ๏ธ
Code Challenge
+20 XP
Guard the build action properly:
tower.cpp
if (! && gold >= ) {
    towers.push_back({ {(float)gx + 20, (float)gy + 20} });
    gold -= ;
}
๐Ÿ’ก Hint: The cell must be free AND affordable; then pay the price.
๐Ÿง 
Knowledge Check
+15 XP
Why is the gold check part of the SAME if as the placement check?
ATo save a line
BBuild and payment must happen together or never, split checks can build without paying (a dupe bug!)
CC++ ifs are faster combined
5
๐Ÿ“
Range Circles & Targeting Maths
Show range on hover; find enemies inside it
Locked
๐ŸŽฏ
Goal for this step

Visualise tower range and detect enemies within it.

  • 1Hovering a tower draws its range: a translucent sf::CircleShape of radius range, centred on the tower (setOrigin(range, range)!).
  • 2In-range test: squared distance, dxยฒ + dyยฒ <= rangeยฒ, skipping the sqrt. The classic optimisation.
  • 3Write Enemy* nearestInRange(Tower&) returning the closest living target or nullptr, Part 2โ€™s shooting uses exactly this.
  • 4Test: spawn one enemy (press E) and watch a hovered towerโ€™s circle as it walks through.
tower.cpp
Enemy* nearestInRange(Tower& t, std::vector<Enemy>& enemies) {
    Enemy* best = nullptr;
    float bestDist = t.range * t.range;
    for (auto& e : enemies) {
        if (!e.alive) continue;
        float dx = e.pos.x - t.pos.x;
        float dy = e.pos.y - t.pos.y;
        float d2 = dx*dx + dy*dy;
        if (d2 <= bestDist) {
            bestDist = d2;
            best = &e;
        }
    }
    return best;
}
๐Ÿ’ก
Comparing squared distances avoids thousands of sqrt calls per frame. Every real engine does this.
โœ๏ธ
Fill in the Blanks
+15 XP
We compare squared distances against range to skip the expensive call. When no enemy is in range the function returns .
๐Ÿง 
Knowledge Check
+15 XP
Returning a pointer (Enemy*) that may be nullptr forces the caller toโ€ฆ
ACrash
BCheck "did I actually get a target?" before using it, nullptr IS the no-target answer
CCopy the enemy
6
๐Ÿ’ฐ
Economy Balance & the Bridge
Tune the numbers, prep the Part 2 hooks
Locked
๐ŸŽฏ
Goal for this step

Balance the opening economy and stage the code for Part 2.

  • 1Balance rules of thumb: starting gold buys 2 towers; a kill (Part 2) refunds ~20-40% of a tower; escapes must hurt.
  • 2Set kill bounty 15 in the Enemy struct now, Part 2 pays it out.
  • 3Add lives = 20, shown in the HUD; an escaped enemy will cost 1.
  • 4Checklist before Part 2: path following โœ“ ghost placement โœ“ gold โœ“ range targeting โœ“. All green? You are ready for war.
tower.cpp
struct Enemy {
    // ... existing fields ...
    int bounty = 15;      // paid on kill (Part 2)
};

int lives = 20;

// escape handling (enemy past last waypoint):
if (e.wp >= (int)path.size() && e.alive) {
    e.alive = false;
    lives--;
}
โŒจ๏ธ
Code Challenge
+20 XP
Charge the player for an escape:
tower.cpp
if (e.wp >= (int)path.() && e.alive) {
    e.alive = ;
    lives;
}
๐Ÿ’ก Hint: Escaped means past the final waypoint; the enemy leaves play and one life is deducted.
๐Ÿง 
Knowledge Check
+15 XP
Why balance the economy BEFORE adding shooting?
AShooting is hard
BEconomy is the skeleton, if building feels wrong now, waves of enemies will only hide the problem
CGold compiles first
๐ŸŽ‰๐Ÿ†๐ŸŽฎโœจ๐ŸŽ‰
Workshop Complete!
Waypoints, vector maths, an economy and buildable towers, the foundation is rock solid. Part 2 arms the towers: bullets, waves and victory!
0
Total XP
1
Level
0
Best Streak
0%
Accuracy
โ–ถ Episode 7: Tower Defence Part 2 โ†’
โญ View My Progress & Certificates

This workshop was free and took many hours to build. If it helped you learn something new, consider supporting the project.

☕ Support on Ko-fi