Expert Topics
Graphics Programming in C++
Table of Contents
Graphics Libraries Overview
Popular Options
| Library | Purpose | Level | Best For |
|---|---|---|---|
| SFML | 2D games/graphics | Beginner | 2D games, multimedia apps |
| SDL2 | 2D games/graphics | Beginner | Cross-platform, game engines |
| Raylib | 2D/3D games | Beginner | Learning, prototyping |
| OpenGL | 2D/3D graphics | Advanced | Custom rendering, 3D |
| Vulkan | High-perf graphics | Expert | AAA games, performance |
Installation (SFML on Ubuntu/Debian)
# Install SFML development libraries
sudo apt install libsfml-dev
# Compile with SFML
g++ -std=c++17 main.cpp -lsfml-graphics -lsfml-window -lsfml-system
The Game Loop
Every graphical application runs a continuous loop that processes input, updates state, and renders graphics.
The Classic Game Loop
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β THE GAME LOOP β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββ β
β β INITIALIZATION β β
β β - Create windowβ β
β β - Load assets β β
β ββββββββββ¬βββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β MAIN LOOP β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β β β β
β β β ββββββββββββββββ β β β
β β β β 1. PROCESS β Poll keyboard, mouse, window events β β β
β β β β INPUT β Handle user actions β β β
β β β ββββββββ¬ββββββββ β β β
β β β β β β β
β β β βΌ β β β
β β β ββββββββββββββββ β β β
β β β β 2. UPDATE β Move objects, apply physics β β β
β β β β STATE β Check collisions, game logic β β β
β β β ββββββββ¬ββββββββ β β β
β β β β β β β
β β β βΌ β β β
β β β ββββββββββββββββ β β β
β β β β 3. RENDER β Clear screen β β β
β β β β GRAPHICS β Draw all objects β β β
β β β β β Display to screen β β β
β β β ββββββββ¬ββββββββ β β β
β β β β β β β
β β ββββββββββββ΄ββββββ Loop continues ββββββββββββββββββββββββββ β β
β β β β
β βββββββββββββββββββββββββββββββββββ Until window closed βββββββββββ β
β β
β βββββββββββββββββββ β
β β CLEANUP β β
β β - Free memory β β
β β - Close window β β
β βββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Frame Rate and Delta Time
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DELTA TIME EXPLAINED β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Problem: Different computers run at different speeds! β
β β
β Fast Computer (120 FPS): Slow Computer (30 FPS): β
β βββββββββββββββββββββββββ βββββββββββββββββββββββββ β
β Frame 1: move 1 pixel Frame 1: move 1 pixel β
β Frame 2: move 1 pixel Frame 2: move 1 pixel β
β ... ... β
β 120 frames = 120 pixels 30 frames = 30 pixels β
β β
β SAME TIME, DIFFERENT DISTANCE! β β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Solution: Use DELTA TIME (dt) = time since last frame β
β β
β movement = velocity * dt β
β β
β Fast Computer (dt = 0.0083s): Slow Computer (dt = 0.033s): β
β βββββββββββββββββββββββββββββ βββββββββββββββββββββββββ β
β Frame 1: 100 * 0.0083 = 0.83px Frame 1: 100 * 0.033 = 3.3px β
β Frame 2: 100 * 0.0083 = 0.83px Frame 2: 100 * 0.033 = 3.3px β
β ... ... β
β 120 frames = ~100 pixels 30 frames = ~100 pixels β
β β
β SAME TIME, SAME DISTANCE! β
β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Coordinate Systems
Screen Coordinate System
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SCREEN COORDINATE SYSTEM β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β (0,0) βββββββββββββββββββββββββββββββββββββββββββββββββΊ X (width) β
β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β WINDOW β β
β β β β β
β β β (100, 50) β β β
β β β β β
β β β (400, 300) β β β
β β β β β
β β β (700, 500) β β β
β β β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β βΌ β
β Y (height) β
β β
β β οΈ NOTE: Y increases DOWNWARD (opposite of math coordinates!) β
β β
β Window Size: 800 x 600 β
β β’ Top-left corner: (0, 0) β
β β’ Top-right corner: (799, 0) β
β β’ Bottom-left corner: (0, 599) β
β β’ Bottom-right corner: (799, 599) β
β β’ Center: (400, 300) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Shape Positioning and Origin
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SHAPE ORIGIN AND POSITIONING β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β By Default: Origin is at TOP-LEFT of shape β
β β
β setPosition(100, 50) β
β β β
β βΌ β
β (100,50) βββββββββββββββ β
β β ββββββββββββ β Shape drawn to the RIGHT and DOWN β
β β ββββββββββββ β from position β
β β ββββββββββββ β β
β ββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β With setOrigin(): Origin can be anywhere β
β β
β setOrigin(width/2, height/2) // Center origin β
β setPosition(100, 50) β
β β
β ββββββββββββββββ β
β β ββββββββββββ β β
β β βββββββββββββ β β Position (100,50) is now at CENTER β
β β ββββββββββββ β β
β ββββββββββββββββ β
β β
β Centering is useful for: β
β β’ Rotation (rotates around origin) β
β β’ Scaling (scales from origin) β
β β’ Positioning objects by center β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
SFML Basics
Window Creation and Game Loop
#include <SFML/Graphics.hpp>
int main() {
// Create a window (width, height, title)
sf::RenderWindow window(sf::VideoMode(800, 600), "My Game");
// Optional: Set frame rate limit
window.setFramerateLimit(60);
// Clock for delta time
sf::Clock clock;
// Main game loop
while (window.isOpen()) {
// 1. PROCESS INPUT (events)
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed) {
if (event.key.code == sf::Keyboard::Escape)
window.close();
}
}
// 2. UPDATE (game logic)
float dt = clock.restart().asSeconds();
// Update game objects here using dt
// 3. RENDER (drawing)
window.clear(sf::Color::Black); // Clear with color
// Draw all objects here
window.display(); // Show on screen
}
return 0;
}
The Rendering Pipeline
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SFML RENDERING PIPELINE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β window.clear() β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β BACK BUFFER β β
β β (invisible, being drawn to) β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β β β β
β β β Cleared to black (or specified color) β β β
β β β β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β window.draw(shape1) β
β window.draw(shape2) β Drawing happens HERE (to back buffer) β
β window.draw(sprite) β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β BACK BUFFER β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β β β² β β β
β β β shape1 sprite β β β
β β β β shape2 β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β window.display() β SWAP buffers (back β front) β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β FRONT BUFFER β β
β β (visible on screen!) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β This double-buffering prevents screen tearing and flickering! β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Color
// Predefined colors
sf::Color red = sf::Color::Red;
sf::Color green = sf::Color::Green;
sf::Color blue = sf::Color::Blue;
sf::Color white = sf::Color::White;
sf::Color black = sf::Color::Black;
sf::Color yellow = sf::Color::Yellow;
sf::Color cyan = sf::Color::Cyan;
sf::Color magenta = sf::Color::Magenta;
sf::Color transparent = sf::Color::Transparent;
// Custom RGB color
sf::Color purple(128, 0, 255); // RGB
// Custom RGBA color (with transparency)
sf::Color semiTransparent(255, 0, 0, 128); // 50% transparent red
// R G B Alpha
// 0=invisible, 255=opaque
Drawing Shapes
Rectangle
sf::RectangleShape rect(sf::Vector2f(100, 50)); // width, height
rect.setPosition(200, 150);
rect.setFillColor(sf::Color::Blue);
rect.setOutlineColor(sf::Color::White);
rect.setOutlineThickness(2);
window.draw(rect);
Circle
sf::CircleShape circle(50); // radius
circle.setPosition(300, 200);
circle.setFillColor(sf::Color::Green);
circle.setPointCount(100); // smoothness
window.draw(circle);
Convex Shapes
sf::ConvexShape triangle;
triangle.setPointCount(3);
triangle.setPoint(0, sf::Vector2f(0, 0));
triangle.setPoint(1, sf::Vector2f(100, 0));
triangle.setPoint(2, sf::Vector2f(50, 100));
triangle.setFillColor(sf::Color::Yellow);
window.draw(triangle);
Lines
sf::Vertex line[] = {
sf::Vertex(sf::Vector2f(10, 10), sf::Color::Red),
sf::Vertex(sf::Vector2f(200, 200), sf::Color::Blue)
};
window.draw(line, 2, sf::Lines);
Textures and Sprites
Loading Textures
sf::Texture texture;
if (!texture.loadFromFile("player.png")) {
// Handle error
return -1;
}
sf::Sprite sprite;
sprite.setTexture(texture);
sprite.setPosition(100, 100);
sprite.setScale(2.0f, 2.0f); // 2x size
window.draw(sprite);
Texture Regions
// Use part of texture (sprite sheet)
sprite.setTextureRect(sf::IntRect(0, 0, 32, 32)); // x, y, w, h
Transform
sprite.setOrigin(16, 16); // Center of rotation
sprite.setRotation(45); // Degrees
sprite.setScale(1.5f, 1.5f);
sprite.move(10, 0); // Relative movement
Animation
Frame-based Animation
class Animation {
sf::Sprite& sprite;
sf::IntRect frameRect;
int currentFrame = 0;
int frameCount;
float frameTime;
float elapsedTime = 0;
public:
Animation(sf::Sprite& s, int frames, float duration,
int frameWidth, int frameHeight)
: sprite(s), frameCount(frames),
frameTime(duration / frames),
frameRect(0, 0, frameWidth, frameHeight) {}
void update(float dt) {
elapsedTime += dt;
if (elapsedTime >= frameTime) {
elapsedTime = 0;
currentFrame = (currentFrame + 1) % frameCount;
frameRect.left = currentFrame * frameRect.width;
sprite.setTextureRect(frameRect);
}
}
};
Delta Time
sf::Clock clock;
while (window.isOpen()) {
float dt = clock.restart().asSeconds();
// Update with delta time
position += velocity * dt;
}
Input Handling
Keyboard
// Real-time (continuous)
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
player.move(-speed * dt, 0);
}
// Event-based (single press)
if (event.type == sf::Event::KeyPressed) {
if (event.key.code == sf::Keyboard::Space) {
jump();
}
}
Mouse
// Position
sf::Vector2i mousePos = sf::Mouse::getPosition(window);
// Buttons
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
shoot();
}
// Events
if (event.type == sf::Event::MouseButtonPressed) {
if (event.mouseButton.button == sf::Mouse::Left) {
click(event.mouseButton.x, event.mouseButton.y);
}
}
Game Loop
Fixed Timestep
int main() {
sf::RenderWindow window(sf::VideoMode(800, 600), "Game");
const float TIMESTEP = 1.0f / 60.0f;
float accumulator = 0;
sf::Clock clock;
while (window.isOpen()) {
float dt = clock.restart().asSeconds();
accumulator += dt;
// Handle events
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
window.close();
}
// Fixed update
while (accumulator >= TIMESTEP) {
update(TIMESTEP);
accumulator -= TIMESTEP;
}
// Render
window.clear();
render(window);
window.display();
}
return 0;
}
Simple Player Class
class Player {
sf::Sprite sprite;
sf::Texture texture;
float speed = 200.0f;
public:
bool init(const string& file) {
if (!texture.loadFromFile(file)) return false;
sprite.setTexture(texture);
return true;
}
void update(float dt) {
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
sprite.move(-speed * dt, 0);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
sprite.move(speed * dt, 0);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
sprite.move(0, -speed * dt);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
sprite.move(0, speed * dt);
}
void draw(sf::RenderWindow& window) {
window.draw(sprite);
}
};
Text Rendering
sf::Font font;
if (!font.loadFromFile("arial.ttf")) {
// Handle error
}
sf::Text text;
text.setFont(font);
text.setString("Hello World!");
text.setCharacterSize(24);
text.setFillColor(sf::Color::White);
text.setPosition(100, 100);
window.draw(text);
Cheat Sheet
// Window
sf::RenderWindow window(sf::VideoMode(w, h), "Title");
window.clear();
window.draw(shape);
window.display();
// Shapes
sf::RectangleShape rect(sf::Vector2f(w, h));
sf::CircleShape circle(radius);
shape.setPosition(x, y);
shape.setFillColor(color);
// Sprites
sf::Texture tex; tex.loadFromFile("img.png");
sf::Sprite sprite; sprite.setTexture(tex);
// Input
sf::Keyboard::isKeyPressed(sf::Keyboard::Space);
sf::Mouse::getPosition(window);
// Time
sf::Clock clock;
float dt = clock.restart().asSeconds();
Compile & Run
# Install SFML first: sudo apt install libsfml-dev
g++ -std=c++17 -Wall examples.cpp -lsfml-graphics -lsfml-window -lsfml-system -o examples
./examples