โš™๏ธ My First C++ Game ยท Episode 4 of 7 ยท See All Episodes
๐Ÿƒ Episode 4 ยท Improver ยท Physics & Structure

Gravity Works!
C++ Platformer

Gravity, jumping, platforms and coins, with a proper Player class, delta-time physics, and clean separation of update and draw. Your platformer skills, upgraded to C++.

๐Ÿ‘ถ Ages 12+ โฑ๏ธ ~2 Hours โš™๏ธ C++ & SFML โœ“ Free
๐Ÿ—๏ธ Player class ๐ŸŒ dt gravity ๐Ÿฆ˜ Jump buffering ๐Ÿงฑ AABB landing โญ Coins ๐Ÿงน Clean structure
โญ
0 XP
Level 1
๐Ÿ”ฅ0
Your Progress 0 / 6 steps
1
๐Ÿ—๏ธ
The Player Class
Position, velocity and shape in one tidy object
Active
๐ŸŽฏ
Goal for this step

Structure the player as a class with its own update method.

  • 1New platformer.cpp, 800ร—480. The Player class owns: shape, vx, vy, onGround.
  • 2Its update(float dt) method will run all physics; main() just calls it, clean separation.
  • 3Gravity in dt units: vy += 1400 * dt (px/secยฒ), position += velocity ร— dt.
  • 4Construct at (100, 380), draw orange.
platformer.cpp
class Player {
public:
    sf::RectangleShape shape;
    float vx = 0, vy = 0;
    bool onGround = false;

    Player() {
        shape.setSize(sf::Vector2f(34, 44));
        shape.setPosition(100, 380);
        shape.setFillColor(sf::Color(255, 119, 0));
    }

    void update(float dt) {
        vy += 1400.f * dt;              // gravity
        shape.move(vx * dt, vy * dt);
    }
};
โœ๏ธ
Fill in the Blanks
+15 XP
Gravity is now an acceleration of 1400 px/ยฒ applied via dt. The Player class keeps physics inside its own method.
๐Ÿง 
Knowledge Check
+15 XP
main() calling player.update(dt) instead of doing physics itself gives youโ€ฆ
AFaster compilation
BA main loop you can read at a glance, and physics you can change in ONE place
CSmaller executables
2
โŒจ๏ธ
Running & Air Control
Target-speed movement that feels crisp
Locked
๐ŸŽฏ
Goal for this step

Move with the arrows using target velocities, including mid-air.

  • 1Each frame set a target vx: โˆ’260, +260 or 0 from the held arrows.
  • 2Instant velocity (vx = target) feels crisp; optionally blend toward it for weight: vx += (target - vx) * 12 * dt.
  • 3Keep air control at 100% for now, floaty platformers reduce it.
  • 4Clamp x to the window.
platformer.cpp
// inside Player::update, before gravity:
float target = 0;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))  target = -260;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) target =  260;
vx += (target - vx) * 12.f * dt;   // smooth toward target
โŒจ๏ธ
Code Challenge
+20 XP
Smooth the run speed toward its target:
platformer.cppp
vx += (target - ) * 12.f * ;
๐Ÿ’ก Hint: Move a fraction of the gap between current and target each frame, scaled by delta time.
๐Ÿง 
Knowledge Check
+15 XP
The line vx += (target - vx) * 12 * dt createsโ€ฆ
AInstant stops
BSmooth acceleration and deceleration, the gap shrinks a proportion every frame, like easing
CRandom drift
3
๐Ÿฆ˜
Jumping with Coyote Time
The secret trick every good platformer hides
Locked
๐ŸŽฏ
Goal for this step

Add jumping plus the "coyote time" forgiveness window.

  • 1Jump on Space (KeyPressed event): vy = -560 if allowed.
  • 2Coyote time: keep a timer of how long since you were last grounded; allow jumps within 0.1 s of leaving a ledge.
  • 3Players constantly press jump a few pixels AFTER running off a ledge, without forgiveness it "eats" their input and feels unfair.
  • 4Track it: sinceGround += dt each frame, reset to 0 when landing; jump while < 0.1.
platformer.cpp
float sinceGround = 999;

// in update:
sinceGround += dt;
if (onGround) sinceGround = 0;

// jump (in the event handler):
if (event.key.code == sf::Keyboard::Space
        && player.sinceGround < 0.1f) {
    player.vy = -560;
    player.sinceGround = 999;   // spend the jump
}
๐Ÿ’ก
Celeste, Hollow Knight, Mario, all use coyote time. Roughly 0.08-0.12 s. Players never notice it; they only notice its absence.
โœ๏ธ
Fill in the Blanks
+15 XP
Coyote time allows a jump within seconds of leaving the ground. After jumping we set the timer to 999 so the jump cannot be twice.
๐Ÿง 
Knowledge Check
+15 XP
Why do players love coyote time without knowing it exists?
AIt makes jumps higher
BIt forgives tiny timing errors, so the game matches what players INTENDED rather than what they exactly did
CIt speeds the game up
4
๐Ÿงฑ
Platforms & Landing
AABB landing checks against a vector of platforms
Locked
๐ŸŽฏ
Goal for this step

Land on platforms with the falling-only rule, in clean C++.

  • 1Platforms: std::vector<sf::FloatRect>, ground plus ledges.
  • 2After moving: for each platform, if bounds intersect AND vy > 0 AND we came from above โ†’ snap on top, vy = 0, onGround = true.
  • 3"Came from above": previous bottom (bottom โˆ’ vyยทdt) was at or above the platform top.
  • 4Draw them from the same rects with one reusable RectangleShape.
platformer.cpp
std::vector<sf::FloatRect> platforms = {
    {0, 440, 800, 40}, {150, 340, 140, 16},
    {380, 260, 140, 16}, {610, 180, 140, 16},
};

onGround = false;
sf::FloatRect pb = shape.getGlobalBounds();
for (auto& plat : platforms) {
    if (pb.intersects(plat) && vy > 0
            && pb.top + pb.height - vy * dt <= plat.top + 1) {
        shape.setPosition(pb.left, plat.top - pb.height);
        vy = 0;
        onGround = true;
    }
}
โŒจ๏ธ
Code Challenge
+20 XP
Snap onto the platform you landed on:
platformer.cpp
shape.setPosition(pb.left, plat.top - pb.);
vy = ;
onGround = ;
๐Ÿ’ก Hint: Standing on top means your bottom equals the platform top; kill the fall; you are grounded.
๐Ÿง 
Knowledge Check
+15 XP
This landing check is your third language writing it. The condition that never changed isโ€ฆ
AThe platform colour
BOnly land while FALLING (vy > 0), the rule that lets you jump up through ledges
CThe vector type
5
โญ
Coins & the HUD
Collectables with the erase-remove idiom for real
Locked
๐ŸŽฏ
Goal for this step

Collect coins using the idiom you met in Episode 3, this time for real.

  • 1Coins: std::vector<sf::FloatRect> above the ledges.
  • 2This time USE erase-remove: erase all coins intersecting the player, counting them first with count_if or a manual loop.
  • 3Score += 10 per coin; draw the rest as gold squares; score text top-left (Episode 1 font skill).
  • 4Feel how natural the idiom is on a shrinking pickup list, right tool, right job.
platformer.cpp
std::vector<sf::FloatRect> coins = {
    {205, 300, 18, 18}, {435, 220, 18, 18}, {665, 140, 18, 18} };

sf::FloatRect pb = player.shape.getGlobalBounds();
int before = coins.size();
coins.erase(
    std::remove_if(coins.begin(), coins.end(),
        [&](const sf::FloatRect& c){ return pb.intersects(c); }),
    coins.end());
score += (before - coins.size()) * 10;
โœ๏ธ
Fill in the Blanks
+15 XP
remove_if shuffles matching items to the end and chops them off. The lambda captures pb with so it can see the player bounds.
๐Ÿง 
Knowledge Check
+15 XP
The [&] at the start of the lambda meansโ€ฆ
AThe lambda is broken
BCapture outside variables by reference, the lambda can read pb from the surrounding scope
CPass by value only
6
๐Ÿ†
Goal, Death & Structure Review
Win flag, pit respawn, and a look at your architecture
Locked
๐ŸŽฏ
Goal for this step

Complete the level and review the clean structure you built.

  • 1Gold flag on the top ledge: touch with all coins gone โ†’ WIN state, banner, R restarts.
  • 2Fall past y 480 โ†’ respawn at start (reset position and vy).
  • 3Now look at your main(): events โ†’ player.update(dt) โ†’ collisions โ†’ draw. Four readable phases. THAT structure is why classes matter.
  • 4Save this file! Episode 5 adds enemies, camera and levels directly on top.
platformer.cpp
// main loop shape, this is the goal:
while (window.isOpen()) {
    handleEvents(window, player);
    float dt = clock.restart().asSeconds();

    if (state == PLAY) {
        player.update(dt);
        resolveCollisions(player, platforms);
        collectCoins(player, coins, score);
        checkGoal(player, flag, coins, state);
    }

    draw(window, player, platforms, coins, scoreText);
}
โŒจ๏ธ
Code Challenge
+20 XP
The win needs the flag AND an empty coin vector:
platformer.cpp
if (pb.intersects(flag) && coins.())
    state = ;
๐Ÿ’ก Hint: Vectors report emptiness with a method; then switch to the winning enum state.
๐Ÿง 
Knowledge Check
+15 XP
Your main loop reads as four named phases. The biggest benefit isโ€ฆ
AIt compiles faster
BAny bug has an obvious home, physics bug? Look in update. Draw glitch? Look in draw
CIt uses less RAM
๐ŸŽ‰๐Ÿ†๐ŸŽฎโœจ๐ŸŽ‰
Workshop Complete!
A structured, class-based platformer with real dt physics. Episode 5 turns it into a full game with enemies and a camera!
0
Total XP
1
Level
0
Best Streak
0%
Accuracy
โ–ถ Episode 5: Platformer 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