Docs

README

Graphics Programming in C++

Table of Contents

  1. Graphics Libraries Overview
  2. The Game Loop
  3. Coordinate Systems
  4. SFML Basics
  5. Drawing Shapes
  6. Textures and Sprites
  7. Animation
  8. Input Handling
  9. Text Rendering

Graphics Libraries Overview

Popular Options

LibraryPurposeLevelBest For
SFML2D games/graphicsBeginner2D games, multimedia apps
SDL22D games/graphicsBeginnerCross-platform, game engines
Raylib2D/3D gamesBeginnerLearning, prototyping
OpenGL2D/3D graphicsAdvancedCustom rendering, 3D
VulkanHigh-perf graphicsExpertAAA 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);

Quick Reference

// 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
README - C++ Tutorial | DeepML