Allegro 5

Programmation de jeux en C ou C++


précédentsommairesuivant

III. Premier pas

III-A. Introduction

Les premiers pas que nous proposons consistent à disposer rapidement d'une application en mode fenêtre ou en plein écran capable de capturer le clavier et la souris. Dans notre optique d'exploration, nous en profitons pour expérimenter les fonctions de redimensionnement et de positionnement d'une fenêtre avec les différentes possibilités de plein écran et la capture des résolutions supportées.

Compte tenu du nombre relativement important d'informations nécessaires pour commencer un programme, nous proposons également de réaliser un modèle d'application (template) qui nous évitera d'avoir à tout reprendre pour chaque nouvelle création.

Pour finir, nous apportons quelques précisions d'une part pour un meilleur contrôle du clavier et de la souris et d'autre part pour l'utilisation de la fenêtre console. Allegro dispose en effet d'une fenêtre console native et donc multiplateforme qu'il peut être intéressant d'utiliser en alternative à la console du système.

III-B. Créer une fenêtre

Créez un nouveau projet et répétez les opérations vues au chapitre Installation de la bibliothèque Allegro : créer un projet, spécifier les chemins d'accès aux dossiers include et lib de la bibliothèque, relier au projet les modules qui sont utilisés.

Ensuite le code de départ est le suivant :

Créer une fenêtre
Sélectionnez
#include <stdlib.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>

// fonction contrôle d'erreur
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);
}
/*************************************************************
*************************************************************/
int main()
{
    // pour obtenir une fenêtre
    ALLEGRO_DISPLAY *display;

    // 1) initialisation Allegro obligatoire
    if(!al_init())
        erreur("al_init()");

    // 2) créer sa fenêtre
    display=al_create_display(800,600);
    if(!display)
        erreur("creation display");

    //donner un nom à sa fenêtre
    al_set_window_title( display, "Premier programme !");

    // petit test : colorer la fenêtre en rouge
    al_clear_to_color(al_map_rgb(255,0,0));

    // pour rendre visibles toutes les opérations de dessin ou
    // d'affichage il faut basculer le double buffer à l'écran.
    // En effet tous les affichages sont faits d'abord dans un
    // double buffer, c'est-à-dire une image en mémoire
    // invisible à l'écran
    al_flip_display();

    // temps d'attente en secondes
    al_rest(5.0);

    // libérer la mémoire allouée pour la fenêtre
    al_destroy_display(display);

    return 0;
}
/*************************************************************
*************************************************************/

La structure de données pour obtenir une fenêtre est :

 
Sélectionnez
ALLEGRO_DISPLAY *display ;

L'initialisation de la fenêtre se fait avec la fonction create_display() . Elle prend en paramètre la taille que l'on veut lui donner et retourne un pointeur ALLEGRO_DISPLAY* correctement alloué et NULL sinon :

 
Sélectionnez
ALLEGRO_DISPLAY* al_create_display(int w, int h)

Nous pouvons lui donner un nom avec la fonction :

 
Sélectionnez
void al_set_window_title(ALLEGRO_DISPLAY*display, const char *title)

Pour tester l'affichage, nous changeons la couleur du fond de la fenêtre avec un appel à la fonction :

 
Sélectionnez
void al_clear_to_color(ALLEGRO_COLOR color)

Cette fonction prend une couleur en paramètre. Une couleur est une structure ALLEGRO_COLOR et la fonction :

 
Sélectionnez
ALLEGRO_COLOR al_map_rgb( unsigned char r, // pour le rouge
                         unsigned char g, // pour le vert
                         unsigned char b) // pour le bleu

retourne une couleur initialisée avec une valeur de rouge, de vert, de bleu comprise chacune entre 0 et 255 maximum.

La couleur définie dans l'appel de al_map_rgb(255,0,0) est maximum de rouge, la couleur retournée au paramètre de la fonction al_clear_to_color() est donc rouge vif.

Ensuite, très important, il y a un appel à la fonction :

 
Sélectionnez
void al_flip_display(void)

Cette fonction permet l'affichage à l'écran du double buffer. Le double buffer est une image en mémoire de la taille de l'écran dans laquelle sont faites d'abord toutes les opérations graphiques d'affichage ou de dessin. Une fois ces opérations terminées, pour en voir le résultat il faut les afficher à l'écran, c'est le rôle de cette fonction : elle recopie le double buffer à l'écran (ou opère une bascule double buffer écran).

Nous avons modifié la fonction de contrôle d'erreur parce qu'à partir du moment où il y a des fenêtres dans le programme il est préférable de récupérer le display courant pour la fenêtre message surtout en cas de plein écran (Fullscreen). Sinon elle reste cachée derrière et impossible à atteindre. Elle bloque alors le programme parce qu'on ne peut pas cliquer sur ok pour la fermer.

III-C. Obtenir le clavier

Le clavier est en général géré avec une boucle d'événements mais il y a un moyen rapide et simple de pouvoir en disposer.

D'abord appeler la fonction d'initialisation :

 
Sélectionnez
bool al_install_keyboard(void)

Cette fonction retourne true sur succès et false sur échec. Ensuite, déclarer une structure d'état du clavier :

 
Sélectionnez
ALLEGRO_KEYBOARD_STATE key;

L'état du clavier y est stocké après chaque appel à la fonction :

 
Sélectionnez
void al_get_keyboard_state(ALLEGRO_KEYBOARD_STATE *ret_state)

Cette fonction retourne dûment complétée la structure passée en paramètre par référence (adresse mémoire). Et pour savoir si une touche a été pressée, il faut utiliser la fonction :

 
Sélectionnez
al_key_down(const ALLEGRO_KEYBOARD_STATE *state, int keycode)

Elle prend en paramètre la structure d'état du clavier passée par référence et une macro constante qui définit une touche du clavier. Pour chaque touche du clavier, il y a une macro constante qui lui correspond ( #define ), le nom de chaque touche commence par le préfixe ALLEGRO_KEY_ puis est ajouté NOMDELATOUCHE , voici les plus importantes :

ALLEGRO_KEY_A … ALLEGRO_KEY_Z

A à Z

ALLEGRO_KEY_0 … ALLEGRO_KEY_9

0 à 9

ALLEGRO_KEY_PAD_0 … ALLEGRO_KEY_PAD_9

0 à 9 (pavé numérique)

ALLEGRO_KEY_F1 … ALLEGRO_KEY_F12

F1 à F12

ALLEGRO_KEY_ESCAPE

Échap

ALLEGRO_KEY_MINUS

Signe -

ALLEGRO_KEY_EQUALS

Signe =

ALLEGRO_KEY_BACKSPACE

Retour arrière

ALLEGRO_KEY_TAB

Tabulation

ALLEGRO_KEY_ENTER

Entrée

ALLEGRO_KEY_DELETE

Suppression

ALLEGRO_KEY_LEFT

Flèche vers gauche

ALLEGRO_KEY_RIGHT

Flèche vers droite

ALLEGRO_KEY_UP

Flèche vers haut

ALLEGRO_KEY_DOWN

Flèche vers bas

La liste complète se trouve dans la documentation Allegro consultable en ligne : http://alleg.sourceforge.net/a5docs/5.0.10/

La fonction al_key_down() retourne true si la touche est appuyée et false sinon. Exemple de mise en place :

Utilisation simple du clavier
Sélectionnez
#include <stdlib.h>
#include <time.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>

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);
}
/*************************************************************
*************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    ALLEGRO_KEYBOARD_STATE key; // clavier simple
    int fin=0;

    srand(time(NULL));

    if(!al_init())
        erreur("al_init()");

    display=al_create_display(800,600);
    if(!display)
        erreur("create_display(800,600)");

    // pour utiliser le clavier
    if(!al_install_keyboard())
        erreur("al_install_keyboard()");
    
    while(!fin){

        // récupération de l'état du clavier
        al_get_keyboard_state(&key);

        // si touche [Echap] appuyée fin boucle et quitter
        if(al_key_down(&key,ALLEGRO_KEY_ESCAPE))
            fin=1;

        // si [Entrée] changer de couleur
        if(al_key_down(&key,ALLEGRO_KEY_ENTER))
            al_clear_to_color(al_map_rgb(rand()%256,rand()%256,rand()%256));
        
        al_flip_display();
    }

    al_destroy_display(display);
    return 0;
}

Si l'utilisateur presse [Entrée], la fenêtre change de couleur. Si vous avez le programme sous les yeux, vous pouvez remarquer qu'en fait elle change plusieurs fois de couleur. En effet, un appui sur une touche, même très bref, correspond à plusieurs centaines de boucles et de passages dans le if de la touche [Entrée]. Nous donnons une solution pour contrôler cela à la fin de ce chapitre.

Disons que cette technique de récupération de l'état du clavier est pratique et aisée à mettre en œuvre. Mais pour des projets importants, il est préférable d'utiliser une boucle d'événement avec un minuteur (timer) qui rythme l'exécution des tâches (événements et boucles d'événements sont présentés dans un chapitre à part).

III-D. Obtenir la souris

Le même principe existe pour gérer rapidement la souris, sans être obligé d'installer une boucle d'événements.

Première chose, appeler la fonction d'initialisation :

 
Sélectionnez
bool al_install_mouse(void)

Cette fonction retourne true sur succès et false sur échec. Ensuite, déclarer une structure d'état de la souris :

 
Sélectionnez
ALLEGRO_MOUSE_STATE mouse;

L'état de la souris (position, clic) y est stocké après chaque appel de la fonction :

 
Sélectionnez
void al_get_mouse_state(ALLEGRO_MOUSE_STATE *ret_state)

Cette fonction retourne dûment complétée la structure passée en paramètre par référence (adresse mémoire). Pour connaître la position de la souris, il suffit d'accéder aux champs x et y de la structure :

 
Sélectionnez
// position de la souris stockée dans masouris.x et masouris.y

Un champ de bits stocke en une seule variable, la variable buttons, l'état de tous les boutons de la souris. Chaque bit du champ buttons correspond à un bouton et s'il est à un c'est que le bouton est pressé. Pour connaître l'état de chaque bit on utilise l'opérateur bit à bit & (ET). Pour mémoire :

 
Sélectionnez
i & 1 donne i (si i vaut 1 ça fait 1 et si i vaut 0 ça fait 0)
i & 0 donne 0

Ensuite, pour accéder à chaque bit séparément, on utilise un masque. Un masque est une variable ou une constante avec sur 0 tous les bits destinés à ne pas être lus et sur 1 tous les bits destinés à être lus. Par exemple :

 
Sélectionnez
masque est une valeur entière :
si masque = 1 // en mémoire ...00000001
si masque = 2 // en mémoire ...00000010
si masque = 4 // en mémoire ...00000100
si masque = 8 // en mémoire ...00001000
etc.

Ici masque est toujours une puissance de deux : 2⁰ , 2¹ , 2² , 2³ , 2⁴, etc.

Alors pour connaître l'état d'un bouton ce sont les expressions :

 
Sélectionnez
masouris.buttons & 1 // état du bouton gauche
masouris.buttons & 2 // état du bouton droit
masouris.buttons & 4 // état du bouton du milieu

Exemple de mise en œuvre :

Obtenir simplement la souris
Sélectionnez
#include <stdio.h>
#include <time.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>

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);
}
/*************************************************************
*************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    ALLEGRO_MOUSE_STATE mouse;    // souris simple
    int fin = 0, oldx = 0, oldy = 0;

    srand(time(NULL));

    if (!al_init())
        erreur("al_init()");

    display = al_create_display(800, 600);
    if (!display)
        erreur("create_display(800,600)");

    // pour utiliser la souris
    if (!al_install_mouse())
        erreur("al_install_mouse()");

    while (!fin){

        // récupération de l'état de la souris
        al_get_mouse_state(&mouse);

        // si mouvement afficher position dans console
        if (oldx != mouse.x || oldy != mouse.y){
            oldx = mouse.x;
            oldy = mouse.y;
            printf("%d-%d\n", mouse.x, mouse.y);
        }

        // si clic gauche changer couleur fenêtre
        if (mouse.buttons & 1)
            al_clear_to_color(al_map_rgb(rand() % 256,
            rand() % 256,
            rand() % 256));

        // si clic droit quitter
        if (mouse.buttons & 2)
            fin = 1;

        al_flip_display();
    }

    al_destroy_display(display);
    return 0;
}

Mais pour ceux qui préfèrent, il y a une fonction qui permet d'obtenir le même résultat :

 
Sélectionnez
bool al_mouse_button_down(const ALLEGRO_MOUSE_STATE *state, int button)

Cette fonction prend au premier paramètre state une structure d'état de la souris passée par référence. Elle retourne true si le bouton dont le numéro est passé au paramètre button est cliqué et false sinon. Attention les boutons sont numérotés à partir de 1 (et non de 0 comme souvent).

III-E. Éléments de gestion des fenêtres

Dans la documentation, la rubrique Displays donne toutes les fonctions disponibles qui permettent d'agir sur une fenêtre. Pour une fenêtre on y trouve :

  • les éléments liés à sa création,
  • des opérations en relation avec l'affichage dans la fenêtre,
  • la possibilité de modifier sa taille et sa position,
  • des paramétrages divers comme l'affichage d'un nom et d'une ou de plusieurs icônes, la possibilité de bloquer la mise en veille.

Pour commencer, nous allons tester ce qui a trait à la taille et à la position de la fenêtre. Le plein écran fera l'objet d'une prochaine section.

III-E-1. Dimensionner une fenêtre existante et la positionner à l'écran

Voici la liste des fonctions dont nous disposons.

La fonction al_resize_display :

 
Sélectionnez
bool al_resize_display(ALLEGRO_DISPLAY *display, int width,int height)

Cette fonction sert à redimensionner la fenêtre. Elle prend en paramètre la fenêtre (pointeur display ) concernée, la nouvelle largeur width , la nouvelle hauteur height . Elle retourne vrai si succès.

La fonction al_get_display_width :

 
Sélectionnez
int al_get_display_width(ALLEGRO_DISPLAY *display)

Retourne la largeur de la fenêtre. Elle prend en paramètre la fenêtre display .

La fonction al_get_display_height :

 
Sélectionnez
int al_get_display_height(ALLEGRO_DISPLAY *display)

Retourne la hauteur de la fenêtre. Elle prend en paramètre la fenêtre display .

La fonction al_set_window_position :

 
Sélectionnez
void al_set_window_position(ALLEGRO_DISPLAY *display, int x, int y)

Permet de positionner la fenêtre n'importe où dans l'écran. Elle prend la fenêtre en paramètre ainsi que la position horizontale x et la position verticale y souhaitées.

La fonction al_get_window_position :

 
Sélectionnez
void al_get_window_position(ALLEGRO_DISPLAY *display,int*x,int *y)

Récupère la position à l'écran de la fenêtre display via deux variables passées par référence aux pointeurs x pour l'horizontale et y pour la verticale.

Dans le test suivant, nous expérimentons ces fonctions. Dans une boucle sont répétées dix fois de suite dans l'ordre les opérations suivantes :

  • Déterminer une taille aléatoire pour la fenêtre.

Si cette taille est acceptable elle est assignée à la fenêtre et :

  • La fenêtre est déplacée à une position aléatoire dans l'écran.
  • Sa couleur est changée.
  • Elle est affichée (bascule al_flip_display du double buffer à l'écran).
  • Une fenêtre de dialogue donne ses valeurs de taille et de position.
Dimensionner et positionner une fenêtre à l'écran
Sélectionnez
#include <stdio.h>
#include <time.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>

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);
}

// une fonction avec chaîne formatée et liste variable de
// paramètres comme printf
void message(const char*format,...)
{
    ALLEGRO_DISPLAY*display;
    char txt[1024];
    va_list args;

    // création liste de param
    va_start(args,format);
    // compose la chaîne selon les paramètres et la récupère
    // dans le tableau de char txt
    vsnprintf(txt,sizeof(txt),format,args);

    // supprime la liste créée
    va_end(args);

    // récupère le diplay s'il existe
    display = al_is_system_installed() ? al_get_current_display() : NULL;
    //affiche une fenêtre de dialogue avec la chaîne
    al_show_native_message_box(display,"Message","",txt,NULL,0);
}
/*************************************************************
*************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    int i,res,w,h,x,y;
    srand(time(NULL));

    if(!al_init())
        erreur("al_init()");

    // fenêtre au départ
    display=al_create_display(800,600);
    if(!display)
        erreur("create_display");

    // 10 fois
    for (i=0; i<10; i++){
        // nouvelle taille aléatoire
        res = al_resize_display(display,// fenêtre courante
                                1+rand()%1920, // largeur
                                1+rand()%1080);// hauteur

        // récupérer les nouvelles largeur et hauteur
        w = al_get_display_width(display);
        h = al_get_display_height(display);

        // si ok
        if(res){
            // changer la position de la fenêtre
            al_set_window_position(display,
                                    rand()%(1920-w), // pos x
                                    rand()%(1080-h));// pos y

            // récupérer la position de la fenêtre
            al_get_window_position(display, &x, &y);
            // changer sa couleur
            al_clear_to_color(al_map_rgb(rand()%256,// rouge
                                rand()%256,// vert
                                rand()%256));// bleu
            al_flip_display();

            // afficher sa taille et sa position
            message("%4d par %4d\npos : (%d,%d)",w,h,x,y);
        }
        else{
            message("%4d par %4d : echoue\n",w,h);
            exit(EXIT_FAILURE);
        }
    }

    al_destroy_display(display);
    return 0;
}

À noter dans ce programme l'utilisation d'une fonction avec une liste variable d'arguments, c'est la fonction message qui permet à la fenêtre de dialogue d'avoir un affichage avec une chaîne formatée comme dans un printf() .

La liste d'arguments associés à la chaîne de caractères formatée est transformée en une chaîne ordinaire grâce à la fonction standard vsnprintf() :

 
Sélectionnez
vsnprintf(char*, int, const char*, va_list);

Cette fonction stocke la chaîne finale au paramètre p1 avec une taille maximum donnée au paramètre p2 à partir de la chaîne formatée donnée au paramètre p3 et de la liste standard d'arguments passée au paramètre p4.

Sous Visual C++ cette fonction est jugée « unsafe », ce qui provoque une erreur à la compilation et il est suggéré d'utiliser plutôt une version sécurisée non standard de Microsoft :

 
Sélectionnez
vsnprintf_s(char*, int, const char*, va_list);

A priori tout ce qui change à l'utilisation est le suffixe _s à ajouter à la fin.

Afin de garder son code propre et portable, tout en évitant les erreurs de compilation sous Visual C++, il est possible d'ajouter :

 
Sélectionnez
#define _CRT_SECURE_NO_WARNINGS

au début du code, pour désactiver la vérification de Visual C++.

III-E-2. Activer le contrôle de redimensionnement

Toutes les fenêtres que nous avons vues jusqu'à maintenant ont une taille fixe décidée avec al_create_display() . Cette taille est éventuellement modifiable dans le code. Mais l'utilisateur ne peut pas la changer en tirant sur un bord ou un coin, ni en double cliquant dans la barre de titre. Pour que le redimensionnement de la fenêtre par l'utilisateur soit possible, il faut le spécifier avant la création de la fenêtre avec la fonction :

 
Sélectionnez
void al_set_new_display_flags(int flags)

Cette fonction permet d'activer différentes fonctionnalités choisies parmi celles qui sont proposées. Le paramètre flags est un tableau de bits. Chacun des bits de l'entier flags correspond à une fonctionnalité. S'il est à 1, la fonctionnalité est activée ; s'il est à 0, elle ne l'est pas. La mise à 1 d'une fonctionnalité est obtenue à l'aide d'une macro constante #define spécifique qui définit le bit voulu à 1. Les macros constantes qui nous intéressent ici sont :

  • ALLEGRO_WINDOWED pour spécifier un mode fenêtré.
  • ALLEGRO_RESIZABLE pour indiquer une fenêtre redimensionnable (nous verrons d'autres fonctionnalités plus tard).

La réunion de ces deux propriétés s'obtient avec l'opérateur bit à bit de mise à 1 de la façon suivante :

 
Sélectionnez
ALLEGRO_WINDOWED | ALLEGRO_RESIZABLE

Ainsi l'appel :

 
Sélectionnez
al_set_new_display_flags(ALLEGRO_WINDOWED | ALLEGRO_RESIZABLE);

signifie que le bit pour mode fenêtré et le bit pour redimensionnement sont mis à 1. Ces deux propriétés sont ainsi activées lors de la création de la fenêtre qui suit. Le programme suivant permet de le constater :

Activer le redimensionnement d'une fenêtre
Sélectionnez
#include <stdio.h>
#include <time.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>

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);
}
/*************************************************************
*************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    ALLEGRO_KEYBOARD_STATE key; // clavier simple
    int fin=0;

    srand(time(NULL));

    if(!al_init())
        erreur("al_init()");

    // paramétrage fonctionnalités fenêtre avant sa création
    al_set_new_display_flags(ALLEGRO_WINDOWED |
                             ALLEGRO_RESIZABLE);
    display=al_create_display(800,600);
    if(!display)
        erreur("create_display(800,600)");

    // pour utiliser le clavier
    if(!al_install_keyboard())
        erreur("al_install_keyboard()");

    // boucle qui laisse le temps à l'utilisateur de manipuler
    // la fenêtre
    while(!fin){

        // récupération de l'état du clavier
        al_get_keyboard_state(&key);

        // si touche [Echap] appuyée fin boucle et quitter
        if(al_key_down(&key,ALLEGRO_KEY_ESCAPE))
            fin=1;

        al_flip_display();
    }
    al_destroy_display(display);
    return 0;
}

III-F. Gestion du plein écran

Pour gérer le plein écran, nous avons deux solutions : soit avoir une fenêtre qui couvre tout l'écran sans barre de contrôle en haut, soit quitter le mode fenêtré et être tout simplement en plein écran. Dans les deux cas, il s'agit d'une fonctionnalité obtenue avec la fonction :

 
Sélectionnez
void al_set_new_display_flags(int flags)

III-F-1. Fenêtre qui couvre tout l'écran

Le mode fenêtré plein écran est en principe donné par :

 
Sélectionnez
ALLEGRO_FULLSCREEN_WINDOWE

En effet d'après la documentation l'appel de :

 
Sélectionnez
al_set_new_display_flags(ALLEGRO_FULLSCREEN_WINDOW);

suivi de :

 
Sélectionnez
display=al_create_display(800,600);

ne tient pas compte des 800x600 demandés. Il donne une fenêtre qui couvre tout l'écran selon la résolution courante, faisant disparaître et la barre de titre dans le haut de la fenêtre et la barre des tâches du bureau exactement comme si vous étiez en en mode plein écran.

Mais en réalité ce n'est pas ce qui se passe actuellement du fait semble-t-il d'une erreur pas encore corrigée. Vous obtiendrez une fenêtre sans barre de titre en haut de 800 par 600 pixels.

Le programme suivant permet de le vérifier :

Fenêtre plein écran
Sélectionnez
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <time.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>

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);
}

void message(const char*format,...)
{
    ALLEGRO_DISPLAY*display;
    char txt[1024];
    va_list args;

    va_start(args,format);
    vsnprintf(txt,sizeof(txt),format,args);
    va_end(args);

    display = al_is_system_installed() ?
                            al_get_current_display() : NULL;
    al_show_native_message_box(display,"Message",txt,txt,NULL,0);
}
/*************************************************************
*************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    int w,h;

    srand(time(NULL));
    if(!al_init())
        erreur("al_init()");

    al_set_new_display_flags(ALLEGRO_FULLSCREEN_WINDOW);
    // ensuite en dépit des 800x600 demandés prend automatiquement
    // la résolution courante de l'écran
    display=al_create_display(800,600);
    if(!display)
        erreur("create_display");

    // changer sa couleur
    al_clear_to_color(al_map_rgb(rand()%256,rand()%256,
                                rand()%256));
    al_flip_display();

    // pour récupérer la largeur et la hauteur
    w = al_get_display_width(display);
    h = al_get_display_height(display);
    message("resolution : %d par %d\n",w,h);
    al_rest(3.0);

    al_destroy_display(display);
    return 0;
}

Cependant, lorsque cette erreur sera corrigée, avec ce même code vous devriez obtenir un mode plein écran. Actuellement pour obtenir une fenêtre plein écran, il faut récupérer soi-même la résolution maximale de l'écran et créer une fenêtre de cette taille :

 
Sélectionnez
ALLEGRO_DISPLAY_MODE rec;
al_get_display_mode(al_get_num_display_modes() - 1, &rec);
al_set_new_display_flags(ALLEGRO_FULLSCREEN_WINDOW);
display = al_create_display(rec.width, rec.height);

Il reste que la barre des tâches n'est pas masquée par la fenêtre. Elle demeure par dessus.

Par ailleurs, cette technique de récupération des résolutions possibles de la carte graphique et de l'écran et par conséquent la possibilité de trouver la résolution la plus grande sont détaillées plus loin.

III-F-2. Plein écran sans fenêtre

Le mode plein écran sans fenêtre est donné par :

 
Sélectionnez
ALLEGRO_FULLSCREEN

et l'appel de al_set_new_display_flags() avant la création de la fenêtre :

 
Sélectionnez
al_set_new_display_flags(ALLEGRO_FULLSCREEN);

III-F-2-a. Expérimentation

Plein écran sans fenêtre
Sélectionnez
#include <stdio.h>
#include <time.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>

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);
}
/*************************************************************
*************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    int w, h;

    srand(time(NULL));
    if (!al_init())
        erreur("al_init()");

    al_set_new_display_flags(ALLEGRO_FULLSCREEN);
    display = al_create_display(320, 200);
    if (!display)
        erreur("create_display");

    // changer sa couleur
    al_clear_to_color(al_map_rgb(rand() % 256, rand() % 256,
        rand() % 256));
    al_flip_display();

    // pour récupérer la largeur et la hauteur
    w = al_get_display_width(display);
    h = al_get_display_height(display);

    // affiche la résolution dans la console
    printf("resolution : %d par %d\n", w, h);
    al_rest(3.0);

    al_destroy_display(display);

    // pour retenir la console avant fermeture
    system("PAUSE");
    return 0;
}

La taille passée pour la résolution, 800x600, est respectée indépendamment de la résolution courante de l'écran (ça marche même avec une résolution de 320 par 200 !). La taille des pixels est adaptée à la résolution adoptée. Par ailleurs si la fonction al_show_native_message_box() est appelée, la fenêtre de dialogue créée passe à l'arrière-plan et demeure inaccessible en quelque sorte derrière l'écran. Elle bloque alors le programme parce qu'il n'est plus possible de la fermer. C'est pourquoi nous préférons afficher la résolution dans la fenêtre console visible à la sortie du programme.

III-F-3. Résolutions supportées

Allegro fournit une structure :

 
Sélectionnez
ALLEGRO_DISPLAY_MODE

qui permet de connaître quels sont les paramètres plein écran que supporte l'ordinateur et sa carte graphique.

Cette structure est composée comme suit :

 
Sélectionnez
typedef struct ALLEGRO_DISPLAY_MODE {
    int width; // largeur de l'écran (résolution)
    int height; // hauteur de l'écran
    int format; // format de pixel du mode
    int refresh_rate; // fréquence de rafraîchissement du mode
} ALLEGRO_DISPLAY_MODE;

Les champs width et height donnent une résolution possible, format correspond à une profondeur de couleur (8, 16, 24, 32 bits).

Le nombre total des résolutions plein écran possibles sur l'ordinateur s'obtient avec la fonction :

 
Sélectionnez
int al_get_num_display_modes(void)

Ensuite les caractéristiques de chaque mode sont récupérables avec la fonction :

 
Sélectionnez
ALLEGRO_DISPLAY_MODE *al_get_display_mode(int n, ALLEGRO_DISPLAY_MODE *mode)

Cette fonction retourne dans la structure mode passée par référence en paramètre p2 les caractéristiques du mode numéro n donné en paramètre p1. En cas d'échec la fonction retourne NULL .

Le programme suivant commence par afficher dans la fenêtre console tous les modes plein écran possibles. Ensuite l'utilisateur peut sélectionner un mode afin d'obtenir le plein écran correspondant. Une fois installé, le plein écran reste trois secondes puis le plein écran display disparaît et nous retournons à la fenêtre console. Pour quitter, il suffit d'entrer une valeur impossible (-1 par exemple).

Obtenir les résolutions supportées
Sélectionnez
#include <stdio.h>
#include <time.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>

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);
}
/*************************************************************
*************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    ALLEGRO_DISPLAY_MODE mode;
    int i,nbmode;
    srand(time(NULL));

    if(!al_init())
        erreur("al_init()");

    // récupère le nombre de résolutions possibles
    nbmode = al_get_num_display_modes();
    printf("il y a %d résolution possibles :\n",nbmode);
    
    for (i = 0; i < nbmode; i++){
        // récupère les informations pour chaque mode
        al_get_display_mode(i, &mode);

        // affiche la résolution du mode courant
        // dans la console
        printf("mode %2d resolution %4d x %4d "
                "%2d bits et %2d \n",
                i, mode.width, mode.height,
                al_get_pixel_format_bits(mode.format),
                mode.refresh_rate);
    }

    // tester les résolutions
    do{
        printf("entrer le numéro du mode souhaite :\n");
        scanf_s("%d", &i);
        rewind(stdin);

        if (i >= 0 && i < nbmode){
            // crée un display plein écran
            // dans le mode courant
            al_get_display_mode(i, &mode);
            al_set_new_display_refresh_rate(mode.refresh_rate);
            al_set_new_display_flags(ALLEGRO_FULLSCREEN);
            display = al_create_display(mode.width,
                                        mode.height);

            // change la couleur de la fenêtre
            al_clear_to_color(al_map_rgb(rand() % 256,
                                rand() % 256,
                                rand() % 256));
            // affiche et reste trois seconde
            al_flip_display();
            al_rest(3.0);

            // détruit le display courant, passe au suivant
            al_destroy_display(display);
        }
    }while(i >= 0 && i < nbmode);

    return 0;
}

La fonction al_set_new_display_refresh_rate :

 
Sélectionnez
void al_set_new_display_refresh_rate(int refresh_rate)

Cette fonction sert à spécifier la fréquence de rafraîchissement dans cette situation où l'on recrée une fenêtre display à partir de celle existante et selon un nouveau mode. Il faut veiller à ce que mode et fréquence de rafraîchissement soient compatibles. Selon le mode souhaité, la fonction permet d'activer la fréquence correspondante qui doit lui être passée en paramètre.

Tous les modes trouvés sont en 32 bits pour la profondeur de couleur et il semble qu'il n'y ait plus d'autre choix actuellement. Par exemple sous Windows 7, 8, 8.1 la profondeur de couleur est obligatoirement 32 bits et non modifiable.

III-F-3-a. Obtenir la plus grande résolution

Les modes sont classés des plus petites capacités graphiques vers les plus grandes. Le mode le plus complet est celui à la position :

 
Sélectionnez
al_get_num_display_modes()-1

La récupération de ses informations est la suivante :

Obtenir la plus grande résolution
Sélectionnez
#include <stdio.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>

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);
}
/*************************************************************
*************************************************************/
int main()
{
    ALLEGRO_DISPLAY_MODE mode;
    int nbmode = 0;
    if (!al_init())
        erreur("al_init()");


    // récupère les informations du dernier mode
    nbmode = al_get_num_display_modes();
    al_get_display_mode(nbmode - 1, &mode);

    // affiche la résolution du mode courant dans la console
    printf("mode %d resolution %4d x %4d\n", nbmode,
        mode.width, mode.height);

    // presser une touche pour continuer
    system("PAUSE");

    return 0;
}

Ensuite il est simple de créer une fenêtre de sa taille :

 
Sélectionnez
ALLEGRO_DISPLAY* display;
display = al_create_display(mode.width,mode.height);

III-G. Création d'un modèle de projet (template)

Un projet Allegro, entre édition de liens et initialisations de base, est un peu long à mettre en place. Mais Code::Blocks comme Visual Studio donnent la possibilité de réaliser des modèles de projet (template en anglais). Nous allons utiliser cette fonctionnalité pour faire un projet Allegro comprenant déjà un linkage spécifique et une première page de code.

Ensuite nous pourrons sélectionner ce modèle de projet parmi les autres lors de la création d'un nouveau projet.

Sous Code::Blocks comme sous Visual C++, la démarche est sensiblement la même :

  • Créer un projet (vide ou console).
  • Configurer soigneusement le projet à savoir indiquer où est la bibliothèque et éditer les liens avec les modules utilisés de la bibliothèque Allegro.
  • Écrire la première page de code. Ce sera le minimum comprenant tout ce qui vous semble important pour démarrer un type d'application Allegro. Attention à bien tester le code et vérifier qu'il ne contient aucune erreur.
  • Exporter en tant que modèle.

III-G-1. Code de base du projet

Voici une proposition de départ utile pour des projets Allegro simples. Vous serez peut-être amené ultérieurement à faire d'autres modèles de projet pour des projets plus complexes intégrant notamment la gestion des événements, des possibilités de dessin et des affichages d'images.

Modèle de projet simple
Sélectionnez
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>


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);
}
/*****************************************************************
*****************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    ALLEGRO_KEYBOARD_STATE key;

    if (!al_init())
        erreur("al_init()");

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

    display = al_create_display(800, 600);
    if (!display)
        erreur("al_create_display()");

    do{
        al_get_keyboard_state(&key);



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

    al_destroy_display(display);
    return 0;
}
/*****************************************************************
*****************************************************************/

Dernier point important, avant de passer à la sauvegarde du projet en tant que modèle, compilez le projet en mode debug et en mode release et vérifiez qu'il n'y pas de bogue.

Une fois ces trois opérations terminées (projet, configuration, tests), il faut sauvegarder le projet sous forme de modèle. Nous détaillons ci­dessous cette sauvegarde pour Code::Blocks, puis pour Visual Studio.

III-G-2. Modèle de projet sous Code::Blocks

Sous Code::Blocks, allez dans le menu File et sélectionnez Save project as template :

Image non disponible

Dans la fenêtre entrez un nom pour le modèle et cliquez sur OK. Le modèle est sauvegardé.

Il se retrouve ensuite dans la fenêtre de sélection de modèles lors de la création d'un projet dans la rubrique User templates sur le côté gauche de la fenêtre :

Image non disponible

Cliquez sur User templates.

La fenêtre suivante s'affiche :

Image non disponible

Dans cette fenêtre, il suffit de sélectionner son modèle. Il est évidemment possible de faire toutes sortes de modèles, par exemple plusieurs types de projets Allegro pour des styles d'applications différentes qui nécessitent des initialisations différentes dans le code au départ.

III-G-3. Modèle de projet sous Visual Studio

Pour sauvegarder un projet en tant que modèle de projet (template) sous Visual Studio :

Dans le menu Fichier sélectionnez Exporter le modèle.

La fenêtre Assistant Exportation de modèle s'ouvre :

Image non disponible

Un choix est proposé : faire un modèle de projet ou un modèle d'élément (par exemple une page de code). Sélectionnez Modèle de projet.

La liste tout en bas permet de sélectionner dans la solution le projet qui va servir de modèle (rappelons qu'une solution peut contenir plusieurs projets). Pour nous, le projet se nomme « modèle de projet ». Ensuite, cliquez sur Suivant.

Une fenêtre d'options pour le modèle apparaît :

Image non disponible

Le premier champ sert à nommer son modèle. Nous avons nommé le nôtre « Allegro 5.0.10-base jeu » (quoiqu'ici ce ne soit pas encore vraiment complet).

Le second champ permet d'entrer une description du modèle, description qui sera retrouvée ensuite sur la gauche dans la fenêtre de création de projets. C'est pratique pour se rappeler de certaines spécifications propres au projet.

Le troisième champ offre la possibilité d'adjoindre une icône pour le projet. Nous avons choisi une petite icône qui se trouve dans un dossier images sur la partition D de notre disque dur.

Le quatrième champ permet d'associer une image à la description du projet. L'image choisie se trouvera centrée en dessous du texte d'explication dans le volet gauche de la fenêtre de création de projets.

L'emplacement de sortie correspond à l'emplacement sur le disque où est sauvegardé le modèle. Mais le modèle qui est utilisé pour un nouveau projet par Visual Studio en est une copie et se trouve dans le dossier Template. Si au besoin vous devez supprimer un modèle de projet et ne plus le voir apparaître dans la fenêtre de création de projets, il faut le supprimer dans le dossier Template.

Si vous cochez Importer automatiquement le modèle dans Visual Studio, celui-ci sera intégré et visible dans la fenêtre nouveau projet.

Si vous cochez Afficher une fenêtre d'explorateur pour le dossier des fichiers de sorties, une telle fenêtre s'ouvre lorsque vous cliquez sur Terminer, ce qui permet de voir où est stocké le modèle et sous quelle forme sur le disque.

Une fois toutes ces informations entrées, cliquez sur Terminer.

Ouvrez la fenêtre Nouveau projet, votre modèle y apparaît.

III-H. Quelques précisions

III-H-1. Utilisation de la fenêtre console

Sous Windows la fenêtre console est fournie par défaut lorsque l'on débute un projet à partir de « projet vide ». Pour supprimer cette console, sous Code::Blocks comme sous Visual Studio, il faut commencer un projet en sélectionnant un modèle du type application Win32.

III-H-1-a. Création d'un projet sans fenêtre console

Sous Code::Blocks, vous devez commencer par choisir un projet Win32 et simplement remplacer tout le code fourni automatiquement avec le modèle par celui de l'application Allegro. Ensuite, si vous partez souvent avec ce type de projet, il peut être utile de réaliser un nouveau modèle.

Pour Visual Studio il y a la possibilité de commencer un projet Win32, de spécifier qu'on le veut vide afin de le compléter pour Allegro ensuite. La démarche est la suivante :

Sélectionnez Nouveau projet, la fenêtre Nouveau projet s'ouvre :

Image non disponible

Dans le volet gauche, sélectionnez Win32 et au centre Projet Win32, donnez un nom pour le projet et la solution, positionnez sur le disque l'endroit de la sauvegarde puis cliquez sur OK.

La fenêtre Assistant Application Win32 s'ouvre :

Image non disponible

Sur la gauche, cliquez sur Paramètres de l'application. La fenêtre correspondante s'ouvre :

Image non disponible

Décochez Vérifications SDL (…) et cochez Projet vide ce qui donne :

Image non disponible

Cliquez sur Terminer.

Un nouveau projet sans page de code est ouvert que vous retrouvez dans l'explorateur de solutions. Il reste à lui ajouter une page de code et à établir les liens pour l'utilisation de la bibliothèque Allegro.

Ce projet ne contiendra pas de fenêtre console.

III-H-1-b. Alternative à la fenêtre console sous Allegro

Dans nos programmes d'expérimentation, nous avons presque toujours laissé la fenêtre console de Windows et nous l'avons utilisée de temps à autre. C'est pratique, mais il peut y avoir un problème si l'on souhaite tester les applications dans un autre environnement et sous un autre système, Linux ou Mac OS par exemple. Pour l'éviter,

Allegro fournit l'équivalent d'une console native à la bibliothèque.

Pour obtenir une console native et l'utiliser, nous avons besoin d'un pointeur sur une structure ALLEGRO_TEXTLOG :

 
Sélectionnez
ALLEGRO_TEXTLOG *console;

Créer et ouvrir la fenêtre console revient à la fonction al_open_native_text_log :

 
Sélectionnez
ALLEGRO_TEXTLOG *al_open_native_text_log(char const *title, int flags)

Cette fonction retourne un pointeur sur une fenêtre console et ouvre la fenêtre. En cas d'impossibilité, elle retourne la valeur NULL . Le premier paramètre title permet de donner un nom à la fenêtre. Le second flags peut recevoir trois valeurs qui déterminent différents paramétrages :

  • 0 : a priori ce sont les valeurs par défaut. La police par défaut semble être Arial et en cas d'utilisation d'événements (les événements sont décrits dans le chapitre suivant), un événement est généré avec un clic sur le bouton de fermeture. C'est un événement du type ALLEGRO_EVENT_NATIVE_DIALOG_CLOSE. La récupération des événements d'une fenêtre console native nécessitera par ailleurs un appel à la fonction al_get_native_text_log_event_source() (voir le chapitre Les événementsÉvénements).
  • ALLEGRO_TEXTLOG_NO_CLOSE : dans le cadre d'une utilisation avec des événements, cette valeur interdit le bouton de fermeture qui dès lors n'envoie pas d'événement.
  • ALLEGRO_TEXTLOG_MONOSPACE : agit sur le choix de la police qui devient une police « monospace » c'est-à-dire une police dans laquelle toutes les lettres prennent la même dimension horizontale, par exemple la police courier New.

Exemple d'ouverture de console native :

 
Sélectionnez
ALLEGRO_TEXTLOG *console;
console = al_open_native_text_log("Ma console", 0);

Pour écrire dans la console c'est la fonction al_append_native_text_log :

 
Sélectionnez
void al_append_native_text_log(ALLEGRO_TEXTLOG *textlog, char const *format, ...)

C'est l'équivalent d'un printf mais adressé à la fenêtre console native dont le pointeur est donné au premier paramètre textlog . Le second paramètre format est la chaîne de caractères formatée sur le même modèle que la fonction standard printf . Voici en exemple d'utilisation, l'affichage de la position de la souris :

 
Sélectionnez
al_append_native_text_log(console, "position souris : %d, %d\n", mouse.x, mouse.y);

Pour fermer la console, c'est la fonction al_close_native_text_log :

 
Sélectionnez
void al_close_native_text_log(ALLEGRO_TEXTLOG *textlog)

La fonction ferme la console et désalloue l'adresse passée en paramètre au pointeur textlog . Attention au fait que le pointeur n'est pas mis à NULL . Il contient toujours la même adresse mémoire mais celle-ci n'est plus accessible.

III-H-1-b-i. Expérimentation

Dans le programme ci-dessous, une fenêtre console native Allegro est créée, la position de la souris est affichée dedans en mode texte et si la touche [Entrée] est appuyée, la fenêtre console est fermée.

Utiliser une fenêtre console native Allegro
Sélectionnez
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>

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);
}
/*****************************************************************
*****************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    ALLEGRO_KEYBOARD_STATE key;
    ALLEGRO_MOUSE_STATE mouse;

    ALLEGRO_TEXTLOG *console; // une console

    if (!al_init())
        erreur("al_init()");

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

    if (!al_install_mouse())
        erreur("al_install_mouse()");

    display = al_create_display(800, 600);
    if (!display)
        erreur("al_create_display()");

    // création et ouverture
    console = al_open_native_text_log("Ma console", 0);
    if (!console)
        erreur("al_open_native_text_log(\"Ma console\", 0)");

    int mx = 0;
    int my = 0;
    do{
        al_get_keyboard_state(&key);
        al_get_mouse_state(&mouse);

        // écrire dans la console
        if (console != NULL && (mx!=mouse.x || my!= mouse.y)){
            mx = mouse.x;
            my = mouse.y;
            al_append_native_text_log(console,
                 "position souris : %d, %d\n", mx, my);
        }

        //fermer la console
        if (al_key_down(&key, ALLEGRO_KEY_ENTER)){
            al_close_native_text_log(console);
            console = NULL;
        }

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

    al_destroy_display(display);
    return 0;
}
/*****************************************************************
*****************************************************************/

III-H-2. Meilleur contrôle du clavier simple

La capture du clavier que nous utilisons pour l'instant est pratique et rapide à mettre en place. La même méthode existe pour la souris mais aussi pour le joystick. Certes les performances sont plus limitées qu'avec une file d'événements (pas de minuteur notamment) mais elle est suffisante dans certaines situations. Nous allons l'utiliser pour tester les fonctions de dessin ainsi que les fonctions d'affichage des images dans les deux chapitres qui suivent.

III-H-2-a. Problème des répétitions du clavier

Cette méthode qui est à la base dans la version 4 d'Allegro a des limites :

  • elle ne permet pas de contrôler la vitesse d'exécution du programme avec un minuteur,
  • les captures des touches du clavier sont très rapides et incontrôlées. Le temps que vous appuyez sur une touche peut correspondre à plusieurs centaines de tours de boucles.
III-H-2-a-i. Expérimentation

Dans le programme suivant, l'appui sur la touche [Entrée] incrémente une variable, et l'appui sur la touche [Espace] affiche la valeur de cette variable dans la fenêtre console.

Compter les répétitions du clavier
Sélectionnez
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
#include <stdio.h>

// contrôle d'erreur
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);
}
/*******************************************
********************************************/
int main()
{
    ALLEGRO_DISPLAY *display;
    ALLEGRO_KEYBOARD_STATE key; // stocke l'état du clavier
    int fin = 0, cmpt = 0;

    if (!al_init())
        erreur("al_init()");

    //Pour avoir le clavier
    if (!al_install_keyboard())
        erreur("al_install_keyboard()");

    display = al_create_display(800, 600);
    if (!display)
        erreur("al_create_display()");
    al_set_window_title(display, "Clavier simple");

    while (!fin){
        // récupérer l'état du clavier
        al_get_keyboard_state(&key);

        if (al_key_down(&key, ALLEGRO_KEY_ESCAPE))
            fin = 1;

        // incrémentation de la variable
        if (al_key_down(&key, ALLEGRO_KEY_ENTER))
            cmpt++;

        // affichage
        if (al_key_down(&key, ALLEGRO_KEY_SPACE))
            printf("%d\n", cmpt);

        al_flip_display();

    }
    al_destroy_display(display);

    return 0;
}
/*******************************************
********************************************/

Pour chaque appui sur la touche [Entrée] il y a sur notre machine entre 300 et 500 passages dans le if. C'est bien sûr valable pour toutes les touches.

III-H-2-b. Compter les répétitions clavier

our résoudre ce problème, l'idée est de compter le nombre des répétitions pour chaque touche du clavier lorsqu'elle est appuyée. Il sera ainsi possible de contrôler l'action en fonction du nombre des répétitions. Ce comptage est mémorisé avec un tableau d'entiers :

 
Sélectionnez
int press[ALLEGRO_KEY_MAX] ;

Dans ce tableau, chaque indice correspond à un identifiant de touche. Par exemple ALLEGRO_KEY_A , ALLEGRO_KEY_ESCAPE , ALLEGRO_KEY_etc.

Ainsi, à chaque tour dans la boucle du programme lorsqu'une touche est restée appuyée, on a :

 
Sélectionnez
if(al_key_down(&key,matouche))
    press[matouche]++ ;

Et elle est remise à 0 uniquement si la touche est relevée :

 
Sélectionnez
if(al_key_down(&key,matouche)){
    press[matouche]++ ;
    // faire les instructions associées à la touche
}
else if(!al_key_down(&key,matouche) // si relevée remise à 0
    press[matouche]=0 ;

Ensuite le nombre de répétitions peut être utilisé par exemple en interdisant au-delà de cinq :

 
Sélectionnez
if(al_key_down(&key,matouche) && press[matouche] < 5){
    press[matouche]++ ;
    // faire les instructions associées à la touche
}
else if(!al_key_down(&key,matouche)
    press[matouche]=0 ;

Dans cet exemple, à partir de cinq répétitions, les instructions associées à la touche ne seront plus exécutées.

Cet algorithme est implémenté dans une fonction à partir d'un tableau d'entiers static local à la fonction. Le mot-clé static signifie qu'il reste en mémoire à l'issue de l'exécution de la fonction de sorte que chaque appel à la fonction retrouve le tableau dans l'état où il était à l'issue de l'appel précédent. Le tableau est initialisé à 0 seulement une fois, lors du premier appel. Voici la fonction :

 
Sélectionnez
int is_key_pressed( ALLEGRO_KEYBOARD_STATE*key,int touche, int repeat)
{
    //le tableau conserve ses valeurs d'un appel à l'autre (static)
    static int press[ALLEGRO_KEY_MAX]={0};
    int res=0;
    if (al_key_down(key,touche)&& press[touche] < repeat ){
        press[touche]++;
        res=1;
    }
    else if( ! al_key_down(key,touche))
        press[touche]=0;
    return res;
}

La fonction retourne true si la touche touche en paramètre est appuyée pour un nombre de fois inférieur au nombre repeat des répétitions autorisées. Elle retourne false sinon, parce que la touche n'est plus appuyée ou que le nombre des répétitions autorisées est atteint.

Le programme suivant permet d'expérimenter cette fonction et nous l'utiliserons pour la plupart des programmes de démonstrations des primitives de dessin.

Contrôler les répétitions du clavier
Sélectionnez
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>


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);
}
/*****************************************************************
*****************************************************************/
int is_key_pressed(ALLEGRO_KEYBOARD_STATE*key, int touche, int repeat)
{
    //le tableau conserve ses valeurs d'un appel à l'autre (static)
    static int press[ALLEGRO_KEY_MAX] = { 0 };
    int res = 0;

    if (al_key_down(key, touche) && press[touche] < repeat){
        press[touche]++;
        res = 1;
    }
    else if (!al_key_down(key, touche))
        press[touche] = 0;
    return res;

}
/*****************************************************************
*****************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    ALLEGRO_KEYBOARD_STATE k, *key = &k;
    ALLEGRO_TEXTLOG * textlog;

    if (!al_init())
        erreur("al_init()");

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

    display = al_create_display(800, 600);
    if (!display)
        erreur("display");

    // fenêtre console native allegro
    textlog = al_open_native_text_log("test clavier",
        ALLEGRO_TEXTLOG_MONOSPACE);

    al_append_native_text_log(textlog,
        "Test fleches pressee, ESCAPE pour quitter :\n");
    do{
        al_get_keyboard_state(key);

        if (is_key_pressed(key, ALLEGRO_KEY_UP, 1))
            al_append_native_text_log(textlog,
            "touche UP pressee\n");

        if (is_key_pressed(key, ALLEGRO_KEY_RIGHT, 2))
            al_append_native_text_log(textlog,
            "touche RIGHT pressee\n");

        if (is_key_pressed(key, ALLEGRO_KEY_DOWN, 3))
            al_append_native_text_log(textlog,
            "touche DOWN pressee\n");

        if (is_key_pressed(key, ALLEGRO_KEY_LEFT, 10))
            al_append_native_text_log(textlog,
            "touche LEFT pressee\n");

    } while (!is_key_pressed(key, ALLEGRO_KEY_ESCAPE, 1));



    al_destroy_display(display);
    return 0;

}
/*****************************************************************
*****************************************************************/

III-H-3. Meilleur contrôle de la souris simple

La capture des clics de la souris avec la méthode que nous utilisons pour l'instant présente les mêmes avantages et inconvénients que la capture du clavier : pratique et rapide à mettre en place, mais le contrôle des clics doit être renforcé.

III-H-3-a. Problème des répétitions de clics

La capture des clics sur les boutons de la souris est très rapide et incontrôlée. Le temps pendant lequel vous cliquez peut correspondre à plusieurs centaines de tours de boucles.

III-H-3-a-i. Expérimentation
Compter les répétitions de clics de la souris
Sélectionnez
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
#include <stdio.h>

int SCREENX = 640;
int SCREENY = 480;

/*****************************************************************
*****************************************************************/
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);
}
/*****************************************************************
*****************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    ALLEGRO_KEYBOARD_STATE key;
    ALLEGRO_MOUSE_STATE mouse;
    int gauche = 0, droite = 0;

    if (!al_init())
        erreur("al_init()");

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

    if (!al_install_mouse())
        erreur("al_install_mouse()");

    display = al_create_display(SCREENX, SCREENY);
    if (!display)
        erreur("al_create_display()");


    do{
        al_get_keyboard_state(&key);
        
        al_get_mouse_state(&mouse);

        if ( al_mouse_button_down(&mouse, 1)){
            gauche++;
            printf("gauche : %d\n", gauche);
        }

        if (al_mouse_button_down(&mouse, 2)){
            droite++;
            printf("droite : %d\n", droite);
        }

        if (al_key_down(&key, ALLEGRO_KEY_ENTER))
            gauche = droite = 0;

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

    al_destroy_display(display);
    return 0;
}
/*****************************************************************
*****************************************************************/

À l'exécution de ce programme sur notre machine pour un clic, il peut y avoir entre 300 et 2 000 tours de boucle.

III-H-3-b. Compter les clics sur chaque bouton de la souris

Nous pouvons utiliser le même algorithme que celui du clavier pour contrôler les clics de la souris. Mais comme le nombre de boutons peut varier d'une souris à l'autre, nous avons intérêt à utiliser un tableau dynamique pour stocker l'état des boutons avec le nombre de répétitions. Le tableau dynamique est déclaré en global au-dessus du main :

 
Sélectionnez
static int*PRESS = NULL;

Le nombre des boutons est obtenu dans la boucle et dans la fonction de contrôle avec la fonction de la bibliothèque al_get_num_mouse_button() .

 
Sélectionnez
int nbbtn= al_get_num_mouse_button() ;

L'initialisation du tableau est faite à la première utilisation de la fonction de contrôle lorsque le pointeur est NULL :

 
Sélectionnez
int nbbtn = al_get_mouse_num_buttons();
if (PRESS == NULL){
    PRESS = (int*)calloc(nbbtn+1, sizeof(int));
    memset(PRESS, 0, sizeof(int)*(nbbtn + 1));
}

La fonction complète pour le contrôle des boutons souris est la suivante :

 
Sélectionnez
int is_mouse_pressed(ALLEGRO_MOUSE_STATE*mouse, int btn, int repeat)
{
    int res = 0;
    int nbbtn = al_get_mouse_num_buttons();
    // initialisation du tableau de contrôle
    if (PRESS == NULL){
        PRESS = (int*)calloc(nbbtn+1, sizeof(int));
        memset(PRESS, 0, sizeof(int)*(nbbtn + 1));
    }
    if (btn > nbbtn)
        btn = nbbtn;
    if (al_mouse_button_down(mouse, btn) && PRESS[btn] < repeat){
        PRESS[btn]++;
        res = 1;
    }
    else if (!al_mouse_button_down(mouse, btn))
        PRESS[btn] = 0;
    return res;
}

Elle retourne true si à partir de l'état state de la souris le bouton btn est pressé et que le nombre total de fois où il est pressé est inférieur à repeat . Elle retourne false sinon. Dans le programme suivant, nous contrôlons l'état de chaque bouton dans une boucle for avec deux répétitions autorisées pour chaque :

 
Sélectionnez
for (i=1; i<=nbbtn; i++)
    if (is_mouse_pressed(&mouse,i,2))
        al_append_native_text_log(textlog,"boutons %d presse\n",i);

Chaque indice i correspond à un bouton c'est pourquoi la boucle commence à 1. Voici le programme d'expérimentation complet :

Contrôler les répétitions de clics de la souris
Sélectionnez
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>

static int*PRESS = NULL;

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);
}
/*****************************************************************
*****************************************************************/
int is_mouse_pressed(ALLEGRO_MOUSE_STATE*mouse, int btn, int repeat)
{
    int res = 0;
    int nbbtn = al_get_mouse_num_buttons();
    
    // initialisation du tableau de contrôle
    if (PRESS == NULL){
        PRESS = (int*)calloc(nbbtn+1, sizeof(int));
        memset(PRESS, 0, sizeof(int)*(nbbtn + 1));
    }

    if (btn > nbbtn)
        btn = nbbtn;

    if (al_mouse_button_down(mouse, btn) && PRESS[btn] < repeat){
        PRESS[btn]++;
        res = 1;
    }
    else if (!al_mouse_button_down(mouse, btn))
        PRESS[btn] = 0;

    return res;
}
/*****************************************************************
*****************************************************************/
int main()
{
    ALLEGRO_DISPLAY*display;
    ALLEGRO_MOUSE_STATE mouse;
    ALLEGRO_KEYBOARD_STATE key;
    ALLEGRO_TEXTLOG * textlog;
    int i,nbbtn;

    if (!al_init())
        erreur("allegro init");

    if (!al_install_mouse())
        erreur("install mouse");

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

    display = al_create_display(800, 600);
    if (!display)
        erreur("display");

    // pour avoir une fenêtre console même sans débug
    textlog = al_open_native_text_log("test souris",
        ALLEGRO_TEXTLOG_MONOSPACE);
    al_append_native_text_log(textlog,
        "SOURIS TEST 2 / ESCAPE pour quitter :\n");

    nbbtn = al_get_mouse_num_buttons();
    do{

        // action souris
        al_get_mouse_state(&mouse);

        // état des boutons
        
        for (i = 1; i <= nbbtn; i++){
            if (is_mouse_pressed(&mouse, i, 2))
                al_append_native_text_log(textlog,
                "boutons %d presse\n", i);

        }

        // recup état du clavier
        al_get_keyboard_state(&key);

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

    al_destroy_display(display);
    free(PRESS);
    return 0;
}
/*****************************************************************
*****************************************************************/

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

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

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.