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.
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.
sf::VideoMode({800, 500})sf::VideoMode({800, 500}) (curly braces), the new event loop while (const std::optional e = window.pollEvent())while (const std::optional e = window.pollEvent()) with e->is<sf::Event::Closed>()e->is<sf::Event::Closed>(), sf::Keyboard::Key::Leftsf::Keyboard::Key::Left (add ::Key::Key), and setPosition({x, y})setPosition({x, y}) / move({dx, dy})move({dx, dy}) (curly braces). The โ
complete finished code at the bottom of this page is full SFML 3 and compiles on a fresh install, use it as your reference.Get a C++ toolchain and SFML installed, and compile a first window.
#include <SFML/Graphics.hpp>
#include <optional>
int main() {
sf::RenderWindow window(sf::VideoMode({800, 500}), "C++ Pong");
window.setFramerateLimit(60);
while (window.isOpen()) {
while (const std::optional event = window.pollEvent())
if (event->is<sf::Event::Closed>())
window.close();
window.clear(sf::Color(15, 18, 32));
window.display();
}
return 0;
}#include <SFML/Graphics.hpp>
#include <optional>
int main() {
sf::RenderWindow window(sf::VideoMode({800, 500}), "C++ Pong");
window.setFramerateLimit(60);
while (window.isOpen()) {
while (const std::optional event = window.pollEvent())
if (event->is<sf::Event::Closed>())
window.close();
window.clear(sf::Color(15, 18, 32));
window.display();
}
return 0;
}
Draw two paddles and move them with W/S and Up/Down.
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::Key::W)) paddle1.move({0, -speed});
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) paddle1.move({0, speed});
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up)) paddle2.move({0, -speed});
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Down)) paddle2.move({0, speed});
window.draw(paddle1);
window.draw(paddle2);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::Key::W)) paddle1.move({0, -speed});
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) paddle1.move({0, speed});
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up)) paddle2.move({0, -speed});
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Down)) paddle2.move({0, speed});
window.draw(paddle1);
window.draw(paddle2);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W))
paddle1.move({0, if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W))
paddle1.move({0, });
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S))
paddle1.move({0, });
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S))
paddle1.move({0, });});
Move the ball using delta time, the professional way.
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;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;
Bounce the ball off paddles with player-controlled angles.
sf::FloatRect b = ball.getGlobalBounds();
if (b.findIntersection(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.findIntersection(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;
}sf::FloatRect b = ball.getGlobalBounds();
if (b.findIntersection(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.findIntersection(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;
}
if (b.if (b.(paddle1.(paddle1.()) && bvx < ()) && bvx < )
bvx = -bvx * 1.05f;)
bvx = -bvx * 1.05f;
Show a real scoreboard with a font, and serve after each point.
sf::Font font;
if (!font.openFromFile("arial.ttf")) return 1;
sf::Text scoreText(font, "0 0", 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));sf::Font font;
if (!font.openFromFile("arial.ttf")) return 1;
sf::Text scoreText(font, "0 0", 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));
Add a computer opponent and finish the game with a winner banner.
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);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);
if (padCentre < ballCentre - if (padCentre < ballCentre - ) paddle2.move({0, ) paddle2.move({0, });
if (padCentre > ballCentre + deadZone) paddle2.move({0, });
if (padCentre > ballCentre + deadZone) paddle2.move({0, });});
The whole game in one file, written in current SFML 3 (what the install step gives you today) and compiled with g++ + SFML 3.0 to confirm it builds. The step snippets above use classic SFML 2 names; this is your working reference. Build it with g++ pong.cpp -o pong -lsfml-graphics -lsfml-window -lsfml-systemg++ pong.cpp -o pong -lsfml-graphics -lsfml-window -lsfml-system then run ./pong./pong (on Windows, from the MSYS2 MinGW terminal so it finds the SFML DLLs).
// Complete C++ Pong for SFML 3. Build with:
// g++ pong.cpp -o pong -lsfml-graphics -lsfml-window -lsfml-system
#include <SFML/Graphics.hpp>
#include <optional>
#include <cstdlib>
#include <string>
int main() {
sf::RenderWindow window(sf::VideoMode({800, 500}), "C++ Pong");
window.setFramerateLimit(60);
sf::RectangleShape p1({14.f, 90.f});
sf::RectangleShape p2({14.f, 90.f});
sf::RectangleShape ball({14.f, 14.f});
p1.setPosition({20.f, 205.f});
p2.setPosition({766.f, 205.f});
float bx = 393, by = 243, bvx = 300, bvy = 180;
int p1Score = 0, p2Score = 0;
// Score text needs a font. Point this at any .ttf on your system.
sf::Font font;
bool haveFont = font.openFromFile("C:/Windows/Fonts/arial.ttf");
auto serve = [&](int dir) {
bx = 393; by = 243;
bvx = 300.f * dir;
bvy = (std::rand() % 2 == 0) ? 180.f : -180.f;
};
serve((std::rand() % 2 == 0) ? 1 : -1);
sf::Clock clock;
while (window.isOpen()) {
while (const std::optional event = window.pollEvent())
if (event->is<sf::Event::Closed>())
window.close();
float dt = clock.restart().asSeconds();
// paddles: W/S and Up/Down, speed in pixels per second
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) p1.move({0.f, -360.f * dt});
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) p1.move({0.f, 360.f * dt});
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up)) p2.move({0.f, -360.f * dt});
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Down)) p2.move({0.f, 360.f * dt});
auto clampY = [](sf::RectangleShape& p) {
float y = p.getPosition().y;
if (y < 0) p.setPosition({p.getPosition().x, 0.f});
if (y > 410) p.setPosition({p.getPosition().x, 410.f});
};
clampY(p1); clampY(p2);
// ball: velocity is pixels per second, scaled by dt so speed stays the same at any frame rate
bx += bvx * dt; by += bvy * dt;
if (by <= 0 || by + 14 >= 500) bvy = -bvy;
ball.setPosition({bx, by});
sf::FloatRect br = ball.getGlobalBounds();
if (br.findIntersection(p1.getGlobalBounds()) && bvx < 0) {
bvx = -bvx + 60.f;
bvy = (((by + 7) - (p1.getPosition().y + 45)) / 7.f) * 60.f;
}
if (br.findIntersection(p2.getGlobalBounds()) && bvx > 0) {
bvx = -(bvx + 60.f);
bvy = (((by + 7) - (p2.getPosition().y + 45)) / 7.f) * 60.f;
}
if (bvx > 720.f) bvx = 720.f; if (bvx < -720.f) bvx = -720.f;
if (bx < 0) { p2Score++; serve(-1); }
if (bx > 800) { p1Score++; serve(1); }
window.clear(sf::Color(12, 14, 24));
window.draw(p1); window.draw(p2); window.draw(ball);
if (haveFont) {
sf::Text s(font, std::to_string(p1Score) + " " + std::to_string(p2Score), 40);
s.setPosition({360.f, 20.f});
window.draw(s);
}
window.display();
}
}// Complete C++ Pong for SFML 3. Build with:
// g++ pong.cpp -o pong -lsfml-graphics -lsfml-window -lsfml-system
#include <SFML/Graphics.hpp>
#include <optional>
#include <cstdlib>
#include <string>
int main() {
sf::RenderWindow window(sf::VideoMode({800, 500}), "C++ Pong");
window.setFramerateLimit(60);
sf::RectangleShape p1({14.f, 90.f});
sf::RectangleShape p2({14.f, 90.f});
sf::RectangleShape ball({14.f, 14.f});
p1.setPosition({20.f, 205.f});
p2.setPosition({766.f, 205.f});
float bx = 393, by = 243, bvx = 300, bvy = 180;
int p1Score = 0, p2Score = 0;
// Score text needs a font. Point this at any .ttf on your system.
sf::Font font;
bool haveFont = font.openFromFile("C:/Windows/Fonts/arial.ttf");
auto serve = [&](int dir) {
bx = 393; by = 243;
bvx = 300.f * dir;
bvy = (std::rand() % 2 == 0) ? 180.f : -180.f;
};
serve((std::rand() % 2 == 0) ? 1 : -1);
sf::Clock clock;
while (window.isOpen()) {
while (const std::optional event = window.pollEvent())
if (event->is<sf::Event::Closed>())
window.close();
float dt = clock.restart().asSeconds();
// paddles: W/S and Up/Down, speed in pixels per second
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) p1.move({0.f, -360.f * dt});
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) p1.move({0.f, 360.f * dt});
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up)) p2.move({0.f, -360.f * dt});
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Down)) p2.move({0.f, 360.f * dt});
auto clampY = [](sf::RectangleShape& p) {
float y = p.getPosition().y;
if (y < 0) p.setPosition({p.getPosition().x, 0.f});
if (y > 410) p.setPosition({p.getPosition().x, 410.f});
};
clampY(p1); clampY(p2);
// ball: velocity is pixels per second, scaled by dt so speed stays the same at any frame rate
bx += bvx * dt; by += bvy * dt;
if (by <= 0 || by + 14 >= 500) bvy = -bvy;
ball.setPosition({bx, by});
sf::FloatRect br = ball.getGlobalBounds();
if (br.findIntersection(p1.getGlobalBounds()) && bvx < 0) {
bvx = -bvx + 60.f;
bvy = (((by + 7) - (p1.getPosition().y + 45)) / 7.f) * 60.f;
}
if (br.findIntersection(p2.getGlobalBounds()) && bvx > 0) {
bvx = -(bvx + 60.f);
bvy = (((by + 7) - (p2.getPosition().y + 45)) / 7.f) * 60.f;
}
if (bvx > 720.f) bvx = 720.f; if (bvx < -720.f) bvx = -720.f;
if (bx < 0) { p2Score++; serve(-1); }
if (bx > 800) { p1Score++; serve(1); }
window.clear(sf::Color(12, 14, 24));
window.draw(p1); window.draw(p2); window.draw(ball);
if (haveFont) {
sf::Text s(font, std::to_string(p1Score) + " " + std::to_string(p2Score), 40);
s.setPosition({360.f, 20.f});
window.draw(s);
}
window.display();
}
}