Monday, April 30, 2018

Même pas peur

J.l.n a grandi. 5 ans maintenant. Il ne se contente plus de regarder papa raconter des Zelda comme si c'était un livre interactif. Il a envie de prendre la manette. Oh, bien sûr il a déjà un peu chipoté dans Rayman Origins ou New Super Mario Bros Wii, mais surtout en mode "bac à sable" ou en mode "ouhla, ça devient dur, j'me mets en bulle".

Son premier vrai jeu est donc Boing! Docomodake un petit jeu mignon qui m'avait été recommandé par Pierrick, à mi-chemin entre lemmings et Rick Dangerous à la sauce bisounours (comprenez, sans les pics qui sortent du sol, et sans que les lemmings n'avancent sans vous). Tout à fait adapté à son niveau, même si moi, perso, le jeu m'avait lassé très rapidement tellement tout était lent dedans.

Pourtant, niveau interactions, c'est assez riche. On bouge avec la croix, on fait apparaître des petits champignons qu'on déplace pour faire des ponts, des échelles, des contre-poids, etc. on bouge encore. On en ramasse pour les lancer sur les (rares) ennemis. Il avance pas trop mal.

Puis l'autre jour il fait un blocage "papa, il faut que tu tires sur la libellule". Papa gribouillait, il encourage le fiston à essayer lui-même "tu te prépares, tu vois, tu t'avances". Rien n'y fait. "mais j'ai peur"!

Et là, j'ai compris la grande puissance du jeu vidéo pour affronter ses peurs. "Regarde, le jeu il est là. Toi tu es ici. Tu es en sécurité: il ne peut rien arriver. Au pire, il y a des petits pixels qui vont un peu changer de couleur, mais tu ne risque rien. Allez, c'est l'occasion parfaite pour apprendre à être plus fort que ta peur".

Beaucoup plus difficile pour lui, par contre, c'est de faire face à la frustration quand il se rend compte qu'il a loupé un coffre (généralement en fin de niveau) et qu'il est impossible de faire marche arrière parce que le développeur a voulu que seul celui qui réfléchit et planifie puisse l'avoir. Là, au bout d'un quart d'heure de concentration pour passer tous les obstacles et récupérer les pièces d'or, ça peut facilement éclater en crise de pleurs qui confirmeront qu'il est temps de jouer à autre chose...

Sunday, April 29, 2018

definition files in geds3

The refactoring keeps going. I'm about to show the "load a map" feature, and then I'll have to show "how characters interact with the ground through properties".

Meanwhile, I'm digging what sort of additional definitions a "Behaviour Editor on DS" should read, and how that would be translated into .h files that convert the "level 2" scripts into plain "level 0 scripts" that can be parsed by the runtime engine on the DS. I'd allow e.g. BEDS to do more lookups in symbol tables while all symbols should normally be translated into values or expressions in level 0 when loading levels in a game.

Maybe you wonder "but what is level 1?" ... well, level 1 is what I'm doing right now with Bilou School Rush, with the C pre-processor expanding symbols for us so that I can write "$RUNLEFT->$RUNRIGHT on eDpad [D_RIGHT]" instead of "statl12->statr13 on event0 [v2 $20 &]".

Sunday, April 22, 2018

to $(AR) or not to $(AR)

During the preparation of the "controllers" tutorial, I faced a weird linking problem. I wanted to split the huge "controllers.cxx" file so that every controller would be in a sub-file that could be compiled separately and no longer depend on the code from other controllers unless there is a good reason for that. And all of suddens, I had no more factories registered.

Just before that, I had reviewed the factory registering system so that it was enough to just write "MomentumFactory mf("momentum");" as a top-level declaration to get everything up and running. But that meant there was no more reference from the main .o files of the game/demo that would require .o files with the factory code (and instance declaration), so they wouldn't be packed in, and certainly wouldn't be initialized either.

So I started thinking about weird mechanisms invoking bool pointers to dummy variables, or no-code functions, and even why not "UsingPlatformer" empty class that would extend UsingMomentum, UsingDpad and others... Then I realized that all this happened because the .o files (compilation output, that is. Equivalent of your *.OBJ if you're on MS-DOS) are packed into a static library and only pulled to populate the .NDS file on-demand at link-time. If instead I explicitly say "link Demo/*.o Controllers/*.o", they are put into the binary and no trick is needed anymore.

Wednesday, April 18, 2018

shell functions

For so long, I have been creating aliases for my shell. "dir" would be "ls -la" and things like that. TCSH even had ways to retrieve some attributes to the aliases. building 20 student programs and testing them would have merely required me to type N (for next), B (for build) and T (to launch simple tests). Do I need to fix something to better evaluate their program ? B again, then T again.

But it had its drawbacks, and it was pretty ugly to code. Nowadays, I'd do that with shell function instead. Rather than trying to rewrite the statement, it truly allows me to extract all the arguments (either separately or together) and then calling one or more commands

cl()
{
    color.pl $* | less -R
}


One last place here I used aliases is with the "quick cd" tool I use to keep my brain sane and my screen not-excessively-cluttered
#!/usr/bin/bash

export CITY=$(pwd)
echo "You are in the City. $CITY"
echo "You can set 4 locations. North, South, East and West."

alias setN='export NORTH=$(pwd)'
alias setW='export WEST=$(pwd)'
alias setE='export EAST=$(pwd)'
alias setS='export SOUTH=$(pwd)'
alias setC='export CITY=$(pwd)'

alias N='cd $NORTH'
alias S='cd $SOUTH'
alias E='cd $EAST'
alias W='cd $WEST'
alias C='cd $CITY'

And yes, it pretends that you're running an old-fashioned, text-based adventure game instead of crawling directories. Because i found it easier to thing of thinks as "west", "north", etc. rather than trying to remember what letter I used for "gstreamer" and what letter was for "alsa".

Saturday, April 14, 2018

Dear ImGUI,

I hope you enjoyed the week-end. It sure was a pleasure to have you around, and getting some pixels rendered without having to bother with ./configure, plugging events into sockets or any kind of new classes.

Sure, I wish you had time to stay for tea and I would have shown you my SpritePages, but I suppose that can be kept for another encounter. I'm pretty sure you and I are meant to meet each other pretty soon.

Everyone was amazed when you just returned "true" in the line of code that painted a new button. Imagine the face they'll have next time when we'll show them GobState representations live and pop up new windows as one explore the state machine...

Stay Safe,
/PypeBros.

PS: okay, the unit-tester requiring 32-bit (so that DS registers addresses are out of the .text segment) and SDL requiring 64-bit won't simplify early integration tests ... we'll find some workaround.

Monday, April 09, 2018

Tutorial revision goes on.

I reached the point where you can write very simple scripts and have them processed on the "tutorial" branch. Of course there isn't much follow-up at the moment despite the 3 forums on which I comment stuff. That doesn't really matter, although I'd love to get feedback on whether it reads well.

What is really interesting here is that it forces me to get rid of many odd things. Hopefully, that will lead to a code base that will be easier to extend. Things like "rules.gam", for instance.

Being busy reviewing the expressions system, for instance, make it obvious that some static array could be gone now that I have the gob collision structure. the "game counter al so cry for a refactoring out of the GameScript class. And making the 'guns/controllers' system easier to understand (esp. by automating the registration system) made it obvious that I need to re-think the way classes access the Camera object.

Wednesday, April 04, 2018

Tutoriel libgeds - day 4

Bon, il est temps de vous montrer ce que libgeds peut faire pour ceux qui n'ont pas le C++ dans le sang. Charger des fichiers, définir des animations, positionner les ennemis dans le niveau, tout ça peut être fait à partir de commandes dans des scripts texte lus et transformés en objets C++ par la bibliothèque.

Je vais commencer par un sous-script qui décrit le comportement d'un personnage. On va commencer très simple, avec une seule animation, et toujours le même comportement: ne pas bouger. Voici donc "xdad.cmd"

Code:
# this is xdad.cmd behaviour description
anim1 0 {
  spr0 4
  delay 3
  spr0 5
  delay 3
  spr0 6
  delay 3
  spr0 7
  delay 3
  spr0 8
  delay 3
  spr0 9
  delay 3
  spr0 a
  delay 3
  spr0 b
  delay 3
  loop
}

state0 :anim1

end

Le bloc 'anim<numéro-d-animation>' donne utilise une page du SpriteSet et construit une animation en indiquant les images à utiliser (dans cette page) et combien de temps attendre entre chaque deux images. On peut ensuite l'attacher à un état, c'est à dire une unité élémentaire de comportement . Bon, évidemment, pour l'instant, avec un seul état, il ne se passera pas grand-chose. C'est un peu notre 'hello world'.

Un fichier comme xdad.cmd, on en créera un par type de personnage (imaginez "goomba.cmd", "koopa.cmd", etc.) Maintenant, il va nous falloir un script qui décrit un niveau. Ce sera demo.cmd, pour nous.
Code:
#demo.cmd, resource management
print "loading tileset"
bg0.load "../bg.spr"
print "loading sprites"
spr.load "../hero.spr":1
On y retrouve des commandes gérant le chargement des fichiers de données qui remplace les SpriteSet::Load().

Code:
#demo.cmd, use 'character' description
input "xdad.cmd"
import state 0
On y retrouve aussi évidemment des commandes pour réutiliser les fichiers comme xdad.cmd, évidemment. la commande 'import state 0' mérite quelques mots d'explication supplémentaires. Pour un vrai personnage de jeu vidéo, il y aura bien sûr de nombreux états... On peut en compter au moins 21 par direction dans Super Mario world, par exemple.

Pourtant, seuls un ou deux de ces états sont nécessaires pour décrire le niveau (commencer tourné vers la droite ou vers la gauche, par exemple, ou éventuellement par une glissade). Geds propose donc un jeu d'identifiants pour l'ensemble des états d'un personnage (tous les états d'un goomba, par exemple) qui sera utilisé dans xdad.cmd, et l'ensemble des états utilisables pour créer des nouveaux objets (un goomba, mario, un koopa rouge ou un koopa vert), qui sera utilisé dans demo.cmd.
Une fois le traitement xdad.cmd terminé, la commande 'import state 0' indique de prendre le premier état de l'ensemble de xdad et d'en faire l'état numéro 0 pour demo.cmd.

Code:
# creating gobs
gob0 :state0 (128, 100)
end
Enfin, on peut constuire un nouveau personnage (le Game object) avec cet état, en donnant les coordonnée du personnage à l'écran.

Comme on le voit sur github, on laisse tomber la classe Hero: il s'agit maintenant d'un GameObject construit et contrôlé directement par la bibliothèque suite au commandes du script.

La classe 'MetaWindow', point de départ de notre démo, a pas mal changé aussi.
Code:
#include <GameWindow.hpp>
class MetaWindow : public DownWindow {
  NOCOPY(MetaWindow);
  Window *active;
  InputReader* reader;
  LoadingWindow loadwin;
  GameWindow gamewin;
public:
  MetaWindow(): active(0),
   reader(0),
   loadwin(&reader),    gamewin(this, &reader, &loadwin)  {
     ntxm9 = new NTXM9(); // for the sound, remember ?
     active = &gamewin;
   }
C'est la classe GameWindow de libgeds qui va créer, charger et traiter les scripts, et assurer la gestion de tous les objets qui en découlent, les animations, les personnages, etc. Sa compagne, LoadingWindow sert lors des changement de niveaux (on y reviendra). Pas grand chose à dire sur le constructeur puisqu'à ce moment-là, les fichiers intégrés à la ROM ne sont pas encore disponibles. L'initialisation, la vraie, se passera dans 'setactive()'.

Code:
   void setactive() {
     FileDataReader fd("efs:/sndtrk.xm");
     reader = new FileReader("efs:/demo.cmd");
     ntxm9->stop();
     u16 err = ntxm9->load(&fd);
     ge.setWindow(active);
     restore(); // just to have the tileset shown again.
     if (err!=0) iprintf("ntxm says %x\n",err);
   }
Par le simple fait de demander au moteur de jeu de passer le contrôle à GameWindow, le chargement commence automatiquement à travers le 'FileReader' (lecteur de fichiers, donc) qu'on vient de construire.
Et c'est tout. A la prochaine fois pour rendre ses déplacement à notre petit père Noël.

Tutoriel libgeds - jour 5



Redonner du mouvement au personnage dans geds, ça correspond à spécifier une série de contrôleurs pour l'état choisi. Chaque contrôleur a une mission bien précise, p.ex. ici

Code:
state0 :anim1 {
   using dpad
   using momentum(x to 512)
   using momentum(y to 512)
}
dpad va lire les registres de la console et sauver les directions
momentum va utiliser les directions sauvées pour augmenter ou diminuer la vitesse horizontale ou verticale (selon qu'on a utilisé "x" ou "y")

On peut aussi passer des paramètres aux contrôleurs: c'est le cas ici du "512" qu'il faut interpréter comme un nombre de 256emes de pixels par frame (1/60eme de seconde) et qui représente la vitesse maximale autorisée sur un axe.

Le ScriptParser cherchera les classes C++ correspondantes et fait le nécessaire pour que le code qu'elles contiennent soient appelées à chaque image générée par le jeu.

Trois lignes de plus dans le script donc (et quelques modifications dans les Makefiles). C'est tout.