Créer un jeu rétro 2D avec SDL en C : Guide complet

Dans le développement de jeux vidéo, Simple DirectMedia Layer (SDL) se distingue comme une bibliothèque incontournable pour les développeurs utilisant le langage C. SDL est particulièrement adaptée pour créer des jeux 2D rétro grâce à sa légèreté, sa polyvalence, et sa simplicité d’intégration.

Cet article vous guidera étape par étape pour créer un jeu rétro en 2D en utilisant SDL. Nous aborderons l’installation et la configuration de l’environnement, les fondamentaux de SDL, la gestion des entrées utilisateur, la création de la boucle principale du jeu, ainsi que l’intégration de sprites et la gestion des collisions.

À la fin de ce guide, vous aurez les bases nécessaires pour concevoir votre propre jeu en 2D, tout en maîtrisant les concepts fondamentaux de SDL et du développement de jeux en C.

Sommaire

Installation de SDL et configuration de l’environnement

Pour commencer à utiliser SDL dans vos projets C, vous devez installer la bibliothèque et configurer correctement votre environnement de développement. Voici les étapes nécessaires.

Étape 1 : Télécharger SDL


Rendez-vous sur le site officiel de SDL (libsdl.org) et téléchargez la dernière version stable de la bibliothèque. Vous pouvez choisir entre les versions précompilées ou le code source, selon vos préférences et votre système d’exploitation.

Étape 2 : Installer SDL sur votre système

  • Sous Windows :
  1. Téléchargez la version précompilée.
  2. Extrayez le contenu du fichier téléchargé.
  3. Ajoutez les fichiers nécessaires (librairies et en-têtes) à votre environnement de développement, comme Visual Studio ou Code::Blocks.
  • Sous macOS :
  1. Installez Homebrew si ce n’est pas déjà fait.
  2. Exécutez la commande suivante dans le terminal :
    bash brew install sdl2
  • Sous Linux :
    La plupart des distributions incluent SDL dans leurs dépôts officiels. Utilisez votre gestionnaire de paquets pour l’installer. Par exemple, sous Ubuntu :
  sudo apt-get install libsdl2-dev

Étape 3 : Configurer le projet

  1. Inclure SDL dans votre code
    Ajoutez l’inclusion suivante dans votre fichier source principal :
   #include <SDL2/SDL.h>
  1. Configurer le compilateur
    Assurez-vous que votre compilateur peut localiser les fichiers d’en-tête et les bibliothèques SDL. Voici un exemple de commande de compilation sous Linux :
   gcc -o mon_jeu mon_jeu.c -lSDL2


Sous Windows, vous devrez configurer manuellement les chemins des bibliothèques dans votre IDE.

Étape 4 : Vérification de l’installation


Testez votre installation en exécutant un programme simple qui initialise SDL et affiche une fenêtre. Voici un exemple :

#include <SDL2/SDL.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        printf("Erreur d'initialisation de SDL : %s\n", SDL_GetError());
        return 1;
    }
    SDL_Window *window = SDL_CreateWindow("Test SDL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, 0);
    if (!window) {
        printf("Erreur de création de la fenêtre : %s\n", SDL_GetError());
        SDL_Quit();
        return 1;
    }
    SDL_Delay(3000);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

Compilez et exécutez ce programme. Si une fenêtre s’ouvre brièvement, votre environnement est prêt à être utilisé. Vous êtes maintenant prêt à passer à l’étape suivante : l’initialisation de SDL pour votre projet.

Initialisation de SDL dans un projet C

L’initialisation de SDL est une étape clé dans le développement de votre jeu. Cette section explique comment configurer SDL pour gérer les graphiques, l’audio, et les événements nécessaires au bon fonctionnement de votre projet.

Étape 1 : Initialiser SDL


Pour commencer, vous devez appeler la fonction SDL_Init. Cette fonction initialise les sous-systèmes nécessaires (vidéo, audio, etc.). Voici un exemple simple :

#include <SDL2/SDL.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    // Initialisation de SDL
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) {
        printf("Erreur d'initialisation de SDL : %s\n", SDL_GetError());
        return 1;
    }
    printf("SDL a été initialisé avec succès !\n");

    // Nettoyage avant de quitter
    SDL_Quit();
    return 0;
}

Dans cet exemple, SDL_INIT_VIDEO initialise le sous-système vidéo, et SDL_INIT_AUDIO active les fonctionnalités audio. Si vous ne souhaitez initialiser que certains sous-systèmes, adaptez les flags en conséquence.

Étape 2 : Créer une fenêtre


Une fois SDL initialisé, la prochaine étape consiste à créer une fenêtre. Utilisez SDL_CreateWindow pour définir la taille, le titre, et la position de la fenêtre :

SDL_Window *window = SDL_CreateWindow(
    "Mon jeu SDL",                 // Titre de la fenêtre
    SDL_WINDOWPOS_CENTERED,        // Position X
    SDL_WINDOWPOS_CENTERED,        // Position Y
    800,                           // Largeur
    600,                           // Hauteur
    SDL_WINDOW_SHOWN               // Flags
);

if (!window) {
    printf("Erreur de création de la fenêtre : %s\n", SDL_GetError());
    SDL_Quit();
    return 1;
}

Étape 3 : Initialiser un rendu


Pour dessiner des éléments graphiques, vous devez créer un rendu associé à votre fenêtre. Utilisez SDL_CreateRenderer :

SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (!renderer) {
    printf("Erreur de création du rendu : %s\n", SDL_GetError());
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 1;
}

Dans cet exemple, le flag SDL_RENDERER_ACCELERATED permet d’utiliser l’accélération matérielle si elle est disponible.

Étape 4 : Nettoyage à la fin du programme


Il est crucial de libérer les ressources utilisées par SDL avant de quitter votre programme. Détruisez la fenêtre et le rendu, puis appelez SDL_Quit :

SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();

Exemple complet


Voici un exemple complet combinant toutes ces étapes :

#include <SDL2/SDL.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) {
        printf("Erreur d'initialisation de SDL : %s\n", SDL_GetError());
        return 1;
    }

    SDL_Window *window = SDL_CreateWindow(
        "Mon jeu SDL",
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        800,
        600,
        SDL_WINDOW_SHOWN
    );

    if (!window) {
        printf("Erreur de création de la fenêtre : %s\n", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (!renderer) {
        printf("Erreur de création du rendu : %s\n", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    // Boucle simple pour afficher une fenêtre pendant 5 secondes
    SDL_Delay(5000);

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

Avec cette base, vous êtes prêt à créer la fenêtre et le rendu nécessaires pour afficher et gérer vos éléments graphiques. La prochaine étape est la création de contenu et la gestion des interactions utilisateur.

Création de la fenêtre et du rendu 2D

Pour afficher et manipuler des éléments graphiques dans SDL, vous devez configurer une fenêtre et un rendu 2D. Cette section détaille les étapes nécessaires pour les créer et les utiliser efficacement.

Étape 1 : Créer une fenêtre


La fenêtre est l’élément principal où le contenu du jeu sera affiché. Utilisez la fonction SDL_CreateWindow pour créer cette fenêtre avec des paramètres personnalisés.

SDL_Window *window = SDL_CreateWindow(
    "Mon Jeu 2D",                 // Titre de la fenêtre
    SDL_WINDOWPOS_CENTERED,       // Position X
    SDL_WINDOWPOS_CENTERED,       // Position Y
    800,                          // Largeur
    600,                          // Hauteur
    SDL_WINDOW_SHOWN              // Flag d'affichage
);

if (!window) {
    printf("Erreur de création de la fenêtre : %s\n", SDL_GetError());
    SDL_Quit();
    return 1;
}

Paramètres importants :

  • SDL_WINDOWPOS_CENTERED place la fenêtre au centre de l’écran.
  • SDL_WINDOW_SHOWN affiche immédiatement la fenêtre à l’écran. Vous pouvez utiliser d’autres flags comme SDL_WINDOW_FULLSCREEN pour un mode plein écran.

Étape 2 : Configurer un rendu


Le rendu est responsable de dessiner des objets 2D dans la fenêtre. Créez un rendu avec SDL_CreateRenderer.

SDL_Renderer *renderer = SDL_CreateRenderer(
    window,                      // Fenêtre associée
    -1,                          // Pilote de rendu (automatique avec -1)
    SDL_RENDERER_ACCELERATED     // Accélération matérielle
);

if (!renderer) {
    printf("Erreur de création du rendu : %s\n", SDL_GetError());
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 1;
}

Options de rendu :

  • SDL_RENDERER_ACCELERATED utilise l’accélération matérielle pour des performances optimales.
  • SDL_RENDERER_PRESENTVSYNC active la synchronisation verticale pour éviter les déchirures d’écran.

Étape 3 : Définir la couleur d’arrière-plan


Avant de dessiner des objets, définissez une couleur d’arrière-plan avec SDL_SetRenderDrawColor.

SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // Noir
SDL_RenderClear(renderer); // Appliquer la couleur au rendu

Étape 4 : Afficher le contenu avec SDL_RenderPresent


Une fois le rendu configuré, utilisez SDL_RenderPresent pour afficher le contenu à l’écran.

SDL_RenderPresent(renderer);
SDL_Delay(3000); // Afficher la fenêtre pendant 3 secondes

Étape 5 : Nettoyage des ressources


Détruisez les objets SDL pour libérer les ressources avant de quitter le programme.

SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();

Exemple complet

Voici un programme complet qui crée une fenêtre avec un rendu et une couleur d’arrière-plan :

#include <SDL2/SDL.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        printf("Erreur d'initialisation de SDL : %s\n", SDL_GetError());
        return 1;
    }

    SDL_Window *window = SDL_CreateWindow(
        "Mon Jeu 2D",
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        800,
        600,
        SDL_WINDOW_SHOWN
    );

    if (!window) {
        printf("Erreur de création de la fenêtre : %s\n", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    SDL_Renderer *renderer = SDL_CreateRenderer(
        window,
        -1,
        SDL_RENDERER_ACCELERATED
    );

    if (!renderer) {
        printf("Erreur de création du rendu : %s\n", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // Couleur de fond : noir
    SDL_RenderClear(renderer); // Appliquer la couleur
    SDL_RenderPresent(renderer); // Afficher le rendu

    SDL_Delay(5000); // Afficher la fenêtre pendant 5 secondes

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

Avec ces étapes, vous avez une fenêtre fonctionnelle et un rendu prêt pour dessiner des éléments graphiques. Dans la prochaine section, nous verrons comment gérer les entrées utilisateur pour rendre votre jeu interactif.

Gestion des entrées utilisateur

Dans un jeu vidéo, la gestion des entrées utilisateur est essentielle pour permettre des interactions avec le joueur. SDL propose une API puissante pour capturer les événements tels que les clics de souris, les frappes de clavier, et autres périphériques d’entrée.

Étape 1 : Comprendre le système d’événements de SDL


SDL utilise une structure SDL_Event pour représenter les événements d’entrée. Cette structure est remplie par la fonction SDL_PollEvent, qui récupère les événements dans la file d’attente SDL.

SDL_Event event;
while (SDL_PollEvent(&event)) {
    // Traiter les événements ici
}

Étape 2 : Gérer les événements courants


Voici comment capturer et traiter les principaux types d’événements.

1. Gestion des événements clavier


Pour détecter une touche pressée ou relâchée, utilisez le champ event.key.keysym.sym.

if (event.type == SDL_KEYDOWN) {
    printf("Touche pressée : %s\n", SDL_GetKeyName(event.key.keysym.sym));
}

if (event.type == SDL_KEYUP) {
    printf("Touche relâchée : %s\n", SDL_GetKeyName(event.key.keysym.sym));
}

2. Gestion des événements de souris


Les clics et les mouvements de souris sont capturés via les événements SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP, et SDL_MOUSEMOTION.

if (event.type == SDL_MOUSEBUTTONDOWN) {
    printf("Bouton de souris %d pressé à (%d, %d)\n",
           event.button.button, event.button.x, event.button.y);
}

if (event.type == SDL_MOUSEMOTION) {
    printf("Souris déplacée à (%d, %d)\n", event.motion.x, event.motion.y);
}

3. Fermeture de la fenêtre


Pour détecter quand l’utilisateur ferme la fenêtre, écoutez l’événement SDL_QUIT.

if (event.type == SDL_QUIT) {
    printf("Fenêtre fermée par l'utilisateur\n");
    running = 0; // Variable de contrôle pour quitter la boucle
}

Étape 3 : Mettre en place une boucle de gestion des événements


Intégrez la gestion des événements dans une boucle principale pour répondre aux interactions utilisateur en temps réel.

int running = 1;
SDL_Event event;

while (running) {
    while (SDL_PollEvent(&event)) {
        if (event.type == SDL_QUIT) {
            running = 0;
        }
        if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) {
            running = 0; // Quitter le programme si Échap est pressé
        }
    }
    // Rafraîchir le rendu ici si nécessaire
}

Exemple complet


Voici un programme qui capture et affiche les entrées utilisateur dans la console :

#include <SDL2/SDL.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        printf("Erreur d'initialisation de SDL : %s\n", SDL_GetError());
        return 1;
    }

    SDL_Window *window = SDL_CreateWindow(
        "Gestion des entrées utilisateur",
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        800,
        600,
        SDL_WINDOW_SHOWN
    );

    if (!window) {
        printf("Erreur de création de la fenêtre : %s\n", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    int running = 1;
    SDL_Event event;

    while (running) {
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                running = 0;
            }

            if (event.type == SDL_KEYDOWN) {
                printf("Touche pressée : %s\n", SDL_GetKeyName(event.key.keysym.sym));
            }

            if (event.type == SDL_MOUSEBUTTONDOWN) {
                printf("Clic de souris à (%d, %d)\n", event.button.x, event.button.y);
            }
        }
    }

    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

Avec cette gestion des entrées utilisateur, vous pouvez ajouter des fonctionnalités interactives à votre jeu, comme le contrôle des personnages ou les interactions avec les objets. La prochaine étape consistera à intégrer une boucle principale pour orchestrer le déroulement du jeu.

Mise en place de la boucle principale du jeu

La boucle principale d’un jeu est le moteur qui contrôle le flux constant d’événements, de mises à jour, et de rendus. Dans cette section, nous allons voir comment structurer cette boucle pour gérer les différentes étapes du cycle de vie d’un jeu.

Étape 1 : Structure d’une boucle principale


La boucle principale doit suivre un modèle clair, souvent structuré comme suit :

  1. Récupération des entrées utilisateur.
  2. Mise à jour de l’état du jeu.
  3. Rendu des graphiques à l’écran.
int running = 1; // Contrôle l'exécution de la boucle
SDL_Event event;

while (running) {
    // 1. Gestion des entrées utilisateur
    while (SDL_PollEvent(&event)) {
        if (event.type == SDL_QUIT) {
            running = 0;
        }
    }

    // 2. Mise à jour de l’état du jeu
    // Exemple : déplacer un personnage ou vérifier des conditions
    updateGameState();

    // 3. Rendu graphique
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // Fond noir
    SDL_RenderClear(renderer); // Nettoyer le rendu
    renderGame(renderer); // Dessiner les objets du jeu
    SDL_RenderPresent(renderer); // Afficher à l’écran
}

Étape 2 : Gestion des entrées utilisateur


La gestion des entrées est essentielle pour capturer les commandes du joueur et les utiliser dans les mises à jour du jeu. Cela a été abordé dans la section précédente (a5).

Étape 3 : Mise à jour de l’état du jeu


Cette étape sert à actualiser les éléments du jeu, comme la position des objets, les vérifications de collisions, ou les états des personnages. Par exemple :

void updateGameState() {
    // Exemple : Mise à jour de la position d’un objet
    player.x += player.vx;
    player.y += player.vy;

    // Vérification des limites de la fenêtre
    if (player.x < 0 || player.x > WINDOW_WIDTH) {
        player.vx = -player.vx; // Rebondir
    }
}

Étape 4 : Rendu graphique


Cette étape consiste à dessiner les éléments du jeu à l’écran en utilisant le rendu configuré. Par exemple, pour dessiner un rectangle représentant un personnage :

void renderGame(SDL_Renderer *renderer) {
    SDL_Rect playerRect = {player.x, player.y, player.width, player.height};
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); // Rouge
    SDL_RenderFillRect(renderer, &playerRect); // Dessiner le rectangle
}

Étape 5 : Contrôler le taux de rafraîchissement


Pour éviter que la boucle ne s’exécute trop rapidement et consomme trop de ressources, utilisez un délai ou synchronisez avec le taux de rafraîchissement de l’écran :

const int FPS = 60; // Images par seconde
const int frameDelay = 1000 / FPS; // Temps par frame (en ms)

Uint32 frameStart;
int frameTime;

while (running) {
    frameStart = SDL_GetTicks(); // Début de la frame

    // 1. Gestion des entrées
    // 2. Mise à jour
    // 3. Rendu

    frameTime = SDL_GetTicks() - frameStart; // Temps écoulé pour la frame

    if (frameDelay > frameTime) {
        SDL_Delay(frameDelay - frameTime); // Délai pour atteindre 60 FPS
    }
}

Exemple complet

Voici un programme complet avec une boucle principale fonctionnelle :

#include <SDL2/SDL.h>
#include <stdio.h>

typedef struct {
    int x, y;
    int vx, vy;
    int width, height;
} Player;

Player player = {400, 300, 2, 2, 50, 50};

void updateGameState() {
    player.x += player.vx;
    player.y += player.vy;

    if (player.x < 0 || player.x + player.width > 800) {
        player.vx = -player.vx; // Rebondir horizontalement
    }
    if (player.y < 0 || player.y + player.height > 600) {
        player.vy = -player.vy; // Rebondir verticalement
    }
}

void renderGame(SDL_Renderer *renderer) {
    SDL_Rect playerRect = {player.x, player.y, player.width, player.height};
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); // Rouge
    SDL_RenderFillRect(renderer, &playerRect); // Dessiner le rectangle
}

int main(int argc, char *argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        printf("Erreur d'initialisation de SDL : %s\n", SDL_GetError());
        return 1;
    }

    SDL_Window *window = SDL_CreateWindow(
        "Boucle principale SDL",
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        800,
        600,
        SDL_WINDOW_SHOWN
    );

    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (!renderer) {
        printf("Erreur de création du rendu : %s\n", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    int running = 1;
    SDL_Event event;
    const int FPS = 60;
    const int frameDelay = 1000 / FPS;
    Uint32 frameStart;
    int frameTime;

    while (running) {
        frameStart = SDL_GetTicks();

        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                running = 0;
            }
        }

        updateGameState();

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // Fond noir
        SDL_RenderClear(renderer);
        renderGame(renderer);
        SDL_RenderPresent(renderer);

        frameTime = SDL_GetTicks() - frameStart;
        if (frameDelay > frameTime) {
            SDL_Delay(frameDelay - frameTime);
        }
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

Avec cette boucle, vous pouvez désormais ajouter des comportements interactifs et dynamiques dans votre jeu SDL. La prochaine étape sera l’intégration des sprites et la gestion des collisions.

Ajout de sprites et de collisions

L’intégration de sprites (images représentant des objets du jeu) et la gestion des collisions sont des étapes essentielles pour rendre votre jeu interactif et visuellement attractif. Cette section vous guide dans le chargement, l’affichage de sprites, et l’implémentation des collisions.


Étape 1 : Charger des sprites

SDL utilise la bibliothèque SDL_Image pour charger des fichiers d’images comme PNG, JPG, ou BMP. Installez SDL_Image si ce n’est pas déjà fait, puis incluez son en-tête :

#include <SDL2/SDL_image.h>

Charger une image en tant que texture :

SDL_Texture* loadTexture(const char* file, SDL_Renderer* renderer) {
    SDL_Surface* surface = IMG_Load(file); // Charger l’image
    if (!surface) {
        printf("Erreur de chargement de l'image : %s\n", IMG_GetError());
        return NULL;
    }
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
    SDL_FreeSurface(surface); // Libérer la surface après création de la texture
    return texture;
}

Exemple d’utilisation :

SDL_Texture* sprite = loadTexture("sprite.png", renderer);
if (!sprite) {
    // Gérer l'erreur
}

Étape 2 : Afficher un sprite

Pour dessiner un sprite, utilisez SDL_RenderCopy avec une texture et une position.

SDL_Rect destRect = {100, 100, 50, 50}; // Position et taille
SDL_RenderCopy(renderer, sprite, NULL, &destRect);
  • destRect détermine où et à quelle taille afficher l’image.
  • Passez NULL pour le second argument si vous voulez afficher l’intégralité de l’image source.

Exemple dans la boucle principale :

SDL_Rect playerRect = {player.x, player.y, player.width, player.height};
SDL_RenderCopy(renderer, sprite, NULL, &playerRect);

Étape 3 : Détecter des collisions

La détection des collisions est essentielle pour les interactions entre objets. Une méthode classique consiste à vérifier si deux rectangles se chevauchent.

Fonction pour vérifier les collisions :

int checkCollision(SDL_Rect a, SDL_Rect b) {
    return !(a.x + a.w <= b.x || // Droite de A avant gauche de B
             b.x + b.w <= a.x || // Droite de B avant gauche de A
             a.y + a.h <= b.y || // Bas de A avant haut de B
             b.y + b.h <= a.y);  // Bas de B avant haut de A
}

Exemple d’utilisation :

SDL_Rect obstacle = {200, 200, 50, 50};
if (checkCollision(playerRect, obstacle)) {
    printf("Collision détectée !\n");
}

Étape 4 : Exemple complet

Voici un programme intégrant le chargement, l’affichage de sprites, et la détection de collisions :

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <stdio.h>

typedef struct {
    int x, y;
    int vx, vy;
    int width, height;
} Player;

Player player = {100, 100, 2, 2, 50, 50};
SDL_Rect obstacle = {300, 300, 50, 50};

SDL_Texture* loadTexture(const char* file, SDL_Renderer* renderer) {
    SDL_Surface* surface = IMG_Load(file);
    if (!surface) {
        printf("Erreur de chargement de l'image : %s\n", IMG_GetError());
        return NULL;
    }
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
    SDL_FreeSurface(surface);
    return texture;
}

int checkCollision(SDL_Rect a, SDL_Rect b) {
    return !(a.x + a.w <= b.x || b.x + b.w <= a.x || a.y + a.h <= b.y || b.y + b.h <= a.y);
}

void updateGameState() {
    player.x += player.vx;
    player.y += player.vy;

    if (player.x < 0 || player.x + player.width > 800) {
        player.vx = -player.vx;
    }
    if (player.y < 0 || player.y + player.height > 600) {
        player.vy = -player.vy;
    }

    SDL_Rect playerRect = {player.x, player.y, player.width, player.height};
    if (checkCollision(playerRect, obstacle)) {
        printf("Collision détectée !\n");
    }
}

void renderGame(SDL_Renderer* renderer, SDL_Texture* sprite) {
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // Fond noir
    SDL_RenderClear(renderer);

    SDL_Rect playerRect = {player.x, player.y, player.width, player.height};
    SDL_RenderCopy(renderer, sprite, NULL, &playerRect);

    SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); // Obstacle jaune
    SDL_RenderFillRect(renderer, &obstacle);

    SDL_RenderPresent(renderer);
}

int main(int argc, char* argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO) != 0 || IMG_Init(IMG_INIT_PNG) == 0) {
        printf("Erreur d'initialisation : %s\n", SDL_GetError());
        return 1;
    }

    SDL_Window* window = SDL_CreateWindow("Sprites et Collisions", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (!window || !renderer) {
        printf("Erreur de création : %s\n", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    SDL_Texture* sprite = loadTexture("sprite.png", renderer);
    if (!sprite) {
        SDL_DestroyRenderer(renderer);
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    int running = 1;
    SDL_Event event;

    while (running) {
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                running = 0;
            }
        }

        updateGameState();
        renderGame(renderer, sprite);
    }

    SDL_DestroyTexture(sprite);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

Avec ces techniques, vous pouvez intégrer des sprites et gérer les interactions entre objets dans votre jeu. La prochaine étape consistera à conclure et préparer le jeu pour une version plus avancée.

Conclusion

Dans cet article, nous avons exploré les étapes fondamentales pour créer un jeu 2D rétro en C avec SDL. Nous avons couvert :

  1. Installation et configuration de SDL, permettant de configurer l’environnement de développement.
  2. Initialisation de SDL, avec la création d’une fenêtre et d’un rendu graphique.
  3. Gestion des entrées utilisateur, offrant une interaction dynamique avec le joueur.
  4. Mise en place de la boucle principale, orchestrant le cycle du jeu.
  5. Ajout de sprites et gestion des collisions, donnant vie aux objets du jeu et facilitant les interactions entre eux.

Ce guide vous a fourni une base solide pour développer un jeu fonctionnel avec SDL. Pour aller plus loin, vous pouvez :

  • Intégrer des animations pour vos sprites.
  • Ajouter des sons et une musique de fond à l’aide du module audio de SDL.
  • Implémenter un système de score ou des niveaux de difficulté.
  • Optimiser les performances en utilisant des techniques avancées de rendu et de gestion des ressources.

Grâce à SDL, les possibilités de création sont vastes. En combinant vos compétences en programmation C et les outils offerts par SDL, vous êtes bien équipé pour concevoir des jeux captivants et uniques. Bonne programmation !

Sommaire