Aller au menu - Aller au contenu

Icône Introduction à la GUI 1/2

Avatar
Mise à jour : 23/03/2011
Difficulté : Facile Facile Creative Commons BY-SA
1 621 visites depuis 7 jours, dont 100 sur ce chapitre classé 80/786
Dans ce chapitre, nous allons aborder quelque chose de fondamental pour quasiment tout programme interactif : l'interface graphique ! Étant donné qu'il y a beaucoup de choses à dire, ce sujet nécessite 2 chapitres :
  • Le premier est dédié à tous les à-côtés, les choses indispensables à l'interface mais qui ne la constituent pas vraiment
  • Le deuxième, quant à lui, vous apprendra à créer l'interface proprement dite (boîte de dialogue, bouton, menu déroulant,...)

Et commençons donc, par le commencement.
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

L'organisation interne

La première chose à savoir est que dans Irrlicht, tout ce qui touche à la GUI est regroupé dans le namespace irr::gui. Comme vous pouvez le voir sur la page de documentation, nous allons y trouver énormément de choses permettant de mettre en place et de manipuler une interface. Celle-ci sera contenue dans la classe IGUIEnvironment qui permet donc de la gérer (ajout d'un bouton, d'un texte, etc...). Cette classe est accessible par le device. Le plus pratique est de créer un pointeur sur l'instance au début du programme pour pouvoir la manipuler plus simplement par la suite :

Code : C++
1
irr::gui::IGUIEnvironment *GUI = device->getGUIEnvironment();


Les éléments de la gui ne sont, par définition, pas des "scene nodes", et donc pas pris en charge par le gestionnaire de scène (scene manager). Ce qui signifie que la mise à jour de la GUI est indépendante de celle du scene manager. Il faut par conséquent rajouter cette ligne à l'intérieur de la boucle de rendu :

Code : C++
1
gui->drawAll();


De cette manière, tous les éléments qui auront été ajoutés à l'environnement de GUI (l'instance de irr::gui::IGUIEnvironment) seront automatiquement mis à jour. Maintenant que vous voyez à-peu-près l'organisation de la chose, passons à la pratique sans plus attendre. Mais pour cela il va nous falloir une scène pour faire nos tests. Reprenons celle du chapitre sur l'éclairage en l'épurant de ce qui ne nous est pas nécessaire :

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <IRR/irrlicht.h>

int main(void)
{
    irr::IrrlichtDevice *device = irr::createDevice (
        irr::video::EDT_OPENGL,
        irr::core::dimension2d<irr::u32>(800,600),
        32,false,true,false,0);
    irr::video::IVideoDriver* driver = device->getVideoDriver ();
    irr::scene::ISceneManager *sceneManager = device->getSceneManager ();

    // recuperation du pointeur GUI et creation d'une camera
    irr::gui::IGUIEnvironment *gui = device->getGUIEnvironment();
    sceneManager->addCameraSceneNodeFPS();

    // ajout du node contenant la piece
    irr::scene::IMeshSceneNode *Nroom = sceneManager->addMeshSceneNode(
        sceneManager->getMesh("room.3ds"));

    // parametrage du material associe a la piece
    sceneManager->getMeshManipulator()->
        makePlanarTextureMapping(Nroom->getMesh(), 0.004f);
    Nroom->setMaterialTexture(0, driver->getTexture("rockwall.jpg") );
    Nroom->setMaterialFlag(irr::video::EMF_LIGHTING, false);


    while (device->run ())                        // la boucle de rendu
    {
        driver->beginScene(true, true,
            irr::video::SColor(255,255,255,255));
        sceneManager->drawAll();
        gui->drawAll();
        driver->endScene();
    }

    device->drop ();
    return 0;
}


Et voilà, rien de nouveau sous le soleil. Maintenant que nous avons ce qu'il faut, on va pouvoir entrer dans le vif du sujet.

Afficher un texte à l'écran

Et le vif du sujet commence par quelque chose de facile : afficher du texte à l'écran. La fonction permettant de faire ça est irr::gui::IGUIEnvironment::addStaticText. Comme d'habitude, pour savoir à quoi correspondent tous les paramètres il vous suffit de jeter un coup d'oeil à la page de la documentation. Voici un exemple d'utilisation :

Image utilisateur
Code : C++
1
2
3
gui->addStaticText(L"Apprendre Irrlicht les yeux fermés avec le\n"
    " 'Petit guide d'Irrlicht' de Kevin Leonhart",
    irr::core::rect<irr::s32>(100,20,400,60), true, true, 0, -1, true);


Vous pouvez cliquer sur l'image pour l'agrandir. La première chose qui saute aux yeux est que le texte est tout moche et illisible. Et oui, ça parait évident mais la mise en page et l'habillement comptent énormément. En l'occurrence, la taille de la police est la première chose à mettre en cause, mais nous y viendrons un peu plus loin.

Si vous ne validez pas le retour à la ligne (quatrième paramètre), vous ne pourrez pas faire de retours manuels (\n) !


La seconde chose qui doit vous turlupiner, c'est ce L devant la chaîne de caractères. il signifie en fait que vous pouvez y mettre des caractères en dehors de la table ASCII standard, étant donné que ce ne sont pas des chars qui vont les contenir mais des wchar_t. À nous donc les caractères accentués ou étranges tels que é, ï, ç ou encore ù. Pour en savoir plus :

Pensez à mettre ce L devant la chaîne !
Souvent, on l'oublie et on a le droit à une jolie erreur de compilation bien agaçante.


Tant que nous y sommes, voyons comment précisément ajouter des accents dans nos textes. Si vous essayez d'écrire une ligne de code dans le style :

Code : C++
1
irr::core::stringw chaine(L"Le problème est résolu");

Le compilateur va vous renvoyer une belle erreur de ce genre :

Citation : Compilateur
converting to execution character set: Illegal byte sequence


Cela est dû au fait que le code source ne peut pas contenir des caractères en dehors de la table ASCII standard, à part les commentaires puisqu'ils ne sont pas pris en compte par le compilateur. La solution consiste alors à désigner les caractères par leurs valeurs dans la table unicode plutôt que de les écrire directement.

Code : C++
1
2
3
wchar_t message[100];
// swprintf est l'équivalent de sprintf pour des wchar_t.
swprintf(message, 100, L"Le probl%cme est r%csolut", 232, 233);


Je vous laisse le soin de consulter une page de manuel pour connaître précisément le fonctionnement de swprintf. La table unicode est immense, mais finalement il ne nous manque pas beaucoup de caractères pour être heureux. Cette page devrait suffire à combler ces quelques manques : morceau de la table unicode sur Wikipedia. Reste encore à savoir comment lire la valeur d'un caractère dans ce tableau. Pour ce faire :
  • Commencez par repérer la case qu'il vous faut
  • Ensuite, prenez le nombre dans la case de la colonne située tout à gauche
  • Rajoutez-lui finalement le chiffre (ou la lettre) de la toute première ligne

Et vous avez la valeur en hexadécimal ! Pour le 'é' par exemple, ça nous donne 00E + 8 = E8 (les 0 en début n'ont pas d'importance). Il ne reste plus qu'à convertir ça en décimal. Quasiment toutes les calculatrices (informatiques) le font mais rien ne vous empêche de le faire à l'ancienne.

Les polices de caractère

Pour notre premier essai, nous n'avions rien spécifié concernant la police de caractères. Par conséquent, c'est la police interne par défaut d'Irrlicht qui a été utilisée. Vous l'aurez remarqué, celle-ci est un peu petite. Eh bien mauvaise nouvelle : il n'existe aucun moyen pour changer dynamiquement la taille d'une police. La seule manière de s'en sortir est donc d'en charger une plus grosse.

Les polices sont chargées à partir de fichiers images, alors on va aller faire un tour dans le dossier media du SDK pour aller en récupérer une. Prenons par exemple le fichier "fontlucida.png". La prochaine étape consiste à charger ce fichier. Il faut déjà savoir qu'une police est contenue dans une classe de type irr::gui::IGUIFont (Font signifie police de caractères en anglais). La méthode permettant de charger une police est la suivante : irr::gui::IGUIEnvironment::getFont. Et elle s'utilise de cette manière :

Code : C++
1
irr::gui::IGUIFont *font = gui->getFont("fontlucida.png");

Où "gui" est le pointeur que l'on a défini au début de ce chapitre, et dont l'unique paramètre est le nom du fichier à charger. Charger la police est la première étape, maintenant il va falloir spécifier que c'est celle-ci que nous voulons utiliser. Cela peut se faire indépendamment pour chaque élément de la GUI, ou en mettant cette police comme police par défaut, ce que nous verrons plus loin. Reprenons donc le code précédent en indiquant cette fois que l'on veut changer la police :

Code : C++
1
2
3
4
5
irr::gui::IGUIStaticText *texte = gui->addStaticText(L"Apprendre Irrlicht les yeux fermés avec le\n"
    " 'Petit guide d'Irrlicht' de Kevin Leonhart",
    irr::core::rect<irr::s32>(100,20,400,60), true, true, 0, -1, true);
irr::gui::IGUIFont *font = gui->getFont("fontlucida.png");  // chargement de la police
texte->setOverrideFont(font);                               // utilisation de la police

Image utilisateur

Et voilà le travail, admirez le résultat sur l'image de droite. Ceci dit, vous remarquerez que la taille du cadre ne correspond pas à celle du texte. En l'occurrence il est trop large. On pourrait modifier les dimensions du cadre "à la main" jusqu'à ce qu'on trouve celles qui sont les mieux adaptées, mais il existe une méthode bien plus rapide. Vous l'avez peut-être même déjà devinée si vous vous êtes attardé sur la page de documentation de la classe irr::gui::IGUIFont. La méthode getDimension permet de récupérer directement les dimensions du texte passé en paramètre. Testons sans plus attendre :

Code : C++
1
2
3
4
5
irr::core::dimension2d<irr::u32 > tailleTexte = font->getDimension(L"Apprendre Irrlicht"
    " les yeux fermés avec le\n 'Petit guide d'Irrlicht' de Kevin Leonhart");

std::cout << "Largeur : " << tailleTexte.Width << std::endl;
std::cout << "Hauteur : " << tailleTexte.Height << std::endl;

Et la console affiche :

Code : Console
Largeur : 261
Hauteur : 36


Reprenons donc le code vu précédemment en changeant les valeurs du cadre, mais avec une petite subtilité toutefois. Ces valeurs sont précise à un pixel près, hors la bordure empiète largement sur l'intérieur du cadre. Rajoutons donc 3 pixels de chaque coté pour donner un peu d'espace :

Code : C++
1
2
3
gui->addStaticText(L"Apprendre Irrlicht les yeux fermés avec le\n"
    " 'Petit guide d'Irrlicht' de Kevin Leonhart",
    irr::core::rect<irr::s32>(100,20,100+267,20+42), true, true, 0, -1, true);

Image utilisateur


Ce qui nous donne l'image de droite.

Le curseur

On enchaîne avec quelque chose de plus simple en voyant un peu ce qu'on peut faire de ce cher curseur. Tout se trouve dans la classe irr::gui::ICursorControl. Comme vous pouvez le voir, on peut :
  • changer et récupérer sa position
  • le rendre visible/invisible et le savoir

Pour mieux se rendre compte de la manière d'utiliser ces méthodes, nous allons voir un exemple. Déjà, il faut pouvoir accéder à l'instance de ICursorControl.

Code : C++
1
irr::gui::ICursorControl *curseur = device->getCursorControl();

Ensuite nous pouvons mettre en application tout ce qu'on a dit plus haut. Par exemple, j'imagine que le curseur doit vous gêner quand vous vous déplacez avec une caméra mobile. Faisons-le disparaître !

Code : C++
1
curseur->setVisible(false);

En mettant true comme paramètre, nous aurions rendu le curseur visible (comme il l'est par défaut). Les autres méthodes étant aussi simples que celle-ci et présentant moins d'intérêt pour vous, je vous laisse le soin de les examiner par vous-même.

Un titre accrocheur

Voyons maintenant une chose simple à réaliser mais très pratique, modifier le titre de la fenêtre. La fonction pour ce faire est la suivante : irr::IrrlichtDevice::setWindowCaption. Et elle s'utilise de cette manière :

Code : C++
1
device->setWindowCaption(L"Tagada");

Image utilisateur

Ce qui donne l'image visible sur la droite (enfin tout dépend de votre environnement graphique bien sûr). N'en restons pas là et servons nous de cette barre de titre pour afficher tout un tas d'informations intéressantes. Et plus particulièrement les deux suivantes :
  • Le nombre de FPS
  • La position de la caméra




Les FPS


Il existe une méthode qui permet de récupérer directement le nombre de FPS. Elle appartient au driver (la classe irr::video::IVideoDriver). Étant donné que le nombre de FPS change constamment, il faut qu'on change le titre de la fenêtre constamment aussi. Nous allons donc placer le code suivant à l'intérieur de la boucle de rendu :

Code : C++
1
2
irr::core::stringw titre = driver->getFPS();
device->setWindowCaption(titre.c_str ());


Comme vous pouvez le voir sur la première ligne, nous commençons par créer une chaîne de caractères made in Irrlicht. Elle s'utilise comme une std::string, vous ne devriez donc pas être trop dépaysé. La principale différence est qu'elle manipule des wchar_t et non des char, comme expliqué dans la deuxième sous-partie de ce chapitre. Nous allons directement initialiser cette stringw avec le nombre de FPS. Ainsi, il n'y a plus qu'à la mettre en titre de la fenêtre et le tour est joué.

Dernière petite difficulté : si on regarde le prototype de setWindowCaption, on s'aperçoit que cette méthode attend un pointeur sur un wchar_t en paramètre et pas un pointeur sur une stringw. On effectue donc la conversion avec la méthode c_str qui renvoie un pointeur vers le tableau de wchar_t (comme pour une std::string classique).



La position de la caméra


Si vous avez compris tout ce qui précède, vous ne devriez pas avoir de mal avec ce qui va suivre. Pour commencer, avant de pouvoir afficher la position de la caméra, il faut bien sûr la connaître. Et comme d'habitude, nous avons la chance d'avoir une fonction toute faite qui s'occupe de tout. Il s'agit de irr::scene::ISceneNode::getPosition. Cette méthode n'est pas spécifique à la caméra, elle est accessible pour tous les nodes. Donc si vous voulez savoir précisément où se trouve le cocotier que vous avez mis sur votre plage virtuelle, vous savez ce qu'il vous reste à faire. ;)

Code : C++
1
2
irr::core::vector3df posCam;
posCam = camera->getPosition();

À la première ligne, nous créons une instance de irr::core::vector3d<T>. Il s'agit tout simplement d'une classe de vecteurs, et nous allons nous en servir pour stocker les coordonnées du node. Il va falloir d'ailleurs créer ce node si vous utilisez le code fourni au début du chapitre, car il est pour l'instant inaccessible. À ce stade, on pourrait sortir les coordonnées dans la console. Par exemple :

Code : C++
1
2
3
std::cout << "X : " << posCam.X << std::endl;
std::cout << "Y : " << posCam.Y << std::endl;
std::cout << "Z : " << posCam.Z << std::endl;

Ce qui donnerait si vous n'avez rien modifié :

Code : Console
X : 0
Y : 0
Z : 0


Mais ce qui nous intéresse, c'est d'arriver à l'afficher dans le titre de la fenêtre. Pour ce faire, on va commencer par créer une stringw comme tout à l'heure :

Code : C++
1
irr::core::stringw titre = L"X = ";


Puis, comme pour une std::string classique, on va tout simplement rajouter les morceaux qui nous intéressent :

Code : C++
1
2
3
4
5
titre += posCam.X;
titre += " Y = ";
titre += posCam.Y;
titre += " Z = ";
titre += posCam.Z;


Et voilà le travail, il ne reste plus qu'à changer le titre :

Code : C++
1
device->setWindowCaption(titre.c_str());

Image utilisateur

Ce qui donne par exemple l'image de droite. Finalement ce n'est pas bien dur, mais plutôt long. Et comme nous préférons y voir clair d'un coup d'œil, raccourcissons ce code. En l'occurrence l'astuce consiste à travailler directement avec des tableaux de wchar_t plutôt qu'avec des stringw. Ce qui nous donne :

Code : C++
1
2
3
wchar_t titre[100];
swprintf(titre, 100, L"X : %f Y : %f Z : %f", posCam.X, posCam.Y, posCam.Z);
device->setWindowCaption(titre);
Même si on ne peut pas réellement parler d'interface en temps que telle, ce chapitre est riche en nouveautés intéressantes et va nous servir pour la suite. Notamment l'affichage des coordonnées de la caméra qui est idéal pour repérer les coordonnées exactes d'un endroit de la scène.
Chapitre précédent Sommaire Chapitre suivant

Partager

10 commentaires pour "Introduction à la GUI 1/2"
Note moyenne : 3.60 / 4 (122 votes)
Pseudo Commentaire
Hors ligne scascar # Posté le 28/09/2008 à 20:21:44

oui en effet il y a une erreure entre gui et GUI je pense ^^
Hors ligne Poppy # Posté le 29/10/2008 à 17:57:36
Avatar

Études : ENSIIE

Salut !

Tout d'abord merci pour ton super tuto qui me permet de me lancer dans la programmation de ce super moteur 3D qu'est Irrlicht.

Je voulais juste te signaler que ton lien vers la table de caractères Unicodes de Wikipedia n'est plus à jour.

Voilà, bonne continuation à toi :) Et encore merci.

"Dieu ne joue pas aux dés." Einstein
"Un seul être vous manque, et tout est dépeuplé." Lamartine
 
Hors ligne Bachir ElMagnifico # Posté le 16/12/2008 à 22:44:00
Avatar
Groupe : Bannis
Flux RSS

gui->drawAll(); doit etre les begiscene/endscene ou 'importe ou das la boucle?

Image utilisateur
 
Hors ligne CSN22 # Posté le 20/04/2010 à 22:31:43
#include "zérozième.z"
Avatar

Tout d'abord, Bravo ! et même Bravissimo !
Je suis complètement débutant en programmation, et après quelques crises de nerf et beaucoup d'acharnement, j'ai fini par mettre en application ton tutoriel sur Irrlicht. Je n'en suis pas encore à la moitié, mais je tenais à te dire combien j'admirais que tu acceptes de donner bénévolement du temps pour d'autres. Dans notre monde ou chacun s'occupe de sa petite vie personnelle sans trop se soucier des autres, ça fait du bien de voir qu'une façon de penser plus altruiste n'est pas en voie d'extinction (ça m'évite de penser que je suis un dinosaure !).
Ton tutoriel est très réussi, très clair dès qu'on a quelques notions de programmation (si j'ai un peu ramé, c'est parce que mes notions de programmation en C++ et autre sont assez limitées...). Encore merci pour le temps et l'investissement que tu y as mis. J'en profite pour remercier aussi les autres zéros.
Et... longue vie aux dinosaures !

Citation célèbre :
"10001010111010100010111010110100110100010100000111011111110010101001"
... euh... si quelqu'un comprend ça, qu'il n'hésite pas à me l'expliquer ! Là j'avoue que je me sens un peu perdu ;)
 
Hors ligne Jagang # Posté le 17/06/2010 à 20:07:10
Oui mais non
Avatar

Avis : Bon

Ville : Les echelles
Pays : France métropolitaine

Bonjour
Tout d'abord super tuto.
J'ai trouvé, après maint essai, une petite manipulation permettant de se passer de la conversion en hexa des caractères étendus.
  • D'abords, créé un projet (sans blague !)
  • Ensuite, créer et enregistrer les fichiers où l'on veux utiliser des caractères étendus
  • Ouvrir les fichiers créé avec un éditeur tel que Notepad++
  • Aller dans le menu encodage et choisir "Convertir en UTF-8"
  • Puis enregistrer le fichier converti
  • Pour finir, l'IDE devrais vous informer que le fichier à été changé et vous demande si vous voulez le recharger. Validez.

Vous pouvez désormais écrire des chaines avec des caractères étendus sans devoir aller les ajouter manuellement.

Cordialement
Jagang

PS: Il est préférable de convertir le fichier avant d'y insérer les caractères étendu sous risque de voir se transformer ses 'ô' en 'ô' par exemple.

"C'est parce que la vitesse de la lumière est plus rapide que celle du son que certains peuvent paraîtrent brillants jusqu'à ce qu'ils ouvrent la bouche." Coluche
Image utilisateur

 

Voir tous les commentaires