โš™๏ธ My First C++ Game ยท Episode 5 of 7 ยท See All Episodes
๐ŸŒฒ Episode 5 ยท Improver+ ยท Upgrades Episode 4

Full Game!
Platformer Part 2

Open your Episode 4 project, we are shipping a full game: an Enemy class with stomps, an sf::View scrolling camera, spikes, checkpoints and multi-level structure. C++ platforming, complete.

๐Ÿ‘ถ Ages 12+ โฑ๏ธ ~2 Hours โš™๏ธ C++ & SFML โœ“ Free
๐Ÿ‘พ Enemy class ๐Ÿฅพ Stomp kills ๐ŸŽฅ sf::View camera โš ๏ธ Spikes ๐Ÿšฉ Checkpoints ๐Ÿ—บ๏ธ Level structs
โญ
0 XP
Level 1
๐Ÿ”ฅ0
Your Progress 0 / 6 steps
1
๐Ÿ‘พ
The Enemy Class
Patrolling walkers built like the Player
Active
๐ŸŽฏ
Goal for this step

Add an Enemy class with patrol movement, stored in a vector.

  • 1Enemy mirrors Player: shape, vx, plus minX/maxX patrol bounds and an alive flag.
  • 2Its update: move by vxยทdt; flip vx at the bounds.
  • 3A constructor taking (x, y, minX, maxX) makes placing them one-liners.
  • 4Two enemies to start: one on the ground, one patrolling a ledge. Draw red.
platformer.cpp
class Enemy {
public:
    sf::RectangleShape shape;
    float vx = 120;
    float minX, maxX;
    bool alive = true;

    Enemy(float x, float y, float lo, float hi)
            : minX(lo), maxX(hi) {
        shape.setSize(sf::Vector2f(30, 24));
        shape.setPosition(x, y);
        shape.setFillColor(sf::Color(248, 113, 113));
    }

    void update(float dt) {
        shape.move(vx * dt, 0);
        float x = shape.getPosition().x;
        if (x < minX || x > maxX) vx = -vx;
    }
};

std::vector<Enemy> enemies;
enemies.emplace_back(390, 236, 380, 500);
enemies.emplace_back(60, 416, 40, 700);
โœ๏ธ
Fill in the Blanks
+15 XP
The constructor line ": minX(lo), maxX(hi)" is an initialiser . Enemies are stored with emplace_back just like were in Episode 3.
๐Ÿง 
Knowledge Check
+15 XP
Enemy and Player share the shape+velocity+update pattern. In bigger games this becomesโ€ฆ
ACopy-paste forever
BA shared base class (inheritance), Entity with Player and Enemy deriving from it
CA single giant class
2
๐Ÿฅพ
Stomp or Suffer
Fall on heads to kill; touch sides to die
Locked
๐ŸŽฏ
Goal for this step

Implement the stomp with the falling-from-above test.

  • 1For each living enemy intersecting the player: falling AND bottom near their top โ†’ stomp (alive = false, bounce vy = -350, +score).
  • 2Otherwise โ†’ death: respawn (checkpoints next step).
  • 3Skip dead enemies everywhere (alive flag, Episode 3 pattern).
  • 4The bounce chains stomps, line your two enemies up and try a double!
platformer.cpp
for (auto& en : enemies) {
    if (!en.alive) continue;
    if (pb.intersects(en.shape.getGlobalBounds())) {
        float enTop = en.shape.getPosition().y;
        if (player.vy > 0
                && pb.top + pb.height - player.vy * dt <= enTop + 10) {
            en.alive = false;
            player.vy = -350;      // bounce!
            score += 50;
        } else {
            respawn(player);
        }
    }
}
โŒจ๏ธ
Code Challenge
+20 XP
Squash the enemy and rebound:
platformer.cpp
en.alive = ;
player.vy = ;
score += 50;
๐Ÿ’ก Hint: The enemy stops existing; the player bounces upward with a negative vertical velocity.
๐Ÿง 
Knowledge Check
+15 XP
The stomp test reuses which platformer check?
AThe coin pickup
BLanding-from-above, the same "falling onto a top surface" logic aimed at an enemy
CThe wall clamp
3
๐ŸŽฅ
The sf::View Camera
SFMLโ€™s built-in camera, three lines of scrolling
Locked
๐ŸŽฏ
Goal for this step

Follow the player through a 2400 px level using sf::View.

  • 1SFML has a real camera class: sf::View. Centre it on the player, clamped to the level, then window.setView(view).
  • 2THAT IS IT, no offsetting of draw calls (remember doing that by hand in Python? Engines do this for you).
  • 3Clamp centre x between 400 and levelWidth โˆ’ 400 so the view never shows past the edges.
  • 4For screen-fixed HUD: switch back with window.setView(window.getDefaultView()) before drawing text.
platformer.cpp
const float LEVEL_W = 2400.f;
sf::View view(sf::FloatRect(0, 0, 800, 480));

// each frame:
float cx = player.shape.getPosition().x + 17;
cx = std::max(400.f, std::min(LEVEL_W - 400.f, cx));
view.setCenter(cx, 240);
window.setView(view);

// ... draw world ...
window.setView(window.getDefaultView());
window.draw(scoreText);          // HUD stays put
โœ๏ธ
Fill in the Blanks
+15 XP
The camera is an sf:: centred on the player. Before drawing the HUD we restore the view so text stays fixed on screen.
๐Ÿง 
Knowledge Check
+15 XP
In Python you subtracted cam_x from every draw call. sf::View replaces that byโ€ฆ
ADrawing twice
BTransforming ALL drawing through the view automatically, the engine does the offsetting
CMoving the window
4
โš ๏ธ
Spikes & Checkpoints
Hazards, flags and one shared respawn function
Locked
๐ŸŽฏ
Goal for this step

Add deadly spikes and fair checkpoint respawns.

  • 1Spikes: vector of FloatRects on the floor; intersecting one calls respawn().
  • 2Checkpoints: flag rects that update a respawnPoint Vector2f when touched.
  • 3One respawn(player) function used by spikes, enemies and pits, single source of truth.
  • 4Place checkpoints before hard sections. Draw spikes as red triangles (sf::ConvexShape), flags teal.
platformer.cpp
sf::Vector2f respawnPoint(100, 380);

void respawn(Player& p) {
    p.shape.setPosition(respawnPoint);
    p.vx = 0; p.vy = 0;
}

for (auto& cp : checkpoints)
    if (pb.intersects(cp))
        respawnPoint = { cp.left, cp.top - 44 };

for (auto& sp : spikes)
    if (pb.intersects(sp))
        respawn(player);

if (player.shape.getPosition().y > 480)   // pit
    respawn(player);
โŒจ๏ธ
Code Challenge
+20 XP
Update the respawn point at a checkpoint:
platformer.cpp
if (pb.intersects(cp))
    respawnPoint = { cp.left, cp.top -  };
๐Ÿ’ก Hint: Spawn standing ON the flag: its top minus the player height.
๐Ÿง 
Knowledge Check
+15 XP
Every death path calls one respawn() function. When you later add fall damage or a death sound, you editโ€ฆ
AEvery collision block
BExactly one function, that is why shared paths matter
CThe Enemy class
5
๐Ÿ—บ๏ธ
Levels as Structs
Bundle a whole level into one loadable object
Locked
๐ŸŽฏ
Goal for this step

Define levels as data structs and swap between them.

  • 1A struct Level holds: platforms, enemies (as spawn data), spikes, checkpoints, door, width.
  • 2A makeLevel(int n) function returns the struct for level n, pure data, no logic.
  • 3Loading = copying the structโ€™s contents into the live vectors and resetting the player.
  • 4Reaching the door loads n+1; after the last, the champion screen. Your Ep 4 game is now a multi-level product.
platformer.cpp
struct Level {
    std::vector<sf::FloatRect> platforms, spikes, checkpoints;
    std::vector<Enemy> enemies;
    sf::FloatRect door;
    float width;
};

Level makeLevel(int n) {
    if (n == 0) {
        Level L;
        L.width = 2400;
        L.platforms = { {0,440,2400,40}, {150,340,140,16} /* ... */ };
        L.enemies.emplace_back(390, 236, 380, 500);
        L.spikes = { {760, 424, 40, 16} };
        L.checkpoints = { {1000, 380, 12, 60} };
        L.door = {2320, 380, 30, 60};
        return L;
    }
    // n == 1 ...
}
โœ๏ธ
Fill in the Blanks
+15 XP
A Level bundles every vector one level needs. The function (n) returns pure data, building levels becomes filling in lists.
๐Ÿง 
Knowledge Check
+15 XP
Separating level DATA from game LOGIC meansโ€ฆ
ALevels run faster
BNew levels need zero new logic, and eventually data can come from files or a level editor
CThe compiler optimises structs
6
๐Ÿ
Polish & Ship
Death counter, timer, and the champion screen
Locked
๐ŸŽฏ
Goal for this step

Add the finishing touches and complete your C++ platformer.

  • 1Track deaths and a speedrun timer (one sf::Clock started at level 1), show both on the champion screen.
  • 2Squash animation: dead enemies flatten (setScale(1, 0.3)) for 0.3 s before vanishing.
  • 3Balance: walk each level; every death should feel like YOUR fault. Adjust spike placement until it does.
  • 4Compile, play, hand the keyboard to someone. Watching a playtester is a developer rite of passage. ๐Ÿ†
platformer.cpp
int deaths = 0;                 // ++ inside respawn()
sf::Clock runTimer;             // started at first level load

// champion screen:
int secs = (int)runTimer.getElapsedTime().asSeconds();
championText.setString(
    "CHAMPION!\nTime: " + std::to_string(secs / 60) + "m "
    + std::to_string(secs % 60) + "s\nDeaths: "
    + std::to_string(deaths));
โŒจ๏ธ
Code Challenge
+20 XP
Format the speedrun time from total seconds:
platformer.cpp
std::to_string(secs  60) + "m "
+ std::to_string(secs  60) + "s"
๐Ÿ’ก Hint: Whole-number division gives minutes; the remainder operator gives leftover seconds.
๐Ÿง 
Knowledge Check
+15 XP
A death counter and timer on the win screen addโ€ฆ
ALag
BReplay value, beat your time, deathless runs. Numbers players can chase for free
CCompile warnings
๐ŸŽ‰๐Ÿ†๐ŸŽฎโœจ๐ŸŽ‰
Workshop Complete!
Enemy objects, a real camera, checkpoints and levels, a complete C++ platformer. Two more episodes: the Tower Defence grand finale!
0
Total XP
1
Level
0
Best Streak
0%
Accuracy
โ–ถ Episode 6: Tower Defence Part 1 โ†’
โญ 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