โš™๏ธ My First C++ Game ยท Episode 1 of 7 ยท See All Episodes
๐Ÿ“ Episode 1 ยท Beginner ยท No C++ Experience Needed

Compile & Play!
Pong in C++

Your first C++ game with the SFML library: two paddles, a bouncing ball, scores and an AI. C++ powers Unreal, most AAA studios and every console, and it starts right here with Pong.

๐Ÿ‘ถ Ages 12+ โฑ๏ธ ~2 Hours โš™๏ธ C++ & SFML โœ“ Free
๐Ÿงฐ SFML setup ๐Ÿ” Game loop ๐ŸŸช Shapes & drawing โŒจ๏ธ Real-time input โฑ๏ธ Delta time ๐Ÿค– Simple AI
โญ
0 XP
Level 1
๐Ÿ”ฅ0
Your Progress 0 / 6 steps
1
๐Ÿงฐ
Install a Compiler & SFML
Set up g++/MSVC and the SFML multimedia library
Active
๐ŸŽฏ
Goal for this step

Get a C++ toolchain and SFML installed, and compile a first window.

  • 1Install a compiler: on Windows the easiest is MSYS2 (which gives you g++), or Visual Studio Community. Mac: xcode-select --install. Linux: sudo apt install g++.
  • 2Install SFML: MSYS2 โ†’ pacman -S mingw-w64-ucrt-x86_64-sfml; Mac โ†’ brew install sfml; Linux โ†’ sudo apt install libsfml-dev.
  • 3Create pong.cpp with the code below.
  • 4Compile & run: g++ pong.cpp -o pong -lsfml-graphics -lsfml-window -lsfml-system then ./pong.
  • 5A dark 800ร—500 window should appear. If it does, your entire toolchain works, the hardest step of C++ is behind you!
pong.cpp
#include <SFML/Graphics.hpp>

int main() {
    sf::RenderWindow window(sf::VideoMode(800, 500), "C++ Pong");
    window.setFramerateLimit(60);

    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event))
            if (event.type == sf::Event::Closed)
                window.close();

        window.clear(sf::Color(15, 18, 32));
        window.display();
    }
    return 0;
}
๐Ÿ‘จโ€๐Ÿ‘ง
Parent note: Everything here is free and open-source. The compile command looks scary but never changes, save it in a build.bat / build.sh file and forget it.
โœ๏ธ
Fill in the Blanks
+15 XP
C++ must be before it runs, unlike Python. The three SFML libraries we link are graphics, window and .
๐Ÿง 
Knowledge Check
+15 XP
What does -lsfml-graphics in the compile command do?
ANames the output file
BLinks your program against the SFML graphics library so window/draw calls exist
CTurns on fast graphics mode
2
๐Ÿ“
Paddles & Real-Time Input
RectangleShapes moved with isKeyPressed
Locked
๐ŸŽฏ
Goal for this step

Draw two paddles and move them with W/S and Up/Down.

  • 1SFML gives us sf::RectangleShape, set a size, a position and a colour.
  • 2Real-time input is one call: sf::Keyboard::isKeyPressed(sf::Keyboard::W), true while held.
  • 3Move paddles with shape.move(0, ยฑspeed), then clamp using getPosition().
  • 4Draw between clear() and display(): window.draw(paddle1);
pong.cpp
sf::RectangleShape paddle1(sf::Vector2f(14, 90));
paddle1.setPosition(20, 205);
paddle1.setFillColor(sf::Color::White);

sf::RectangleShape paddle2 = paddle1;
paddle2.setPosition(766, 205);

// inside the loop, before drawing:
float speed = 6.f;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))    paddle1.move(0, -speed);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))    paddle1.move(0,  speed);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))   paddle2.move(0, -speed);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) paddle2.move(0,  speed);

window.draw(paddle1);
window.draw(paddle2);
โŒจ๏ธ
Code Challenge
+20 XP
Move paddle 1 with W and S:
pong.cpp
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
    paddle1.move(0, );
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
    paddle1.move(0, );
๐Ÿ’ก Hint: W moves up (negative y), S moves down (positive y). move() takes (dx, dy).
๐Ÿง 
Knowledge Check
+15 XP
paddle2 = paddle1; then repositioning it works becauseโ€ฆ
AThey share one shape
BC++ copies the whole object, paddle2 is an independent clone with the same size and colour
CSFML links them
3
โšช
The Ball & Delta Time
Frame-rate independent movement with sf::Clock
Locked
๐ŸŽฏ
Goal for this step

Move the ball using delta time, the professional way.

  • 1Add a sf::CircleShape ball(7) and velocity floats bvx = 300, bvy = 180, in pixels per SECOND now, not per frame.
  • 2An sf::Clock measures the time each frame took: float dt = clock.restart().asSeconds();
  • 3Move by velocity ร— dt, on a 60 FPS machine or a 144 FPS gaming rig, the ball covers the same distance per second.
  • 4Bounce off top/bottom by flipping bvy.
pong.cpp
sf::CircleShape ball(7);
ball.setPosition(393, 243);
float bvx = 300.f, bvy = 180.f;
sf::Clock clock;

// in the loop:
float dt = clock.restart().asSeconds();
ball.move(bvx * dt, bvy * dt);

if (ball.getPosition().y <= 0 || ball.getPosition().y + 14 >= 500)
    bvy = -bvy;
๐Ÿ’ก
Per-frame movement (Java/Python episodes) is fine for learning; velocity ร— dt is what real engines do. You just learned the difference.
โœ๏ธ
Fill in the Blanks
+15 XP
dt comes from clock.().asSeconds() and is the time the last frame took. Movement becomes velocity ร— , making speed independent of frame .
๐Ÿง 
Knowledge Check
+15 XP
Without delta time, a game running at 144 FPS instead of 60 wouldโ€ฆ
ALook smoother but play identically
BRun 2.4ร— faster, the ball crosses the screen in under half the time
CCrash
4
๐Ÿ’ฅ
Paddle Collision & Spin
getGlobalBounds().intersects() plus angle control
Locked
๐ŸŽฏ
Goal for this step

Bounce the ball off paddles with player-controlled angles.

  • 1Every SFML shape has getGlobalBounds() returning a FloatRect, and FloatRects have .intersects(other).
  • 2On paddle hit (moving toward it): flip bvx, and set bvy from the hit offset, centre hits go flat, edge hits go steep.
  • 3Speed up each rally: multiply bvx by 1.05 (cap ~700).
  • 4This is the same spin logic as Java Breakout, patterns transfer across languages!
pong.cpp
sf::FloatRect b = ball.getGlobalBounds();
if (b.intersects(paddle1.getGlobalBounds()) && bvx < 0) {
    bvx = -bvx * 1.05f;
    float padCentre  = paddle1.getPosition().y + 45;
    float ballCentre = ball.getPosition().y + 7;
    bvy = (ballCentre - padCentre) * 5.f;
}
if (b.intersects(paddle2.getGlobalBounds()) && bvx > 0) {
    bvx = -bvx * 1.05f;
    float padCentre  = paddle2.getPosition().y + 45;
    float ballCentre = ball.getPosition().y + 7;
    bvy = (ballCentre - padCentre) * 5.f;
}
โŒจ๏ธ
Code Challenge
+20 XP
Detect the ball hitting paddle 1:
pong.cpp
if (b.(paddle1.()) && bvx < )
    bvx = -bvx * 1.05f;
๐Ÿ’ก Hint: FloatRects overlap-test each other; every shape can produce its bounds; only bounce when heading left (toward paddle 1).
๐Ÿง 
Knowledge Check
+15 XP
Multiplying by 1.05 on every hit does what to a long rally?
ANothing measurable
BCompounds, after 15 hits the ball is about twice as fast, forcing rallies to end
CSlows the ball down
5
๐Ÿ”ข
Scores, Text & Serving
sf::Font, sf::Text and point scoring
Locked
๐ŸŽฏ
Goal for this step

Show a real scoreboard with a font, and serve after each point.

  • 1SFML needs a font FILE: download a free .ttf (or copy one from C:\Windows\Fonts, e.g. arial.ttf) next to your .cpp.
  • 2Load once: sf::Font font; font.loadFromFile("arial.ttf");, always check it loaded!
  • 3An sf::Text uses the font: set character size 48, position top-centre; update its string each frame with std::to_string.
  • 4Ball out left โ†’ player 2 scores; out right โ†’ player 1; then reset to centre with a serve toward the loser.
pong.cpp
sf::Font font;
if (!font.loadFromFile("arial.ttf")) return 1;
sf::Text scoreText("0   0", font, 48);
scoreText.setPosition(330, 20);
int s1 = 0, s2 = 0;

// scoring:
float bx = ball.getPosition().x;
if (bx < -20)  { s2++; ball.setPosition(393, 243); bvx = 300;  bvy = 180; }
if (bx > 820)  { s1++; ball.setPosition(393, 243); bvx = -300; bvy = 180; }
scoreText.setString(std::to_string(s1) + "   " + std::to_string(s2));
โœ๏ธ
Fill in the Blanks
+15 XP
SFML text needs a loaded from a .ttf file. Numbers become strings with std:: before going into setString.
๐Ÿง 
Knowledge Check
+15 XP
Why check the return value of loadFromFile?
AIt returns the font size
BA missing font file fails silently otherwise, you would draw invisible text and hunt the bug for hours
CC++ requires all returns checked
6
๐Ÿค–
AI Opponent & Game Over
A beatable computer paddle and first-to-7
Locked
๐ŸŽฏ
Goal for this step

Add a computer opponent and finish the game with a winner banner.

  • 1The AI drives paddle2: chase the ballโ€™s y with a dead-zone (18 px) and a max speed slightly below yours, flawed on purpose.
  • 2First to 7 wins: freeze the ball, show "PLAYER 1 WINS!" in big text, R restarts.
  • 3You now have the full game-state pattern in a third language. MENU/PLAY/OVER is universal.
  • 4Stretch: two-player toggle on key 2, rally counter, sound with sf::Sound.
pong.cpp
bool vsComputer = true;
float aiSpeed = 5.f, deadZone = 18.f;

if (vsComputer) {
    float padCentre  = paddle2.getPosition().y + 45;
    float ballCentre = ball.getPosition().y + 7;
    if (padCentre < ballCentre - deadZone) paddle2.move(0,  aiSpeed);
    if (padCentre > ballCentre + deadZone) paddle2.move(0, -aiSpeed);
}

bool gameOver = (s1 == 7 || s2 == 7);
โŒจ๏ธ
Code Challenge
+20 XP
Write the flawed-on-purpose AI chase:
pong.cpp
if (padCentre < ballCentre - ) paddle2.move(0, );
if (padCentre > ballCentre + deadZone) paddle2.move(0, );
๐Ÿ’ก Hint: Only react outside the dead-zone; below the ball move down (positive), above it move up (negative).
๐Ÿง 
Knowledge Check
+15 XP
You have now built Pong AI in Java AND C++. What stayed identical?
AThe syntax
BThe LOGIC, chase with a dead-zone and speed limit. Languages change, game design thinking transfers
CThe compiler
๐ŸŽ‰๐Ÿ†๐ŸŽฎโœจ๐ŸŽ‰
Workshop Complete!
You compiled, linked and shipped a C++ game with delta-time movement, genuinely professional foundations. Episode 2: Snake!
0
Total XP
1
Level
0
Best Streak
0%
Accuracy
โ–ถ Episode 2: Snake โ†’
โญ 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