Forums Casio - Vos tutoriels et astuces

Index du Forum | Vos tutoriels et astuces | Tutoriels d'utilisation de gint
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10113
Défis: 130
Message
Posté le 15/07/2017 13:53

Tutoriels d'utilisation de gint :

Pas de commentaires sur ce topic ! Merci de poster par ici.

Bien le salut, développeur ! Vous êtes tombés sur une série de tutoriels d'utilisation d'un outil de développement d'add-ins sous Linux, le fxSDK, et plus particulièrement sa bibliothèque centrale, gint.

Pour reproduire les manips' réalisées ici, il vous faut au préalable avoir installé :
Un cross-compilateur pour la calculatrice ;
Le fxSDK (gint étant inclus dedans) ;
Un wrapper d'add-ins, celui-ci étant un vieux que j'ai forké de Kristaba.

Plutôt que de vous embêter avec un catalogue de fonctions qui ne serait amusant ni pour vous ni pour moi, je vous propose d'utiliser gint pour monter un petit jeu de stratégie en développant au fur et à mesure le code et les ressources. Bon, comme c'est pas un tutoriel de pixel art, je vous parachuterai les assets, mais c'est l'idée.

Il n'est pas question de donner tout le code et toutes les commandes shell (ce serait beaucoup trop indigeste), mais de s'arrêter sur le développement de différents points-clés intéressants, prétextes pour utiliser les fonctionnalités du fxSDK et pour parler un peu de game design. Si ça vous semble utile, je pourrai mettre le code sur un dépôt git.

Enfin, pour garder les différents posts (qui seront peut-être un peu longs) ensembles et dans l'ordre sur ce topic, je vous demanderai de ne pas commenter ici mais sur le topic suivant :

Tutoriels d'utilisation de gint (commentaires)

Sur ce, il est temps de passer aux choses sérieuses. Bonne lecture !

Tutoriels existant à ce jour (en cours)

01 — "Hello, World!", encore et toujours

La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.



Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10113
Défis: 130
Message
Citer : Posté le 15/07/2017 13:56 | #
01 — "Hello, World!", encore et toujours

Je vous propose de commencer cette série par l'écran d'accueil du jeu et un peu de gestion de projet. Normalement il faudrait coder le moteur d'abord, mais ce serait trop brutal. Voilà ce qu'on va réaliser à la place :


Image 1 : un accueil chaleureux, mais attention aux crabes !

Une police facile à dessiner, un titre vendeur (des monstres ! ) et un palmier mutant (qui a une bonne tête de boss de fin), rien de bien extravagant. J'ai ajusté les dimensions sur l'image pour que le texte passe partout (même si le joueur choisit comme nom WWWWWWWW, ce qu'au moins l'un de vous fera juste pour m'embêter).

À gauche donc, les trois sauvegardes du jeu. À droite, les informations sur la partie sélectionnée, ou une nouvelle partie. Les sauvegardes ce sera pas pour tout de suite, donc on va se coltiner une nouvelle partie à chaque test pendant quelques temps (quitte à commenter le menu pour aller plus vite).

Les ressources

Voici les ressources (assets) que l'on va utiliser : une icône pour l'add-in, l'image du titre avec le palmier, et la police de caractères pour écrire le texte. On ne va pas utiliser celle par défaut de gint, qui manque un peu de style. Les flèches sont dans la police, donc on a tout. Je le répéterai, mais pensez bien à toujours utiliser des images au format bitmap, et pas en monochrome (fxconv ne le supporte pas encore).

Vous pouvez trouver les trois fichiers sur mon serveur personnel, à l'adresse /gint-tuto/01. Ils sont enregistrés dans un format bitmap approprié.


Image 2: Une police qui a du caractère !

Comme vous pouvez le voir, la police qu'on utilise est vraiment une image. Elle est constituée d'une première ligne qui indique ses propriétés : le jeu de caractères (ici ASCII, donc tous les 128 caractères), la hauteur moyenne des lettres (pas utilisée directement par gint, mais peut être utile à d'autres applications), et le nom de la police. Sur la gauche, les traits noirs indiquent sur quelles rangées de pixels les caractères sont dessinés. Les pixels gris sur les glyphes de l'espace et des guillemets évite que fxconv ne saute l'espace ou ne coupe les guillemets au milieu.

Ce système est un peu archaïque et pas ce que j'ai fait de mieux. On verra s'il change plus tard. J'ai rajouté des caractères dans la portion entre 0 et 31, notamment les flèches dont j'ai parlé tout à l'heure. On utilisera ou modifiera peut-être le reste si on a besoin de quelques icônes.

Structure du projet

Je vais parler un peu de le méthode de compilation, donc voilà pour référence la tête de mon répertoire de travail :

assets # Les fichiers de ressources
├── font-island.bmp
├── title.bmp
└── ...
build # Les fichiers compilés (« objets »)
├── main.o
└── ...
include # Les en-têtes du code
├── moisland.h
└── ...
src # Le code source
├── main.c
└── ...
Makefile
moisland.g1a
moisland.bmp

Je travaillerai toujours dans le répertoire du projet, jamais dans un sous-répertoire, au moment d'écrire les commandes.

Hello, World!

C'est parti. Notre add-in tourne sous gint, et sera lancé prêt à partir comme ceux du SDK (en prime, on n'a plus les #pragma tous moches à la fin !). Notre "Hello, World!" est le suivant :

#include <display.h>
#include <keyboard.h>

int main(void)
{
    dclear();
    dtext(1, 1, "Hello, World!");
    dtext(1, 9, "gint talking to you.");
    dupdate();

    getkey();
    return 0;
}

Pour le réaliser, on a besoin des headers display.h (tout ce qui est affichage) et keyboard.h (gestion du clavier). Ensuite, on y va gaillardement :
dclear() efface la VRAM (mémoire vidéo de l'add-in), c'est l'équivalent de Bisp_AllClr_VRAM() ;
dtext(x, y, str) affiche une chaîne de caractères à la position indiquée, repérée en pixels. (0, 0) est en haut à gauche, ce qui sera le cas pour toutes les fonctions de dessin sans exception. C'est proche de PrintXY(), seule la couleur n'est pas donnée à chaque appel (configurée ailleurs). Les coordonnées indiquées définissent le point en haut à gauche du rectangle dans lequel le texte sera dessiné.
dupdate() affiche les contenus de la VRAM à l'écran, c'est identique à Bdisp_PutDisp_DD().
getkey() attend que l'utilisateur appuie sur une touche et renvoie le numéro de cette touche. Il y a un nom pour chaque touche, que vous pouvez trouver dans keyboard.h.

J'aimerais insister sur trois choses au sujet de getkey() :
getkey() attend. Le code qui suit (ici notre return) ne sera pas exécuté tant que l'utilisateur n'aura pas appuyé sur une touche, peu importe si ça lui prend des heures !
getkey() renvoie le code de la touche, alors que GetKey() modifie un pointeur qu'on lui passe en argument.
getkey() ne rafraîchit pas l'écran, contrairement à GetKey() qui appelle Bdisp_PutDisp_DD() avant de se mettre en attente.

Compiler tout ça va demander un peu de travail. Le processus se décompose en plusieurs étapes, avec une commande-type pour chaque étape (parfois pour chaque fichier) :
— Compiler indépendamment tous les fichiers sources en fichiers objets (.o).
— Convertir nos images et nos polices en fichiers objets aussi (.o).
— Réunir tous les objets dans un premier fichier d'add-in (.elf).
— Retirer toutes les informations du format ELF pour obtenir un binaire pur (.bin) ;
— Ajouter à cela l'en-tête avec l'icône de l'add-in pour obtenir le fichier final (.g1a).

Pour la première étape, on va donc créer le dossier build (si vous ne l'avez pas encore fait) puis compiler le fichier source à l'aide de gcc :

$ mkdir -p build
$ sh3eb-elf-gcc -c src/main.c -o build/main.o `fxsdk --cflags`

Les options passées à gcc sont les suivantes :
-c indique que l'on veut créer un fichier objet. Sans ça, gcc chercherait à créer tout de suite le fichier ELF.
src/main.c est le nom du fichier à compiler.
-o build/main.o indique que le fichier objet doit être placé, sous le nom main.o, dans le dossier build.
`fxsdk --cflags` indique qu'il y a d'autres options, et qu'elles peuvent être obtenues en exécutant fxsdk --cflags. Ce sont soit des options que le fxSDK donne à votre place, soit des choses spécifiques à l'utilisation de gint. Vous pouvez regarder, mais ça ne vous parlera que si vous connaissez bien GCC.

Ensuite, il faut convertir les images et les polices. On n'en a pas encore, donc on va sauter cette étape. Pour tout réunir dans le fichier ELF, on fait de nouveau appel à GCC :

$ sh3eb-elf-gcc build/main.o -o build/moisland.elf `fxsdk --cflags --libs`

Cette fois-ci, on veut bien un fichier ELF, donc plus besoin de -c. Les fichiers à réunir (on dit linker pour cette étape) sont tous nos fichiers objets, ici il n'y a que build/main.o. Quand on aura des images on les ajoutera à la commande. Pour cette étape, le fxSDK a encore d'autres options, qui sont données par fxsdk --libs. Attention à toujours placer fxsdk --libs à la fin de la commande !

Maintenant, il faut supprimer tout ce qui fait de ce fichier un ELF (qui est un format très utile pour le PC mais inconnu de la calculatrice) pour ne garder que le code et les données. Pour ça, on utilise objcopy. Je ne vais pas donner tous les détails, mais -O binary spécifie qu'on veut un binaire pur, et les -R indiquent qu'on supprime au passage deux-trois choses (sections) inutiles.

$ sh3eb-elf-objcopy -R .comment -R .bss -O binary build/moisland.elf build/moisland.bin

On est presque au bout ; il ne nous reste plus qu'à en faire un g1a en bonne et due forme. Pour cela, j'utilise le g1a-wrapper en indiquant au passage qu'il faut ajouter une belle icône à notre add-in :

$ g1a-wrapper build/moisland.bin -o moisland.g1a -i moisland.bmp

Et c'est terminé ! Il ne reste qu'à l'envoyer sur la machine via votre méthode préférée (pour moi, ce sera p7 send -f moisland.g1a), et on peut observer le résultat attendu :


Image 3 : Yeah!

Ce procédé est beaucoup trop long et complexe pour un programmeur fainéant tel que moi, donc j'ai tout automatisé avec un Makefile. Si vous ne savez pas comment en écrire, il existe plein de tutos sur Internet à ce sujet, et je ne saurais en écrire de meilleur ; je vous y renvoie donc. Pensez à aller les lire, car le Makefile est un allié puissant !

Sur ce, il est temps de passer aux choses sérieuses !

Dessiner un menu principal

Grâce à notre image de titre et à notre police, on peut réaliser facilement le début de notre menu principal :

int main(void)
{
    extern image_t assets_title;
    extern font_t assets_font_island;

    text_configure(&assets_font_island, color_black);

    dclear();
    dimage(0, 1, &assets_title);
    dtext(9, 31, "NEW GAME");
    dtext(9, 39, "NEW GAME");
    dtext(9, 47, "NEW GAME");
    dupdate();

    getkey();
    return 0;
}


Image 4 : Le menu qui prend forme

Il y a pas mal de choses à dire sur cette nouvelle fonction. D'abord, les deux premières lignes :

extern image_t assets_title;
extern font_t assets_font_island;

Ce sont des déclarations de variables. La première est du type image_t (probablement notre image de titre), et la seconde du type font_t (probablement notre... enfin, vous voyez ). Les deux sont marquées extern, ce qui est très important : ça signifie qu'on ne crée pas de variable, on indique seulement au compilateur que ces variables existent ailleurs et on lui promet de les lui fournir quand il en aura besoin (c'est au moment de créer le fichier ELF !). Voilà comment on récupère nos ressources dans le programme ; vous verrez, vous le ferez tout le temps.

Ensuite, on a l'appel à la fonction text_configure(), qui modifie les paramètres de dessin du texte :

void text_configure(font_t *font, color_t color);

— Le premier paramètre de cet appel est la nouvelle police à utiliser, qui est celle que nous avons enregistrée dans le fichier bitmap. Cela peut vous paraître curieux, mais la fonction demande un pointeur, et on prend donc l'adresse de la variable externe. C'est en fait à la fois un avantage de performance et obligatoire de toute façon compte tenu de la forme du type font_t. Cela sera toujours le cas pour toutes les variables issues de la conversion de ressources.
— Le second paramètre est la couleur à utiliser pour dessiner, qui est une valeur de l'énumération color_t. Les valeurs valides de ce type lorsqu'on dessine en monochrome (comme ici) sont color_black, color_white, color_none (transparent, autant dire que ça n'a aucun intérêt ici), et color_invert. Cette dernière « couleur » est vue par gint comme un opérateur qui inverse la couleur des pixels, c'est l'équivalent direct de ML_XOR.

Avec ça, on est parés pour écrire du texte avec notre nouvelle police. Il n'y a rien à changer une fois que cette configuration est faite, dtext() fonctionne comme avant mais utilise notre police personnalisée. Rappelez-vous bien que les coordonnées fournies à dtext() sont toujours celles du point en haut à gauche du texte.

Il ne nous reste plus que le dessin de l'image, dont vous devriez comprendre sans problème le fonctionnement maintenant :

dimage(0, 0, &assets_title);

Encore une fois, les coordonnées du dessin sont les premières paramètres (et ce sera toujours le cas), et comme je l'avais annoncé, il faut passer un pointeur sur l'image. Le type image_t contient toutes les informations dont gint a besoin pour réaliser le rendu, donc vous n'avez plus besoin d'indiquer les dimensions à la main !

Et bien sûr, on n'oublie pas d'actualiser l'écran avec dupdate(), ce que getkey() ne fait pas pour nous. Et c'est gagné !

Convertir les ressources pour le programme

Si vous avez essayé de compiler ce programme, vous avez probablement été insulté par le compilateur en ces termes :

build/main.c.o: In function `_main':
main.c:(.text+0x7c): undefined reference to `_assets_font_island'
main.c:(.text+0x88): undefined reference to `_assets_title'
collect2: error: ld returned 1 exit status

Un langage un peu cryptique pour vous rappeler que vous ne pouvez pas linker le fichier ELF sans fournir les variables dont vous avez annoncé l'existence. Vous vous êtes rendu coupable de ne pas tenir votre parole envers votre compilateur (et la milice locale des compilateurs est sans doute déjà devant chez vous ) !

Il va falloir effectuer l'étape qu'on a sauté tout à l'heure : convertir nos ressources en fichiers objets. C'est là que le fxSDK intervient, et il a tout prévu. On a une image et une police, donc...

$ fxconv -image assets/title.bmp -o build/title.bmp.o -n assets_title
$ fxconv -font assets/font-island.bmp -o build/font-island.bmp.o -n assets_font_island

— On a d'abord un argument de « mode de conversion » qui indique à fxconv le type de ressource qu'on lui fournit : -image ou -font ici. Notez qu'il existe aussi -binary pour permettre l'accès aux données sans conversion.
— Ensuite, le nom du fichier à convertir, tiré tout droit de notre dossier d'assets ;
— L'argument -o <fichier> indique quel est le fichier de sortie, ie. le résultat de la conversion en fichier objet ;
— Et enfin, -n <variable> indique à fxconv le nom sous lequel on veut utiliser la variable. Il faut que ce soit le même que dans le code !

Notez par ailleurs que fxconv ne se contente pas de créer un fichier objet contenant les données des assets : il effectue des conversions de format pour transformer l'image bitmap en image_t et la police bitmap en font_t. Ces deux nouveaux formats sont optimisés pour que gint puisse effectuer le dessin rapidement !

Maintenant que tout ceci est fait, on peut linker de nouveau notre kernel en prenant bien garde à ajouter nos nouveaux fichiers objets à la commande :

sh3eb-elf-gcc build/main.c.o build/font-island.bmp.o build/title.bmp.o -o build/moisland.elf `fxsdk --cflags --libs`

Il faut de nouveau effectuer les deux étapes finales, objcopy et g1a-wrapper, mais ici rien ne change. Comme suis un peu flemmard, j'ai tout automatisé avec mon Makefile, y compris la conversion des ressources avec fxconv. Je vous invite à faire pareil !

Le curseur et la description

Il ne nous reste plus qu'à afficher le curseur (les deux flèches) et la description de l'entrée sélectionnée à droite. Lorsqu'il y aura des sauvegardes, on pourra y mettre le nom du joueur, son niveau et l'avancement dans l'histoire, mais pour l'instant on n'a que des nouvelles parties, donc on va simplifier un peu notre code :

int main(void)
{
    extern image_t assets_title;
    extern font_t assets_font_island;
    int entry_selected = 0;

    text_configure(&assets_font_island, color_black);

    while(1)
    {
        dclear();
        dimage(0, 1, &assets_title);
        dtext(9, 31, "NEW GAME");
        dtext(9, 39, "NEW GAME");
        dtext(9, 47, "NEW GAME");

        dline(66, 30, 66, 54, color_black);
        dtext(69, 33, "Start from");
        dtext(69, 39, "the beginning");
        dtext(69, 45, "of the story.");

        dtext(4, 31 + 8 * entry_selected, "\x01");
        dtext(52, 31 + 8 * entry_selected, "\x02");
        dupdate();

        switch(getkey())    
        {
        case KEY_UP:
            if(entry_selected) entry_selected--;
            break;
        case KEY_DOWN:
            if(entry_selected < 2) entry_selected++;
            break;
        case KEY_EXE:
            return 0;
        }
    }
    return 0;
}


Image 5 : C'était pas si difficile !

Il y a plusieurs nouvelles choses à remarquer ici :
— J'ai utilisé la fonction dline(int x1, int y1, int x2, int y2, color_t color) pour tracer une ligne. Les deux extrêmités sont incluses dans la ligne !
— Les flèches sont obtenus par les chaînes de caractères "\x01" et "\x02". Il s'agit de séquences d'échappement qui permettent d'introduire un octet de valeur quelconque, ici 1 et 2. C'est en effet les positions des flèches dans la table de la police.
— J'ai récupéré la valeur de getkey() directement dans un switch, mais j'aurais pu la stocker dans une variable. On prend ensuite une décision appropriée selon la touche pressée (rien d'inattendu ici).
— La boucle redessine l'écran chaque fois qu'on appuie sur une touche, même s'il n'y a rien à changer (comme quand j'appuie sur [4] par exemple), ce qui n'est pas génial. Il suffirait d'introduire une petite variable de contrôle int redraw pour éviter ça, je voulais simplement éviter de surcharger le code.
— Arrêter le programme quand on appuie sur [EXE] peut sembler bizarre, mais en pratique on mettrait le menu dans une fonction en on renverrait entry_selected, donc c'est normal !
— Pour le reste du jeu je préférerai utiliser [SHIFT] mais ce n'est pas encore possible avec getkey() qui s'en sert comme modifieur pour faire des combinaisons comme [SHIFT][×] → [{]. On pourra faire mieux plus tard !
— Même si la boucle est infinie, on peut toujours quitter l'add-in en appuyant sur [MENU], puis revenir ensuite, comme avec le GetKey() de fxlib.

Conclusion

C'est un menu très schématique et qu'on pourrait facilement améliorer : animation d'entrée du titre, déplacement progressif du curseur, peut-être un fondu d'entrée/sortie sur la description, une bordure décorée au lieu d'une simple ligne, gérer la touche SHIFT, et mieux encore.

Ce tutoriel n'est qu'une brève introduction sur les fonctions fournies par gint mais décrit la grande majorité de ce qui peut se produire lors d'une compilation. J'en parlerai très peu dans la suite, laissant ce travail fastidieux à un Makefile bien rodé. Ce sera donc probablement moins technique.
----------------------------------
La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.


Index du Forum | Vos tutoriels et astuces | Tutoriels d'utilisation de gint
Publicité et partenaires
Casio Education
Casio éducation

TI-Planet
Casiopeia
Casiopeia
CasioFan, la communauté ClassPad
CasioFan
CodeWalrus
CodeWalrus

Planète Casio v42 © créé par Neuronix et Muelsaco 2004 - 2015 | Il y a 37 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements
Rugby Manager | Jeu de basket | Jeu de handball | Jeu de tennis | Nova Raider | Réparation téléphone | Soccer Rush | Tasty Tale

Planète Casio est un site communautaire indépendant, géré bénévolement et n'est donc pas affilié à Casio | Toute reproduction de Planète Casio, même partielle, est interdite
Les fichiers, programmes et autres publications présents sur Planète Casio restent la propriété de leurs auteurs respectifs et peuvent être soumis à des licences ou des copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd