C++ with Raylib

Setup

  • Install clang :

  • Get the latest RayLib release :

  • File system :

    ๎—ฟ .
    โ”œโ”€โ”€ ๎˜ž main.cpp  // the main file where the game is
    โ””โ”€โ”€ ๎—ฟ raylib    // folder where I put the downloaded raylib
        โ”œโ”€โ”€ ๓ฐ‚บ README.md
        โ”œโ”€โ”€ ๏€ญ LICENSE
        โ”œโ”€โ”€ ๏€– CHANGELOG
        โ”œโ”€โ”€ ๎—ฟ lib
        โ”‚   โ”œโ”€โ”€ ๎ฎœ raylib.dll
        โ”‚   โ”œโ”€โ”€ ๏…ผ libraylibdll.a
        โ”‚   โ””โ”€โ”€ ๏…ผ libraylib.a
        โ””โ”€โ”€ ๎—ผ include
            โ”œโ”€โ”€ ๎˜ž rlgl.h
            โ”œโ”€โ”€ ๎˜ž raymath.h
            โ””โ”€โ”€ ๎˜ž raylib.h
    
  • Compile :

    • clang++ main.cpp -o main.exe -Lraylib/lib -lraylib -lopengl32 -lgdi32 -lwinmm

      • clang++

        • Invokes the Clang C++ compiler.

        • clang  uses the the C compiler driver, not the C++ one, so it does not link the C++ standard library. Use clang++  instead.

      • main.cpp

        • Source file to compile.

      • -o main.exe

        • Sets the output executable name.

      • -Lraylib/lib

        • Adds this directory to the linkerโ€™s search paths.

        • Clang will look here when resolving -l...  flags.

      • -lraylib

        • Links against:

          • raylib/lib/libraylib.a  (static)

          • or

          • raylib/lib/libraylibdll.a  (import lib for DLL)

        • The linker automatically resolves:

          • -lraylib  โ†’ libraylib.a

      • -lopengl32

        • Windows system library, required for raylib.

        • Links OpenGL (graphics backend)

      • -lgdi32

        • Windows system library, required for raylib.

        • Windows GDI (window/context handling pieces)

      • -lwinmm

        • Windows system library, required for raylib.

        • Windows multimedia (timing, audio support)

  • Execute :

    • .\main.exe

Moving Ball

/*
clang++ main.cpp -o main.exe -L../raylib/lib -lraylib -lopengl32 -lgdi32 -lwinmm
*/

#include "../raylib/include/raylib.h"


int main() { // <- this is the only difference from C
    constexpr int SCREEN_WIDTH  = 800;
    constexpr int SCREEN_HEIGHT = 450;

    InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Raylib in C++ | 1_moving_ball");
    SetTargetFPS(60);

    // Initialize ball
    Vector2 ball_position = { SCREEN_WIDTH / 2.0, SCREEN_HEIGHT / 2.0 };

    while (!WindowShouldClose()) {
        // Update ball position
        if (IsKeyDown(KEY_RIGHT)) ball_position.x += 2.0f;
        if (IsKeyDown(KEY_LEFT))  ball_position.x -= 2.0f;
        if (IsKeyDown(KEY_UP))    ball_position.y -= 2.0f;
        if (IsKeyDown(KEY_DOWN))  ball_position.y += 2.0f;

        // Draw
        BeginDrawing();
        ClearBackground(RAYWHITE);

        DrawText("move the ball with arrow keys", 10, 10, 20, DARKGRAY);
        DrawCircleV(ball_position, 50.0, MAROON);

        EndDrawing();
    }

    CloseWindow();

    return 0;
}

Collect The Coins

/*
clang++ main.cpp -o main.exe -L../raylib/lib -lraylib -lopengl32 -lgdi32 -lwinmm
Only for C++11 or newer.
*/

#include "../raylib/include/raylib.h"
#include "../raylib/include/raymath.h"

#include <array>

constexpr int   MAX_COINS    = 20; // Comparing to C: Real language construction with type-checking
constexpr float PLAYER_SPEED = 4.0;

struct Player {
    Vector2 position{}; // Zero-initialized by default.
    float   radius{};
    int     score{};
};

struct Coin {
    Vector2 position{};
    float   radius{};
    bool    active{};
};

int main() {
    const int SCREEN_WIDTH  = 800;
    const int SCREEN_HEIGHT = 600;

    InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Raylib in C++ | 2_collect_the_coins");
    SetTargetFPS(60);

    // Initialize player
    Player player{};
    player.position = {400.0, 300.0};
    player.radius   = 20.0;
    player.score    = 0;

    // Initialize coins
    std::array<Coin, MAX_COINS> coins{};
    for (auto& coin : coins) {
        coin.position = {
            static_cast<float>(GetRandomValue(20, SCREEN_WIDTH - 20)),
            static_cast<float>(GetRandomValue(20, SCREEN_HEIGHT - 20))
        };
        coin.radius = 8.0;
        coin.active = true;
    }

    while (!WindowShouldClose()) {
        // Update player
        if (IsKeyDown(KEY_W)) player.position.y -= PLAYER_SPEED;
        if (IsKeyDown(KEY_S)) player.position.y += PLAYER_SPEED;
        if (IsKeyDown(KEY_A)) player.position.x -= PLAYER_SPEED;
        if (IsKeyDown(KEY_D)) player.position.x += PLAYER_SPEED;

        // Check collisions
        for (auto& coin : coins) {
            if (coin.active) {
                float dist = Vector2Distance(player.position, coin.position);

                if (dist < player.radius + coin.radius) {
                    coin.active = false;
                    player.score += 1;
                }
            }
        }

        // Draw
        BeginDrawing();
        ClearBackground(DARKGREEN);

        DrawCircleV(player.position, player.radius, BLUE);
        for (const auto& coin : coins) {
            if (coin.active) {
                DrawCircleV(coin.position, coin.radius, GOLD);
            }
        }
        DrawText(TextFormat("Score: %d", player.score), 10, 10, 20, WHITE);

        EndDrawing();
    }

    CloseWindow();
    return 0;
}

Space Invaders

/* 
clang++ main.cpp -o main.exe -L../raylib/lib -lraylib -lopengl32 -lgdi32 -lwinmm
Only for C++11 or newer.
*/

#include "../raylib/include/raylib.h"
#include "../raylib/include/raymath.h"

#include <array>
#include <vector>

constexpr int   MAX_BULLETS  = 64; // Comparing to C: Real language construction with type-checking
constexpr float BULLET_SPEED = 6.0f;

struct Player {
    Vector2 position{};
    float   radius{};
};

struct Bullet {
    Vector2 position{};
    Vector2 velocity{};
    float   radius{};
    bool    active{};
};

struct Enemy {
    Vector2 position{};
    float   radius{};
    bool    active{};
    Color   color{};
};

void spawn_bullet(std::array<Bullet, MAX_BULLETS>& bullets, Vector2 pos);
void spawn_inactive_enemies(std::vector<Enemy>& enemies);

int main() {
    constexpr int SCREEN_WIDTH  = 800;
    constexpr int SCREEN_HEIGHT = 600;

    InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Raylib in C++ | 3_space_invaders");
    SetTargetFPS(60);

    // Initialize player
    Player player{{400.0f, 500.0f}, 20.0f};
    std::array<Bullet, MAX_BULLETS> bullets{};  // Stack allocation

    // Initialize enemies
    std::vector<Enemy> enemies(20); // Heap allocation
    int enemies_destroyed_count = 0;
    spawn_inactive_enemies(enemies);

    while (!WindowShouldClose()) {
        // Update player
        if (IsKeyDown(KEY_A)) player.position.x -= 4.0f;
        if (IsKeyDown(KEY_D)) player.position.x += 4.0f;
        if (IsKeyDown(KEY_W)) player.position.y -= 4.0f;
        if (IsKeyDown(KEY_S)) player.position.y += 4.0f;
        if (IsKeyPressed(KEY_SPACE)) {
            spawn_bullet(bullets, player.position);
        }

        // Update bullets
        for (auto& b : bullets) {
            if (b.active) {
                b.position.x += b.velocity.x;
                b.position.y += b.velocity.y;

                if (b.position.y < 0) {
                    b.active = false;
                }
            }

        }

        // Update enemies
        int active_enemies = 0;
        for (auto& e : enemies) {
            if (e.active) {
                e.position.y += 0.5f;
                active_enemies += 1;
            }
        }

        // Respawn
        if (active_enemies < 5) {
            spawn_inactive_enemies(enemies);
        }

        // Collisions
        for (auto& b : bullets) {
            if (!b.active) continue;

            for (auto& e : enemies) {
                if (!e.active) continue;

                float d = Vector2Distance(b.position, e.position);
                float r = b.radius + e.radius;

                if (d < r) {
                    b.active = false;
                    e.active = false;
                    enemies_destroyed_count += 1;
                }
            }
        }

        // Draw
        BeginDrawing();
        ClearBackground(BLACK);

        DrawCircleV(player.position, player.radius, BLUE);

        for (const auto& b : bullets) {
            if (b.active) {
                DrawCircleV(b.position, b.radius, YELLOW);
            }
        }

        for (const auto& e : enemies) {
            if (e.active) {
                DrawCircleV(e.position, e.radius, e.color);
            }
        }

        DrawText("Space Invaders: WASD move | SPACE shoot", 10, 10, 20, WHITE);
        DrawText(TextFormat("Enemies destroyed: %d", enemies_destroyed_count),
                 10, 40, 20, YELLOW);

        EndDrawing();
    }

    CloseWindow();
    return 0;
}

// Spawn first inactive bullet
void spawn_bullet(std::array<Bullet, MAX_BULLETS>& bullets, Vector2 pos) {
    for (auto& b : bullets) {
        if (!b.active) {
            b.active   = true;
            b.position = pos;
            b.velocity = {0.0f, -BULLET_SPEED};
            b.radius   = 5.0f;
            return;
        }
    }
}

// Activate all inactive enemies
void spawn_inactive_enemies(std::vector<Enemy>& enemies) {
    for (auto& e : enemies) {
        if (!e.active) {
            e.position = {
                static_cast<float>(GetRandomValue(50, 750)),
                static_cast<float>(GetRandomValue(50, 300))
            };
            e.radius = 15.0f;
            e.active = true;
            e.color  = {
                static_cast<unsigned char>(GetRandomValue(100, 255)),
                static_cast<unsigned char>(GetRandomValue(100, 255)),
                static_cast<unsigned char>(GetRandomValue(100, 255)),
                255
            };
        }
    }
}