IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Allegro 5

Programmation de jeux en C ou C++


précédentsommairesuivant

XI. Décor, monde

XI-A. Introduction

Ce chapitre montre comment faire défiler une grande image qui dépasse la taille de l'écran (scroll). Ensuite il présente la technique classique de cartographie d'images (tile mapping). C'est intéressant de la connaître notamment parce qu'elle peut inspirer des moyens de constituer des décors à la volée pendant le fonctionnement du programme.

XI-B. Deux principes de décor : continu ou discontinu

Le décor du jeu peut être envisagé soit sous forme continue, soit sous forme discontinue.

XI-B-1. La forme continue : une image, des calques, des rails

Au départ, le monde est tout simplement un unique dessin qui tient sur une seule bitmap. Il est sophistiqué, constitué de montagnes, rivières, maisons, prairies, etc. En général il est en million de couleurs (32 bits) et il n'est pas possible de se repérer dedans en s'appuyant sur les pixels constitutifs de l'image.

Alors pour pouvoir s'y repérer, l'idée est de superposer à ce monde des calques invisibles avec des couleurs codées. Par exemple, un calque en noir et blanc dessine en noir l'ombre d'une rivière. Le programme sait alors si un personnage est dans l'eau lorsque sa position coïncide avec des pixels noirs du calque rivière. La technique consiste à disposer de plusieurs calques et il est envisageable d'ajouter sous l'image une infrastructure de rails colorés qui donne des chemins possibles dans ce monde. En fait toutes sortes d'informations peuvent transiter par ces calques. Nous avons déjà évoqué ce point dans le chapitre Modèle de jeu simple, section Collisions - Recherche d'une couleur sur un fondRecherche d'une couleur sur un fond.

XI-B-2. La forme discontinue : technique du « tile mapping »

Le décor repose là sur une série relativement limitée de petites images prédécoupées. Il est construit en additionnant et en multipliant ces petites images de sorte que l'on obtienne une cartographie de petites images. À chaque petite image est associé un nombre, et l'ensemble de la composition est stocké dans une matrice de nombres. Toute l'algorithmique relative aux déplacements des personnages et aux rapports à leur monde se réfère alors à la matrice des nombres sous-jacents mais pas directement à l'image visible du décor.

Quelle que soit l'orientation choisie, continue ou discontinue pour la conception d'un monde, il y a un tronc commun de principes algorithmiques pour résoudre des problèmes de chemins et de recherche de chemin, et souvent la possibilité d'adapter des solutions d'une approche à l'autre.

XI-C. Défilement d'une grande image (scroll)

Le programme ci-dessous crée une fenêtre de 640 par 480. Le fond est constitué par une image de 1600 par 1200. La partie visible du fond dans la fenêtre du programme est toujours une région de l'image de fond qui a la taille de la fenêtre.

Pour être accessible facilement, la taille de la fenêtre est stockée avec deux variables globales :

 
Sélectionnez
int SCREENX = 640 ;
int SCREENY = 480 ;

La position dans l'image de fond varie selon les touches flèches : haute et basse pour les déplacements verticaux, gauche et droite pour les déplacements horizontaux. Cette position est stockée dans un couple de variables :

 
Sélectionnez
int x,y ;

Au départ, l'image de fond est calée sur le coin haut-gauche de l'image avec x et y égaux à 0.

La partie visible du fond est toujours la région qui part de la position (x,y) de l'image de fond selon la largeur SCREENX et la hauteur SCREENY de la fenêtre. L'affichage est assuré par la fonction al_draw_bitmap_region() avec l'appel :

 
Sélectionnez
al_draw_bitmap_region(fond, // l'image de fond
                      x, y, // position dans l'image de fond
                      SCREENX, SCREENY, // taille dans l'image de fond
                      0, 0, // position de destination
                            // (dans la fenêtre)
                      0); // pas de permutation

Pour le déplacement un pas est spécifié avec une variable :

 
Sélectionnez
int pas = 5 ;

Et selon les flèches appuyées, la position x,y dans le fond est modifiée. Il faut un contrôle des bords afin que le fond couvre en permanence la fenêtre. Il ne doit s'échapper par aucun côté. Le contrôle horizontal est le suivant :

 
Sélectionnez
if (al_key_down(&key, ALLEGRO_KEY_LEFT))
x = (x - pas < 0) ? 0 : x - pas;

Vers la gauche, si x-pas reste supérieur ou égal à 0, l'opération est faite et x vaut x-pas. Sinon x est bloqué à 0.

 
Sélectionnez
if (al_key_down(&key, ALLEGRO_KEY_RIGHT))
x =(x+pas > tx-SCREENX) ? tx-SCREENX : x+pas;

Vers la droite, si le déplacement laisse suffisamment d'images de fond pour couvrir toute la fenêtre, c'est-à-dire si x+pas reste inférieur ou égal à la taille de l'image de fond moins la taille de la fenêtre, alors l'opération x+pas est effectuée. Sinon la position est bloquée de façon à ce que la fenêtre soit couverte par l'image de fond, et x vaut la largeur de l'image de fond moins la largeur de la fenêtre :

 
Sélectionnez
x=tx-SCREENX

Le contrôle vertical obéit au même principe. Voici le code complet du programme :

Défilement d'une grande image (scroll)
Sélectionnez
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
#include <allegro5/allegro_image.h>
#include <time.h>

int SCREENX = 640;
int SCREENY = 480;

ALLEGRO_DISPLAY* init_allegro();
void erreur(const char*txt);
/*****************************************************************
*****************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    ALLEGRO_KEYBOARD_STATE key;

    // la grande image de fond
    ALLEGRO_BITMAP*fond;
    // position et taille de l'image
    int x, y, tx, ty;
    // le déplacement
    int pas;

    display = init_allegro();

    // l'image est dans le répertoire du programme
    fond = al_load_bitmap("grande_image.bmp");
    if (!fond)
        erreur("al_load_bitmap()");
    
    tx = al_get_bitmap_width(fond);
    ty = al_get_bitmap_height(fond);

    // au départ l'image est calée sur le coin 
    // haut-gauche
    x = y = 0;
    pas = 5;
    do{
        al_get_keyboard_state(&key);
        
        // déplacement
        if (al_key_down(&key, ALLEGRO_KEY_LEFT))
            x = (x - pas < 0) ? 0 : x - pas;

        if (al_key_down(&key, ALLEGRO_KEY_RIGHT))
            x = (x + pas > tx - SCREENX) ? tx - SCREENX : x + pas;

        if (al_key_down(&key, ALLEGRO_KEY_UP))
            y = (y - pas < 0) ? 0: y-pas;

        if (al_key_down(&key, ALLEGRO_KEY_DOWN))
            y = (y + pas > ty - SCREENY) ? ty - SCREENY : y + pas;

        // affichage
        al_draw_bitmap_region(fond, x, y, SCREENX, SCREENY, 0, 0, 0);
        al_flip_display();
        al_rest(1 / 30.0);
        
    } while (!al_key_down(&key, ALLEGRO_KEY_ESCAPE));

    al_destroy_display(display);
    return 0;
}
/*****************************************************************
*****************************************************************/
ALLEGRO_DISPLAY* init_allegro()
{
ALLEGRO_DISPLAY*display;
    if (!al_init())
        erreur("al_init()");

    if (!al_install_keyboard())
        erreur("al_install_keyboard()");

    if (!al_init_image_addon())
        erreur("al_init_image_addon()");

    display = al_create_display(SCREENX, SCREENY);
    if (!display)
        erreur("al_create_display()");
    return display;
}
/*****************************************************************
*****************************************************************/
void erreur(const char*txt)
{
    ALLEGRO_DISPLAY*d;
    d = al_is_system_installed() ? al_get_current_display() : NULL;
    al_show_native_message_box(d, "ERREUR", txt, NULL, NULL, 0);
    exit(EXIT_FAILURE);
}
/*****************************************************************
*****************************************************************/

XI-D. Un univers composé de petites images (tile mapping)

Littéralement tile mapping signifie cartographie de tuiles, tile signifie carrelage et nous entrons dans un monde discontinu réalisé à partir d'échantillons. En informatique, c'est une technique utilisée dans la création de jeu pour obtenir des décors, des terrains. Au départ, on dispose d'un ensemble de petites images qui sont les tuiles et il s'agit de composer un monde avec elles. Voici un ensemble de tuiles :

Image non disponible

Chaque carreau est une tuile. En les disposant adroitement, on peut composer un monde avec elles. Remarquons au passage qu'il s'agit à l'origine d'un procédé de compression. L'ensemble des petites images qui permet de composer des mondes pèse moins lourd en mémoire qu'une grande image.

Voici par exemple un extrait de monde construit avec des tuiles, les tuiles sont apparentes en pointillés :

Image non disponible

Voici un autre exemple de monde en cours de construction dans l'éditeur libre de tile map « Tiled » que vous pouvez trouver à l'adresse : http://www.mapeditor.org/

Image non disponible

Et un dernier exemple d'édition toujours avec le même éditeur de map « Tiled » :

Image non disponible

Nous pouvons constater qu'il existe des ensembles de tuiles sophistiqués qui permettent de créer des décors très aboutis dans lesquels les tuiles servant à la construction ne sont pas clairement apparentes et pas facilement identifiables.

Pour la partie informatique, nous allons voir comment dans le code agencer et manipuler ces tuiles et ensuite comment déplacer des personnages dans ce type de mondes.

XI-D-1. Décor continu aléatoire

Notre première expérimentation consiste à créer un monde aléatoire à partir d'un ensemble de tuiles. Certes le monde obtenu n'est en l'occurrence pas vraiment cohérent. Mais l'idée est intéressante dans une perspective hybride avec un monde continu. En effet il est possible de créer des mondes cohérents relativement facilement à partir de gros carreaux.

En fait notre programme reprend le programme précédent qui fait défiler une grande image (scroll) et la seule différence est que la grande image est créée à la volée au lancement du programme à partir d'un ensemble de tuiles.

Toutes les tuiles doivent avoir la même taille, elles sont rectangulaires et agencées comme dans un tableau sur un fichier unique exactement comme les sprites d'animations. Nous utilisons la fonction recup_sprite expliquée précédemment (cf. section Récupération d'une série d'images sur un fichier unique du chapitre Animation, spritesAnimations, sprites) pour récupérer chaque carreau.

Voici notre ensemble de tuiles sur le fichier kit_de_tuiles.png qui doit se trouver dans le répertoire du programme :

Image non disponible

L'algorithme consiste à :

  • Créer la grande bitmap pour le décor.
  • La parcourir en se déplaçant par pas de carreaux et à chaque pas ajouter un nouveau carreau pris au hasard.

Soit tx et ty la taille totale du décor en pixels, TX et TY la taille d'un carreau en pixels, all_tuiles la bitmap de l'ensemble des tuiles, NB_COL le nombre de colonnes et NB_ELEMENT le nombre de carreaux, nous avons :

 
Sélectionnez
for (y = 0; y < ty; y += TY){
    for (x = 0; x < tx; x += TX){
        ALLEGRO_BITMAP*tuile = recup_sprite(all_tuiles,TX+1,TY+1,0,0,
                                            NB_COL,rand()%NB_ELEMENT);
        if (!tuile)
            erreur("recup_sprite()");
        al_set_target_bitmap(decor);
        al_draw_bitmap(tuile, x, y, 0);
        al_destroy_bitmap(tuile);
    }
}

Le déplacement en y est de TY pixels par pas et en x de TX pixels par pas. La récupération est ordinaire. Il faut toutefois ajouter 1 à TX et TY parce que les carreaux sont sur un quadrillage chacun isolé par une ligne de 1 pixel d'épaisseur. Le dernier paramètre est pour le numéro d'élément et il est tiré au hasard parmi l'ensemble.

Une fois un carreau récupéré, la bitmap tuile correspondante est copiée à la position x , y dans la grande bitmap decor qui constitue pour la suite du programme le décor et le monde du jeu.

Pour le reste le programme est identique à celui vu pour le défilement d'une grande image. Voici le programme complet :

Fabriquer un décor aléatoire
Sélectionnez
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
#include <allegro5/allegro_image.h>
#include <time.h>

#define    SCREENX 640
#define SCREENY 480

// taille d'une tuile
#define TX        32
#define TY        32
// nombre de colonnes pour les tuiles dans le fichier
#define NB_COL 10
// nombre de tuiles dans le fichier
#define NB_ELEMENT 39

ALLEGRO_DISPLAY*    init_allegro    (void);
ALLEGRO_BITMAP*        recup_sprite    (ALLEGRO_BITMAP*scr,
                                    int tx, int ty,
                                    int startx, int starty,
                                    int colonne, int i);
void                erreur            (const char*txt);
/*****************************************************************
*****************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    ALLEGRO_KEYBOARD_STATE key;

    // la grande image de décor
    ALLEGRO_BITMAP*decor;
    // position et taille du décor
    int x, y, tx, ty;
    // le déplacement du décor
    int pas;
    // récupération du fichier de tuiles
    ALLEGRO_BITMAP*all_tuiles;

    display = init_allegro();

    // l'image est dans le répertoire du programme
    decor = al_create_bitmap(TX*100, TY*64);
    if (!decor)
        erreur("al_create_bitmap()");
    tx = al_get_bitmap_width(decor);
    ty = al_get_bitmap_height(decor);

    // récupération du fichier de tuiles
    all_tuiles = al_load_bitmap("kit_de_tuiles.png");
    if (!all_tuiles)
        erreur("al_load_bitmap(\"kit_de_tuiles.png\")");
    
    //création d'un décor aléatoire de tuiles
    for (y = 0; y < ty; y += TY){
        for (x = 0; x < tx; x += TX){
            ALLEGRO_BITMAP*tuile = recup_sprite(
                all_tuiles, // le fichier
                TX+1, TY+1,// taille tuile+separation de 1 pixel
                0, 0,// destination dans bitmap tuile
                NB_COL,//nombre de colonnes
                rand() % NB_ELEMENT); // élément au hasard
            if (!tuile)
                erreur("recup_sprite()");
            al_set_target_bitmap(decor);
            al_draw_bitmap(tuile, x, y, 0);
            al_destroy_bitmap(tuile);
        }
    }
    // retour affichage dans la fenêtre 
    al_set_target_backbuffer(display);

    // au départ l'image est calée sur le coin 
    // haut-gauche
    x = y = 0;
    pas = 5;
    do{
        al_get_keyboard_state(&key);

        // déplacement
        if (al_key_down(&key, ALLEGRO_KEY_LEFT))
            x = (x - pas < 0) ? 0 : x - pas;

        if (al_key_down(&key, ALLEGRO_KEY_RIGHT))
            x = (x + pas > tx - SCREENX) ? tx - SCREENX : x + pas;

        if (al_key_down(&key, ALLEGRO_KEY_UP))
            y = (y - pas < 0) ? 0 : y - pas;

        if (al_key_down(&key, ALLEGRO_KEY_DOWN))
            y = (y + pas > ty - SCREENY) ? ty - SCREENY : y + pas;

        // affichage
        al_draw_bitmap_region(decor, x, y, SCREENX, SCREENY, 0, 0, 0);
        al_flip_display();
        al_rest(1 / 30.0);

    } while (!al_key_down(&key, ALLEGRO_KEY_ESCAPE));

    al_destroy_display(display);
    return 0;
}
/*****************************************************************
*****************************************************************/
ALLEGRO_DISPLAY* init_allegro()
{
    ALLEGRO_DISPLAY*display;
    if (!al_init())
        erreur("al_init()");

    if (!al_install_keyboard())
        erreur("al_install_keyboard()");

    if (!al_init_image_addon())
        erreur("al_init_image_addon()");

    display = al_create_display(SCREENX, SCREENY);
    if (!display)
        erreur("al_create_display()");
    return display;
}
/*****************************************************************
Récupérer les tuiles
*****************************************************************/
ALLEGRO_BITMAP*recup_sprite(
    ALLEGRO_BITMAP*scr, // bitmap d'origine
    int tx, int ty, // taille élément
    int startx, int starty, // à partir de
    int colonne, // nombre de colonnes
    int i) // ieme élément
{
    ALLEGRO_BITMAP*sprite = NULL;
    int x, y;
    sprite = al_create_bitmap(tx, ty);
    if (sprite != NULL){
        // attention colonne doit être > 0
        x = startx + (i%colonne)*tx;
        y = starty + (i / colonne)*ty;

        al_set_target_bitmap(sprite);
        al_draw_bitmap_region(scr, x, y, tx, ty, 0, 0, 0);
    }
    return sprite;
}
/*****************************************************************
*****************************************************************/
void erreur(const char*txt)
{
    ALLEGRO_DISPLAY*d;
    d = al_is_system_installed() ? al_get_current_display() : NULL;
    al_show_native_message_box(d, "ERREUR", txt, NULL, NULL, 0);
    exit(EXIT_FAILURE);
}
/*****************************************************************
*****************************************************************/
XI-D-1-a. Capture d'écran
Image non disponible

XI-D-2. Utiliser une matrice de nombres

Dans un ensemble de tuiles, chaque tuile est numérotée. Dans l'exemple précédent de construction aléatoire, le numéro de chaque tuile est son numéro d'ordre sur le fichier. Pour avoir un monde jouable et cohérent, nous allons dessiner le terrain à partir de sa représentation dans un tableau de nombres à une ou deux dimensions.

Soit par exemple les tuiles :

Image non disponible

Voici une composition de terrain dans un tableau à une dimension :

 
Sélectionnez
#define MAPX 10 // nombre de tuiles horizontales
#define MAPY 10 // nombre de tuiles verticales
int map[MAPX*MAPY]={
    0,0,0,0,0,0,0,0,0,0,
    0,1,1,1,8,8,1,2,2,0,
    0,2,2,1,6,6,6,2,2,0,
    0,2,2,1,2,3,3,2,2,0,
    0,2,2,1,1,3,3,2,2,0,
    0,2,2,2,2,3,3,2,2,0,
    0,2,2,2,3,2,2,0,2,0,
    0,2,2,2,3,2,2,2,2,0,
    0,2,2,2,2,0,2,2,2,0,
    0,0,0,0,0,0,0,0,0,0
};

0 dans le tableau correspond à une tuile 0.

1 dans le tableau correspond à une tuile 1, etc.

Le décor va être généré en parcourant le tableau et en ajustant dans une grande bitmap chacune des tuiles qui le constitue. Dans le programme, le décor repose sur ce tableau et les interactions des personnages avec le terrain vont s'y référer. L'image du décor n'est pas nécessaire à la gestion des collisions avec le décor. C'est un monde discontinu.

Nous allons expérimenter comment construire l'image d'un terrain à partir d'un tableau. Notre terrain dépasse la taille de l'écran et un défilement avec les flèches est implémenté (scroll).

De même que pour la création d'un décor aléatoire, toutes les tuiles sont organisées en lignes et colonnes rigoureusement sur un même fichier. Pour les récupérer à l'aide de la fonction recup_sprite() nous avons :

  • La taille de chaque tuile :
 
Sélectionnez
#define TX 32
#define TY 32
  • Le nombre de colonnes dans lesquelles elles sont rangées :
 
Sélectionnez
#define NB_COL 10
  • Le nombre total de tuiles différentes :
 
Sélectionnez
#define NB_ELEMENT 39

Pour le défilement du décor, le pas est fixé de la façon suivante :

 
Sélectionnez
#define PAS 8

Le tableau pour le terrain nécessite le nombre de tuiles pour l'horizontale et le nombre de tuiles pour la verticale. Ce sont les macros constantes :

 
Sélectionnez
#define MAPX 25
#define MAPY 25

et le tableau de notre décor est le suivant :

 
Sélectionnez
int map[MAPX*MAPY]={
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,3,3,3,3,2,2,2,2,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,3,3,3,3,2,2,2,2,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,1,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,1,1,1,1,2,2,1,1,1,2,2,2,2,2,0,
    0,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,0,
    0,2,2,2,2,2,1,1,1,1,1,1,2,2,1,2,1,2,1,2,2,2,2,2,0,
    0,2,2,2,2,1,1,1,1,1,1,2,2,2,8,9,1,1,1,1,1,1,1,2,0,
    0,2,2,2,1,1,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,0,
    0,2,2,1,1,1,1,1,8,8,8,8,2,8,8,8,8,8,8,8,9,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
    0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

Nous avons besoin également de la taille résultante en pixels afin de générer l'image correspondant au terrain programmé dans le tableau :

 
Sélectionnez
#define DECORX TX*MAPX // largeur
#define DECORY TY*MAPY // hauteur

En l'occurrence, un #define est bien pratique. Il suffit de modifier la taille du terrain pour que tout ce qui dans le programme en dépend s'adapte automatiquement.

Pour ce qui est de la composition de l'image du décor, l'algorithme est le même que pour une composition aléatoire sauf que les numéros des tuiles sont donnés par le tableau de sorte que la modification est mineure :

 
Sélectionnez
for (y = 0, pos = 0; y < DECORY; y += TY){
    for (x = 0; x < DECORX; x += TX){
        ALLEGRO_BITMAP*tuile = recup_sprite(
                    all_tuiles,
                    TX + 1, TY + 1,
                    0, 0,
                    NB_COL,
                    MAP[pos++]); // choix élément
        if (!tuile)
            erreur("recup_sprite()");
        al_set_target_bitmap(decor);
        al_draw_bitmap(tuile, x, y, 0);
        al_destroy_bitmap(tuile);
    }
}

Une variable pos prend dans l'ordre toute les positions du tableau de tuiles et MAP[pos] donne à chaque fois le numéro de la tuile à prendre. Attention à la post incrémentation ++, dans l'expression le nombre est pris d'abord pour le calcul et il est incrémenté après. Ici c'est pratique, pos s'incrémente à chaque tour après utilisation.

Voici le code complet de l'expérimentation avec en gras ce qui est différent par rapport au programme précédent de génération aléatoire :

Fabriquer un décor à partir d'une matrice de nombres
Sélectionnez
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
#include <allegro5/allegro_image.h>
#include <time.h>

#define SCREENX        640
#define SCREENY        480

// TILES
#define TX            32// la taille de chaque tiles (tuiles ou carreaux)
#define TY            32
#define NB_COL 10// nombre de colonnes dans l'image globale
#define NB_ELEMENT 39// nombre total de carreaux dans l'image globale
#define PAS 8 // pas d'avancement du scroll

//MAP
#define MAPX 25 // taille du terrain en nombre de tiles horizontales
#define MAPY 25 // taille du terrain en nombre de tiles verticales
#define DECORX (TX*MAPX) // largeur du terrain en pixels
#define DECORY (TY*MAPY) // hauteur du terrain en pixels

// exemple de décor pour le principe
int MAP[MAPX*MAPY] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 1, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 8, 9, 1, 1, 1, 1, 1, 1, 1, 2, 0,
    0, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0,
    0, 2, 2, 1, 1, 1, 1, 1, 8, 8, 8, 8, 2, 8, 8, 8, 8, 8, 8, 8, 9, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

ALLEGRO_DISPLAY* init_allegro(void);
ALLEGRO_BITMAP*     recup_sprite(ALLEGRO_BITMAP*scr,
                             int tx, int ty,
                             int startx, int starty,
                             int colonne, int i);
void             erreur         (const char*txt);
/*****************************************************************
*****************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    ALLEGRO_KEYBOARD_STATE key;

    // la grande image de décor
    ALLEGRO_BITMAP*decor;
    // position du décor
    int x, y;
    // le déplacement du décor
    int pas;
    // récupération du fichier de tuiles
    ALLEGRO_BITMAP*all_tuiles;
    int pos; 

    display = init_allegro();

    // l'image est dans le répertoire du programme
    decor = al_create_bitmap(DECORX, DECORY);
    if (!decor)
        erreur("al_create_bitmap()");
    
    // récupération du fichier de tuiles
    all_tuiles = al_load_bitmap("kit_de_tuiles.png");
    if (!all_tuiles)
        erreur("al_load_bitmap(\"kit_de_tuiles.png\")");

    //création d'un décor aléatoire de tuiles
    for (y = 0, pos = 0; y < DECORY; y += TY){
        for (x = 0; x < DECORX; x += TX){
            ALLEGRO_BITMAP*tuile = recup_sprite(
                all_tuiles,
                TX + 1, TY + 1,
                0, 0,
                NB_COL,
                MAP[pos++]); // élément 
            if (!tuile)
                erreur("recup_sprite()");
            al_set_target_bitmap(decor);
            al_draw_bitmap(tuile, x, y, 0);
            al_destroy_bitmap(tuile);
        }
    }
    // retour affichage dans la fenêtre 
    al_set_target_backbuffer(display);

    // au départ l'image est calée sur le coin 
    // haut-gauche
    x = y = 0;
    pas = 5;
    do{
        al_get_keyboard_state(&key);

        // déplacement
        if (al_key_down(&key, ALLEGRO_KEY_LEFT))
            x = (x - pas < 0) ? 0 : x - pas;

        if (al_key_down(&key, ALLEGRO_KEY_RIGHT))
            x = (x + pas >DECORX - SCREENX) ? DECORX - SCREENX : x + pas;

        if (al_key_down(&key, ALLEGRO_KEY_UP))
            y = (y - pas < 0) ? 0 : y - pas;

        if (al_key_down(&key, ALLEGRO_KEY_DOWN))
            y = (y + pas > DECORY - SCREENY) ? DECORY - SCREENY : y + pas;

        // affichage
        al_draw_bitmap_region(decor, x, y, SCREENX, SCREENY, 0, 0, 0);
        al_flip_display();
        al_rest(1 / 30.0);

    } while (!al_key_down(&key, ALLEGRO_KEY_ESCAPE));

    al_destroy_bitmap(decor);
    al_destroy_display(display);
    return 0;
}
/*****************************************************************
*****************************************************************/
ALLEGRO_DISPLAY* init_allegro()
{
    ALLEGRO_DISPLAY*display;
    if (!al_init())
        erreur("al_init()");

    if (!al_install_keyboard())
        erreur("al_install_keyboard()");

    if (!al_init_image_addon())
        erreur("al_init_image_addon()");

    display = al_create_display(SCREENX, SCREENY);
    if (!display)
        erreur("al_create_display()");
    return display;
}
/*****************************************************************
Récupérer les tuiles
*****************************************************************/
ALLEGRO_BITMAP*recup_sprite(
    ALLEGRO_BITMAP*scr, // bitmap d'origine
    int tx, int ty, // taille élément
    int startx, int starty, // à partir de
    int colonne, // nombre de colonnes
    int i) // ieme élément
{
    ALLEGRO_BITMAP*sprite = NULL;
    int x, y;
    sprite = al_create_bitmap(tx, ty);
    if (sprite != NULL){
        // attention colonne doit être > 0
        x = startx + (i%colonne)*tx;
        y = starty + (i / colonne)*ty;

        al_set_target_bitmap(sprite);
        al_draw_bitmap_region(scr, x, y, tx, ty, 0, 0, 0);
    }
    return sprite;
}
/*****************************************************************
*****************************************************************/
void erreur(const char*txt)
{
    ALLEGRO_DISPLAY*d;
    d = al_is_system_installed() ? al_get_current_display() : NULL;
    al_show_native_message_box(d, "ERREUR", txt, NULL, NULL, 0);
    exit(EXIT_FAILURE);
}
/*****************************************************************
*****************************************************************/
XI-D-2-a. Capture d'écran
Image non disponible

XI-D-3. Composer le décor avec un éditeur graphique

Dessiner un décor en manipulant des nombres limite grandement les possibilités graphiques. Pour arriver à quelque chose d'intéressant, il est nécessaire d'utiliser un éditeur graphique. Il existe plusieurs éditeurs libres de droits pour cartographier des décors à partir de tuiles (tile map editor).

Nous avons repéré :

XI-D-3-a. Tiled Map Editor

Il est téléchargeable sur le site http://www.mapeditor.org/.

Voici une capture d'écran qui donne une idée de l'utilisation :

Image non disponible

Dans la grande fenêtre de gauche se trouve le terrain en cours d'élaboration à partir de l'ensemble de tuiles qui se trouve sur la droite. Il y a plusieurs possibilités de sauvegarde, dont une sauvegarde au format texte (.txt) qui permet très facilement de récupérer le terrain sous la forme d'un tableau de nombre dans son programme. En général c'est un tableau d'entiers à deux dimensions. Pour faire très simple, il suffit d'ouvrir le fichier texte obtenu et de copier-coller dans son programme le tableau généré par l'éditeur.

Bien sûr un éditeur offre beaucoup de possibilités et il est rapidement utile de se tourner vers la documentation fournie avec. Tiled semble très utilisé, voici un tutoriel qui a l'air bien pour démarrer :

http://gamedevelopment.tutsplus.com/tutorials/introduction-to-tiled-map-editor-a-great-platform-agnostic-tool-for-making-level-maps--gamedev-2838

XI-D-3-b. Mappy Editor 1.4.23

Autre éditeur très convenable Mappy Editor actuellement dans sa version 1.4.23. Il est téléchargeable sur le site http://tilemap.co.uk/mappy.php. Le principe de base est proche du précédent : sur la gauche une fenêtre avec le terrain en construction et sur la droite l'ensemble des tuiles. Pour obtenir un tableau de nombres en texte (.txt) il faut utiliser la commande Export as text dans le menu File.

Image non disponible

Obtenir ce livre

Image non disponible

Ce document est une retranscription autorisée du livre Allegro 5 - Programmation de jeux en C et C++ écrit par Frédéric DROUILLON. Initialement publié aux éditions ENI, vous pouvez commander le livre sur le site de l'éditeur.


précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2015 Frédéric DROUILLON. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.