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.

Friday, March 30, 2018

Aladdin Sources Analysis

They made a wonderful job at gamehistory.org, based on an in-depth analysis of the sources of the Mega-drive game "Aladdin". The game was made by David Perry's team who also brought us Cool Spot. At the core of their work is a technique and a toolset to allow more flexibility in animating graphics on 16-bits system that had read/write video memory on-board (as opposed to NES with read-only video memory alone, on the cartridge) and fixed-size sprites (e.g. 16x16, 16x32, 32x32).

https://gamehistory.org/aladdin-source-code/#sect_36Everything else will seem silly to you if you do not accept that, by then, getting more KB of memory for your game was very - very - hard. The size of your game was decided by non-technical people based on how much the console vendor would charge for a 2Mbit chip, when the game should came out and how much kids would be allowed to spend given which license you'd be using. So they have early planning deciding how much to dedicate to sprites, levels, code, maps, etc. Based on that, they'll decide how much levels there will be in the game, etc.

Of course, game characters animation all started by having characters whose size fit the hardware requirements (mario nicely stands within a 16x16 box and a 16x16 mushroom makes him 16x32), flipping from one sprite to another within an all-in-VRAM bank. Then some special characters (the hero) would get a special status and only get one or two VRAM slots dynamically updated. To crunch more animation frames, one could use run-length-encoding compression that does wonders on row of pixels of identical color. Others have used 2/3-bit-to-4-bit decompression once realizing that Link sprite (and all others) only need 8 colors per palette, not 16. But all this requires CPU, and the CPU resources too, were limited (Not even 8MHz. Less than my good old 80386).


If we could instead keep the same binary format between the ROM and the RAM, having the right picture in video memory at the right time is all a matter of "blasting" them through the Direct Memory Access chip. See that big line on my notes ? that's the DMA doing its job, while the CPU can focus on crunching numbers to make the game physics stunning and fun...

To make that possible with fun stretch-and-squash, cartoon-like animation, they ultimately relied on their chopper tool that cuts pictures into hardware-sized sprites. Just like the one I imagined for Titus's Prehistorik II sprites.

Ok, granted, it doesn't look completely automated. But the idea is clearly there. And ultimately, it would run on a system that has 1/4 of the power of my Nintendo DS.

So, am I allowed to dream of porting some libgeds game on 16-bit engines ? Well, with the engine refactoring that splits script parsing, it is pretty tempting to see what we could do about it.


Let's start with the animations, thus. What is weird with the animations is that their code has to interrupt every here and there when there is some delay. In high-level language, we'd likely use a switch construct branching you to frame T or frame T+1 code depending on some argument we'd pass to the function. But if we're generating machine code instead, we can do much better. We can then have the actual next animation instruction remembered, rather than an index into an array of virtual instructions. No more conditionals and branch delays on that non-speculating old CPU. Just one jump.

Implementing "keep that state for N screen refreshes" is then looking a lot like software multi-threading: you have a call to some yield_animation micro-routine (and saving your current position into the generated animation code on the stack), which will pop that resume position into some CPU register (an internal scratch variable, in case you didn't know yet), and then return to the code that called animate_aladdin, letting it save the next animation position where it sees fit. Looping animation ? super-easy ! Have you seen how much boilerplate the current virtual-RISC-processor-for-animations of libgeds and AnimEDS must deal with instead ?


What else ? State machine of course. State machines are built with simple expressions used either to guard transition (only let them used when some condition is met) or to define what to do when the transition occur (besides changing states, that is, like playing a sound, changing speed, etc).

The collision system currently will follow a list of GobExpressions calling eval(guard_predicate) until one returns true, then proceeding with eval(action) and changing state. Instead, with generated machine code, that would all be packed into a sequence of predicate code that branch to the appropriate action code or keep testing until we hit the "okay, then nothing happens" terminator that returns to the collision system itself.

One day ... maybe. That would be much more interesting on 16-bit than it would be on DS or native x86_64 code, anyway.

Monday, March 19, 2018

Biggest Refactory Ever

For me, at least. I wanted to make the scripts occur as soon as possible in my tutorial series, since the GEDS engine is meant to allow game-making even for those who don't know about C++ programming. But I also want to be able to introduce a behaviour editor, which suggests that the same script-parsing logic should be able to drive either the game engine or the state machine model in the editor.

So this last week, I've been busy splitting the big singleton "GameScript" that had both the parsing logic and the engine intimacy into two classes, the ScriptParser that knows the language rules and the Game* objects well enough to create them but has no knowledge about the Nintendo DS resources or the game engine per se, and the GameScript, that knows about the engine's runtime, last as long as the level does, hold resources and the like.


ça bosse ferme ... restructuration du lecteur de scripts pour pouvoir introduire un éditeur de machines d'état ...

I've finally reached a point where all my automated tests work again. Of course, School Rush isn't running fine in this branch ... yet.

edit: Okay, SchoolRush runs fine again in the emulator. Just some un-initialized arrays. -Weffc++ should have caught that, though.

Friday, March 09, 2018

libgeds Animator system

Je pensais réécrire une partie du système d'animation. Il est inspiré d'un gestionnaire d'évènement à retardements pour systèmes d'exploitations. Sauf que dans le cas des sprites dans libgeds, à peu près tout est exécuté à chaque image. Ne serait-ce que parce qu'il me faut compenser les mouvements de la caméra.

I had somehow convinced myself that the animation scheduler system of libgeds needed a rewrite, that scanning through the list of animators to push new content in the middle everytime some new object was shot was a mistake, and that everything would work better, faster and stronger if I had a list of 'play every frame" in addition to the current list of "play when delay expires". But actually, the insertion policy is somewhat different: we insert _before_ any item that has the same delay, therefore making most in-game insertion as trivial as 'insert at the head of the list'.

Je craignais qu'il y ait régulièrement des éléments qui doivent inutilement être placés en bout de file d'attente parce qu'il y a de nombreux autres sprites à animer, et tous avec le même délai. Mais en fait, il n'y a pas besoin de modification. A cause d'un tout petit détail dans sa définition "place a après tous les animés qui ont un délai strictement inférieur". Donc tous les éléments qui ont le délai minimum (les objets et personnages du jeu) seront placés en tête de liste, temps d'exécution minimum aussi.

Wednesday, March 07, 2018

libgeds tutorial

I have just started a github with one branch of the dsgametools project: the 'tutorials' branch, where I'm reconstructing and detailing step by step the components of the (refactored) game engine... together with Creative-Commons pictures and sounds to make demos on a regular basis.

Additional chatting and promotion of the tutorials happen in gbatemp and e-magination forums.


Cette fois-ci ça y est. J'ai transféré la branche "un tutoriel après l'autre" sur github. Pour que chacun puisse facilement suivre ma tentative de réécrire le moteur des jeux Bilou. Histoire que les différentes fonctions disponibles soient capturées clairement, et non pas éparpillées sur une demi-douzaine de patches.

On verra bien si ça intéresse du monde...

the mercurial-to-git conversion is performed by the fast-export tool from Frej. The process looks as follows:

cd hg2git/
cd dsgametools-hg/
# hg incoming -r $(hg id -b)
hg pull -r $(hg id -b)
cd ../tutorials-git/
../fast-export/hg-fast-export.sh -r ../dsgametools-hg
# git log
# git push --dry-run git@github.com:PypeBros/libgeds-tutorials.git
git push git@github.com:PypeBros/libgeds-tutorials.git
cd ..

Thursday, March 01, 2018

The "last" map

Allez, je me suis bricolé une dernière map pour School Rush: la récompense pour ceux qui seront parvenus à grimper jusqu'en haut du "niveau secret". Il y aura une présentation des monstres, bien sûr, mais aussi une petite surprise qui m'a pris du temps à mettre au point. Au niveau de l'idée, je veux dire.

I sketched up a last map for School Rush, that will be the final reward to players who beat the secret climbing challenge. It took me some time to nail down the idea I wanted to have, but I think all I have to do now is some pixels and some scripting. And making sure I can restrict the part of the level that the camera can show. I'll have to keep the todo list off-line, though so that you have a real surprise ;-).

Côté réalisation, je vais avoir besoin, pour la première fois, de restreindre les mouvements de la caméra. Il me faudra aussi une petite variante de la gomme, quelques graphismes sur tableau vert et un mode "calmos" pour les encriers... Pas facile de se faire une todo liste pour un truc qui doit rester secret >_<

__lock_jiffies

Perdu dans un petit coin d'un driver Linux, une fonction sympa qui augmente un des verrous avec un chronomètre ... histoire de voir combien de temps on est resté en section critique.

Je devrais peut-être bien ajouter quelque-chose de ce genre avec parseLine() dans GameScript, tiens.

Tuesday, February 20, 2018

Ideas for a handwriting recognition on DS


Il y a un moment que je retourne l'idée dans ma tête : un système permettant d'écrire librement (du code, pour l'éditeur de comportements) sur DS. Le système serait basé sur une grille 3x5 visible par l'utilisateur. Parce que les claviers virtuels, c'est pénible et qu'un système qui autorise l'utilisateur à écrire n'importe où et n'importe comment, ça n'aura aucune chance de marcher avec 66MHz


That is another idea that has been laying on paper for a while. I lack text and script editors at the NDS.  I don't want to go for a virtual keyboard, because they are really too slow to work with. I can't afford a complex recognition system, and even Google's handwriting system would suck on code expressions.


But I feel like it would be easy to recognize letters when they have been written on a grid. A bit like if we were painting a low-resolution font.


Every letter drawn corresponds to a 15-bit map that can be quickly tested against a small database of features and suggest letters to pick when the decision is fuzzy.

 Additional features could be the starting and terminating coordinates, as well as the number of strokes.

Cette petite grille fait que chaque lettre tracée correspond à un code sur 16 bits unique. A partir de ce code, on pourrait rapidement identifier un groupe d'entrées dans une petite base de données qui traduit les traîts en lettre (p.ex. à partir de la position du point d'origine et du nombre de traits). Je ne veux pas me limiter aux lettres, évidemment. L'idée est de reconnaître l'ensemble des caractères ASCII pour pouvoir éditer du code n'importe où le plus facilement possible. 

I hope the result will be an interesting compromise between
- early PDA "one-weird-stroke-to-write-them- all" (Palm's Graffiti system)
- sharp Zaurus "Free-letters-one-by-one"
- OCR from scanned text.


I know segmenting is a complex task in letters recognition. With pre-drawn grids, the user is guided to start a new segment or stay in the current one. We can also tell apart uppercase and lowercase Eg. "p", punctuations, etc. locally rather having to rely on some line-level calibration. And we can naturally move to the next space for the next letter.



Avec cette grille, je m'affranchis d'un des problèmes les plus costauds de la reconnaissance de caractères (la segmentation) tout en permettant d'utiliser le plus naturellement possible l'espace disponible (sur Zaurus, on devait presque toujours revenir au même endroit pour tracer la lettre suivante ou le mot suivant). Et c'est toujours plus pratique à apprendre (encore qu'on pourrait essayer d'apprendre de nouvelles écritures à la DS) d'écrire "comme Pype/Zaurus" plutôt que "comme en Grafiti", même si grafiti avait le bon goût de ne demander qu'un trait par caractère.

Granted, the hand script writing I use when I want maximum readability already separates letters a lot and uses only one stroke for most lowercase. Because I reformatted my writing for the Zaurus already. But it's not like I had a huge user base to take into account ^^".

The granularity of the grid should help making tricky characters like braces-versus-digits-versus-C easier to deal with.

Saturday, February 17, 2018

Histoire d'encre

Eh bien, le fait de se déplacer dans un encrier n'est décidément pas intuitif pour le joueur lambda de School Rush. J'en prends pour témoin les réactions des membres de "e-magination" à mon screenshot:

Le pot d'encre dans lequel sauter, tu l'amènes comme ça dans le LD ? Parce que j'aurais pas envie de sauter dessus personnellement, il ressemble trop à un piège  ...

Peut-être qu'il faudrait rendre l'encrier plus amical ? Perso avec ce regard, je lui ferais pas confiance. ^^

Ce serait une approche assez classique du problème, c'est vrai. Mais l'encrier n'est pas amical,

 sans être pour autant fondamentalement méchant:

Il s'agit d'un gardien, et d'un gardien face à une invasion. il ne sait pas de quel côté est Bilou. Il ne cherche pas à l'aider quand il le projette, juste à s'en débarasser. Il est dangereux (à cause de ses gouttes d'encre plutôt toxiques), pas franchement engageant (parce que entrainé à être gardien), mais peut être utile si on sait s'y prendre. C'est une dimension que j'aime bien (les carapaces de koopa, notamment) et que j'ai envie de développer dans mon jeu.

Friday, February 16, 2018

typedef u16 pageno_t

J'avoue que quand j'ai fait ma transition de l'assembleur vers le C, je faisais une mimique assez cocasse à la vue de uint32_t et size_t. Quoi, c'est un 'dword' (double-mot, jargon intel dans les assembleurs). Pourquoi aller lui donner un nom à coucher dehors pareil ? Bref, j'étais pas un fan de typedef, à part pour éviter de devoir écrire "struct" devant le nom de mes structures.

Mais sur l'année écoulée, j'ai eu droit à plusieurs situations du genre "int bitrate". Ah oui? en quelle unité, s'il vous plait, le bitrate ? bit/seconde ? kilobit/seconde ? megabit/seconde ? Et dans le code de mon moteur de jeu, "int x" ou "int width" est à peu près pareil. Est-ce que x est une coordonnée absolue (dans le niveau) ou relative (à l'écran) ? est-ce qu'elle est exprimée en pixels ? en tiles (si, si, souvenez vous, les petits pavés élémentaires de graphisme) ? en 256eme de pixel pour les calculs de déplacement avant affichage ?

Alors bien sûr, on peut tenter "int kbps, int x_in_tiles", etc. Mais pourquoi. Pourquoi faire rentrer dans le nom de la variable une information (l'unité de mesure) qui dénote plutôt de son type (on peut additionner deux valeurs en kbps entre elles, mais pas des Mbps et des kbps sans faire une conversion au préalable). Bref, j'ai pris l'habitude de construire des types qui expriment des unités de mesure. Beaucoup. Au point que je me méfie presque maintenant du code qui annonce juste un "int" parce que dès qu'on va commencer à intégrer des morceaux de code qui viennent d'horizon différents, on aura aucun moyen de se souvenir si le "* 1000" qui est là dans l'appel de fonction est justifié ou non.

Et comme je viens d'atteindre le point ou la branche "revisite pour cause de tutoriel" de mon moteur de jeu est prête à recevoir le code servant à charger des fichiers .spr, ce serait peut-être le bon moment pour y introduire des types comme "numéro de page, numéro de bloc, numéro de tile", etc.

Sunday, February 11, 2018

It's Cube Time!

Among all the hidden anti-cubes of fez, the four ones to collect at the clock tower were clearly the less interesting one. As soon as you've learned how to read your world map aud made your way up to the tower, you know you're missing something and that it isn't in a hidden room.

Parmi tous les anti-cubes de Fez, il y en a 4 à récolter autour de l'horloge, et selon moi, ils sont malheureusement les moins intéressants du jeu. Une fois qu'on a appris à "lire" la carte du monde, on sait qu'on a raté un truc à cet endroit (la petite icône ne disparaît pas), et on sait aussi qu'il n'est pas dans une salle cachée (l'icône est sur le noeud de la tour lui-même, pas un noeud annexe).

You have likely disovered that there is more to do in the game than hunting for
cube bits. Some anti-cubes are revealed by moving/stacking blocks, some by reading hints in the levels and turning them into secret codes to input through your game controller, konami-style.
All of these mechanics have been introduced over the "normal" gameplay, through switches, levers to manipulate .and doors with cryptic engraving. Granted, the d-pad language isn't easy to grasp and decode, but it is introduced with simple sequences of turn left/turn right.


Au moment où vous en arrivez là, vous vous serez très certainement rendu compte que la chasse aux petits cubes dorés ne représente qu'une petite partie du jeu. On trouve des anti-cubes en déplaçant des blocs, ou en lisant des indices pour exécuter des "cheat codes" à la manette. Le plus souvent, ces mécaniques ont été enseignées à travers des manoeuvres "normales" pour activer des interrupteurs ou des portes au inscriptions mystérieuses.


The clock anti-cube work like no other in the game. One of them had appeared with no apparent reason, then disappeared before I could grab it. Then I noticed one of the clockish and was moving and predicted time where the anti-cube Pops. Then the moving hand disappeared as I grabbed the anti-cube.
you will barely notice that all the four hands move, Imho. Not unless you stay idle, not even rotating the world for a few minutes. But why would you? There is a wrapper portal just above the clock and absolutely nothing to keep you thinking nearby. (not even script to translate). The odds that you see another cube pop there while passing by is 1/30 at best. And that map is no hub. You're never returned there. it's not as if it was the village's clock...


Mais les anti-cubes de l'horloge ne sont définitivement pas de ce genre. Tout d'abord parce que pour la seule fois du jeu, il y en a un qui apparaît, comme ça, sans prévenir. puis qui disparaît avant qu'on ne puisse s'en saisir. Je me rends compte qu'il se synchronise sur la seule aiguille qui bouge. je me synchronise aussi, je m'en empare. Par contre, chapeau à celui qui aura remarqué que les autres aiguilles bougent elles aussi (nettement plus lentement, il faut bien dire). Si au moins il y avait eu quelque-chose à faire ... une inscription à traduire .. n'importe. Mais non. Le portail pour le retour est juste au-dessus ... Et il aurait fallu être retardé plusieurs minutes. Et on est pas sur un noeud central, mais en "bout de branche": on ne reviendra pas là par hasard.

Your best hint comes from Dot, actually. But
by then, you've learned not to take care of Dot's comments. Dot doesn't remember, she's afraid of owls, etc. She's only helpful for basic stuff like knowing .what is interactive. When Dot says "it is cube time", its sounds like cheap humour.
And even then, she never says "it is quarter to Cube" nor "half past cube". The link is static, not flowing as the time.
If only that clock was behaving differently from our regular clocks, et with the "hours" hand doing one full revolution over Gomez's day and night cycle, giving you some more chance to understand that you are not expected to find anything to do there, just meet the date...


Notre meilleur indice (sur le fait que les couleurs de l'horloge n'ont rien à voir avec les couleurs des boutons de la manettes et qu'il ne faut pas essayer de lire des chiffres sur l'horloge, etc) nous vient de Dot, le cube volant qui nous accompagne. "C'est l'heure du cube" semble plaisanter Dot. Mais le joueur aura appris -- une fois la phase tutorielle passée -- à ne plus tenir compte de ses remarques. Dot ne sait pas, a oublié, n'aime pas les hiboux, etc. Peut-être aurais-je été plus attentif si Dot avait annoncé qu'il est "cube moins le quart" ou "cube et demie" selon la position des aiguilles ...

Non. Rien de tout celà. Le joueur qui passe une à deux minutes dans cette partie du tableau a entre une chance sur 30 et une chance sur 60 de voir apparaitre le deuxième des quatre anti-cubes de l'horloge. Pour le dernier, il faudra que je repasse mardi, entre 1h30 et 2h30 du matin. Et non, ça n'est pas drôle. Quitter puis relancer le jeu après avoir changé l'heure de la playstation une demi-douzaine de fois non plus. Si au moins les aiguilles avaient avancé "plus vite" (une 'heure' de 10 minutes, une journée d'une heure, etc). Et peut-être un moyen d'entrer dans la tour pour modifier l'heure actuelle sans quitter le jeu ?


Finally, even if all that had been clear, understanding that the anti-cube I need to beat the game will show up next Tuesday between 1 AM and 2 AM is no fun. I really missed some way to enter the clock and start messing with its gears to force the last two cubes to my convenience. That would have felt awesome and fun.

Tuesday, February 06, 2018

gource and reDS

C'est un outil super: gource. Transformer les fichiers logs d'un gestionnaire de versions en une animation interactive où les développeurs deviennent des jardiniers taillant, soignant, élagant, greffant...

Et facile à utiliser, en plus! Bon évidemment, ici c'est assez moyennement intéressant puisque je suis tout seul sur le projet depuis le début ...

Mais au moins, ça met bien en valeur les efforts pour restructurer le code et permettre à d'autres (au cas où) de faire des projets à base du moteur de jeu GEDS plus facilement.
Au passage, j'ai franchi un cap avec le code d'AnimEDS. Les "modèles" sont maintenant activés. Je regrette un peu d'avoir quand même dû passer par l'émulateur et une exécution pas à pas pour corriger les bugs introduits lors du refactoring alors que j'avais des programmes de tests pour l'architecture native juste à côté... mais bon, les erreurs étaient dans le setup des objets, pas dans leur logique.
Maintenant, il reste à sortir la gestion des "callback" hors de la classe "Engine" (je les verrais bien en gestion autonome, avec un objet complètement interne qui assure la gestion et qui est caché derrière les méthodes "register" et "unregister" de la classe-maîtresse). edit: c'est fait.

Wednesday, January 31, 2018

School Touch?


My brother comes back again with the idea of School Rush on Android platform. Porting issues set aside, I guess he means "on the bare device", and not assuming one bluetooth dpad and buttons attached, Nintendo-switch-like. That implies converting touch and taps and gestures into valid SNES inputs.

Mon frangin n'en démord pas: la plate-forme qu'il faudrait attaquer pour un portage de School Rush, c'est les smartphones & tablettes. Androïd, donc. Au-delà de l'aspect purement "portage", ce qui va manquer le plus sur Androïd, ce sont bien sûr les boutons et le D-PAD. Je doute qu'il avait en tête qu'on se bricole quelque-chose avec des contrôleurs bluetooth en plus du téléphone lui-même. Bon, la bonne nouvelle c'est que la DS elle-même a un écran tactile. On pourrait donc imaginer faire une version "School Touch" pour essayer le potentiel du jeu avec cette interface-là avant de se lancer dans des longues soirée de méditation sur les interfaces Androïd.

Good news: the DS also has a touchscreen. So as a preliminary test, I could just try to use the touchscreen to see how the game feels. Imho, a specific area to emulate the d-pad on having to hold a finger on the screen as we do for the (A) button would be terrible. No one could complete the game in those conditions. I've tried that with e.g. Sonic 2 back then and it was really degrading the gameplay.

Ce qui est déjà clair, c'est que je ne ferai pas une espèce de pad virtuel à travers l'écran tactile. C'est généralement une catastrophe. Il faudra plutôt que j'aille vers une sorte de mélange entre "platform panic" de Nitrome (glisser à gauche ou à droite pour changer le sens de la course avec Bilou qui continue à courir tout seul) et Phantom Hourglass (tapper un monstre pour utiliser les mains de Bilou)

Ideally, we should be constantly running when playing School Rush. That means in the School Touch game, Bilou should start running forward as soon as we trigger RUN, keep running until we give a contradictory input. Similarly, just tapping Jump should do a max-height jump. modulating dump height on distance would be additional inputs rather than timing on the initial input.

No idea when I would do that,... though.


Là où je sèche plus, c'est pour le saut. Dommage, pour un jeu de plate-forme, j'en conviens. Dans School Rush, on dose son saut par la durée de la pression sur le bouton. Faire pareil sur un écran, c'est risquer d'avoir toujours les doigts dans le chemin quand on veut regarder la suite du niveau. Je partirais plutôt vers un système où on fait de base le saut maximum, que l'on "interrompt" si Bilou arrive plus haut que l'endroit où on a tappé  / l'endroit où est le doigt. Enfin, il faudra trouver quelque-chose pour pouvoir atterrir avec assez de précision sur des plate-formes parfois assez étroites.

I liked the idea of "tap another GOB to trigger the "punch" button: it works for picking up bladors, throwing them on baddies, grabbing spons and punching things.

Saturday, January 27, 2018

J'adore le mauve

 Parce que dans Fez, le mauve est synonyme de mystère et d'intrigue. Généralement une petite pièce qui semble en-dehors du monde "normal", une atmosphère de trans-dimensionnalité... du fez quoi.

I love than purple shade of mystery Phil introduced in his Fez game. He mostly used it in small, indoors location that feel a bit disconnected from the "overworld". It usually looks bigger inside than it should be given the exteriors' dimensions. We first encounter it in the strange place where the cube gives us power, and it was absent completely from the village, to find it again just when we cross the door to "the world beyond" the village.

L'introduction du personnage à son nouveau pouvoir se fait dans le mauve, couleur presque absente de son village. On la retrouve pourtant derrière la porte qui mêne "au monde au-delà du village.

Et quel monde! les cascades tombent de nulle part, les blocs flottent dans les airs, il y a des portes partout et pourtant elles n'ont nulle part où mener (enfin, ça devrait chaque fois être une autre porte vers la même salle qui fait au mieux deux fois la chambre de gomez, mais en fait non. Ici, l'intérieur, c'est l'extérieur).

That first hub is the goal of our game: we want cubes to open those doors. The mystery color is used again, which makes the room feel unique, with floating blocks and Cascadas flowing from nowhere. There seems to be which echoes the duality of the gameplay: the normal, outerworld is for exploring, and the purple, inner world is for riddles.  Indeed, past that first hub, purple rooms will mostly be locations where we took for anti-cubes.

Il y a donc deux couches de réalité dans Fez: le monde extérieur, propice à l'exploration, et le monde intérieur, mauve, propice à ...

Eh bien propice aux énigmes. On va essentiellement le retrouver dans des salles "cachées" où il va falloir faire quelque-chose pour faire apparaître un anti-cube. C'est d'ailleurs probablement dans ces salles que vous avez le plus de chance d'y trouver votre premier si vous n'avez pas encore jeté un oeil sur la liste des "achievements" du jeu.

Un des aspects du jeu qui m'avait vraiment plu dès ma première "vraie" partie, faisant un peu écho aux puzzle qu'on pourrait trouver dans un Zelda.

Mais le mieux, c'est que le jeu nous met la puce à l'oreille. une salle-mauve en-dedans a sa porte d'accès sur un mur mauve au-dehors. De quoi provoquer l'anticipation du mystère mais aussi pouvoir la repérer plus facilement parmi les autres portes d'un noeud (on ne peut pas parler de monde ni de niveau pour fez) et simplifier un peu le back-tracking.

One clever design idea about those riddle-rooms is that their purple status tends to contaminate the outerworld. The door to the room itself will be purple as well, and sometimes, even some of the bricks around are purple, too.  This helps quickly identifying the right door when we backtrack to riddles we had not yet solved.
 

Some level of backtracking is impossible to avoid with Fez, but to be honest, the more you can focus on the things immediately reachable, the best. I wished I could close doors towards rooms I couldn't understand, To reduce the amount of trials when I'll have to go there again.  understanding how the map system works was a Key factor in bearing he game, and I eventually. picked up a sketchpad to take notes of my progress, and scribble anything that could be useful later, like the location of owls, obelisks and interesting paintings. without it, I think I would still be running that maze for final cubes. Likely, I should have depicted the secret doors as well.

Bien que, je vous l'avoue, j'avais déjà fait l'expérience assez désagréable de constater que Fez est beaucoup moins pratique quand on doit retourner quelque part que quand on y fait son premier passage. En fait, il aurait été pratique de pouvoir refermer une porte quand on se rend compte qu'elle mène vers une énigme qu'on ne sait pas encore résoudre, de sorte qu'elle apparaisse comme "à faire" au prochain passage.

Si bien que l'on doit assez vite apprendre à se servir de ce qui tient lieu de "carte" pour ce monde. garder un oeil sur ce qu'il y a à l'arrière-plan quand on tourne le monde dans telle ou telle direction, vérifier si la salle marquée d'un '?' est bien sur la "face" visible, s'il y a plusieurs portes sur cette face, etc. (et tenir compte du fait que la carte s'autorise à changer la position d'une porte si elle est sur la face par laquelle on arrive dans le noeud). Sans ça, c'est l'effet labyrinthe garanti.

Et oui, j'ai finalement pris un carnet à spirale pour y prendre note des choses sortant un peu de l'ordinaire (en particulier dans les salles mauves) y compris par où on y parvenait pour pouvoir y retourner facilement.
J'aurais probablement dû faire pareil avec les "portes dérobées" (marquées d'un double-carré) qui ne marchent que dans un sens la première fois mais qui offrent des raccourcis en cas de re-parcours.

AnimationModel

Donc, j'ai fini Fez (enfin, j'ai 64 cubes) mais ça, je vous en parlerai plus tard. J'ai aussi fini de réorganiser le code de mon éditeur d'animation de sorte que les manipulations effectuées sur la liste d'images puisse être extraites du code de l'interface graphique correspondante. Je ne vais pas qualifier ça de Model/View/Controller, mais c'est un pas dans la bonne direction, à mon avis.

At last, am done with one big chunk of nefactoring on the DS Animation Editor. Frames list can now be manipulared from any piece of code. It no longer requires emulation of the whole widgets/windows engine. 

It is probably not scleanly separated as MVC code, but it is definitely one step in the right direction. Now I can get rid of the ugly coupling between thumbs display and thumbs-bitmap filling when saving the file. I can get rid of global objects. I can introduce my new Resources class.

Tout ça parce qu'il était impossible dans l'état actuel de débarasser AnimEDS de ses objets globaux vu le couplage tordu et foireux entre la fenêtre qui fait le rendu des miniatures et le système de sauvegarde. Pensez un peu: pour sauver les miniatures (utilisées par l'éditeur de niveau), on allait devoir faire croire à cette "fenêtre" que l'utilisateur passe d'une page à l'autre pour la forcer à re-générer des prévisualisations en mémoire vidéo que l'on capture pour les placer dans le fichier.

Prévisualisations qui - au passage -- demandaient qu'on fasse croire à la fenêtre d'édition des animations qu'il y a une nouvelle animation à éditer pour qu'elle la décode, construise la liste de frames correspondante et qu'on puisse demander un rendu de la première frame à chaque coup.

C'est presque un miracle qu'on ait pu continuer à éditer des animations après une sauvegarde, à ce tarif-là.

Tout ça parce que si AnimEDS garde ses objets globaux, il est impossible de réorganiser la gestion des resources dans le Game/GuiEngine.


Thursday, January 11, 2018

Engine Refactory

The Big Refactory has started (I could pretend it is a new-year resolution). And it even went pretty far, like abstracting away the animated objects list so that only the code for animated objects need to know details of the animation scheduler.

I was about to try changing the VRAM allocation system so that only classes deriving from using Resources could request items, and I got stuck in the animation editor.

Unlike SEDs and -to some extent- LEDs, AniMEDs still use a lot of global variables. Things like the sprite set or the animation being editted are used almost directly in many places, instead of being captured as "structural singletons". And at some places, it also means that we were calling Ressource Manager directly at top-level code, and that contradicts what a common base class can do.

There is one other peculiar thing about the animation editor: the set of pixels used in the game isn't uploaded to the video memory. Instead, we keep it in main memory, and only some bits of it go tothe VRAM through a Sprite sheet that map some sprite memory.  These sprite sheets are linked to a specific widget, such as the timeline or the frame preview. I must not try to make them part of a "model": they truly belong to the implementation of the widgets.

And yeah, although I have a digital sketchpad and an e-boox, I still enjoy some good paper and fine (erasable) pens for such things.

Thursday, January 04, 2018

Presenting monsters


This is something I really loved in DKC closing cutscene and which I'll now seriously start working on for School Rush: seeing the nicknames of all the baddies in the game with the baddies themselves. Later on, when I wanted to bring Badman II to an end, I tried to do something similar: which apparently worked quite well it was acclaimed by Aderack in his wiki and essays.

And as if I'd need one more proof, my brother just convinced Lazycow that such a show was all he needed to make the. end-of-game cut scene of Power Glove feel satisfying for the players reaching that point.


J'étais complètement fan de la séquence de fin de Donkey Kong Country, avec sa présentation des ennemis un peu humoriste qui dévoile leurs noms et permet de se souvenir de tout ce qu'on a traversé. J'avais fait quelque-chose d'un peu similaire pour la fin de Badman II, ce qui a plu, visiblement (au moins à Aderack ;)

Et comme s'il en fallait plus pour m'encourager à remettre le couvert dans School Rush, voilà que mon frère me raconte que LazyCowse demandait que faire pour le final de son PowerGlove et mon frère de lui dire que "bin si, les joueurs aiment bien qu'on leur remontre la galerie des monstres/boss avec les noms de tout le monde (surtout quand les noms sont un peu rigolos). Et sisi, y'a pas forcément besoin de beaucoup plus.

Donc ne vous inquiétez pas, je vais quand-même garder un petit quelque-chose d'original : le croquis ci-dessus est une ancienne idée. Mais j'ai encore quelques petits détails à régler pour que ce soit réussi. 

Of course, these are just some preliminary sketches, and there has been some evolutions. But I know you'll allow me to keep those details a secret, and not spoiling your future satisfaction myself.

Wednesday, January 03, 2018

A dos de blador.

Mes taille-crayons, une fois en mouvement, font d'assez mauvaises plates-formes, en vérité. Celà vient de leur animation. D'abord (comme la plupart des animations de marche), elle va retarder certains mouvements pour consommer d'un coup 2 pixels de déplacement après être restée immobile pendant 4 images si la vitesse de consigne est d'1/2 pixel-par-60eme de seconde. Ensuite, parce que contrairement à Bilou, le déplacement de dumblador s'interrompt pour une courte pause avant de reprendre: non seulement ce taille est trop "dumb" pour mettre un pied devant l'autre, mais en plus, il lui faut un moment pour se souvenir de ce qu'il convient de faire pour finir le mouvement qu'il a entammé. Autant dire qu'il vaudrait sans doute mieux pour une "pile de dumbladors" de tomber d'un étage quand un des tailles-crayon se remet en mouvement plutôt que de jouer à rajouter des "selles invisibles" pour offrir quelque-chose de plus souple.

Non. Je préfère tenter maintenant de faire tenir Bilou sur un taille-crayon à l'arrêt. Et là aussi, j'ai du travail: il faudra veiller à

  • forcer une mise à l'arrêt complet au contact, puisque le contrôleur "stopper" sera court-circuité,
  • "détacher" Bilou de son support lorsqu'il saute,
  • compléter le contrôleur "onpath" pour qu'il provoque le détachement lorsque Bilou n'est plus par-dessus le chemin.
This has been a "known issue" since April 2014, and it still wouldn't be easy to fix it today: you can walk on a Dumblador (after stunning it) as if it was a platform, but as it wakes up, weird things occur that make Bilou slightly slip forward and eventually fall of the blador. Initially, it was also affecting stacked dumbladors, so I added a rule to "break" the stack as a blador wakes up.

The core of the problem comes from the fact that a carried GOB will adapt to the "moving platform"'s speed in order to give the illusion of friction, but when it comes to dumbladors, the speed and the effective motion quite disagree. First because - as with any WALKing GOB - the horizontal speed isn't constant, and second because I introduced a small pause between two steps for the blador, to reinforce the feeling of "dumbness". As a reminder, the GPath abstraction is as follow:

  • GameObjects have an 'onpath' pointer, which complements the 'attach' pointer (to a gob).
  • GobArea now implements the (horizontal) GPath API, returning always the same y coordinate, and checking that you're in the desired xrange.
  • 'Ap' in a gobexpression will attach the object to a "path" rather than to an object, the area that triggered the collision will be used to define the new path.
I gave path-capable engine a try back in february 2013, but that was nothing I could test. The idea was to support both NSMB-like angled mushrooms and (softly) swinging ropes. The current code with dynamic GPath comes from June 2013 and has still to be complemented with a controller that uses it.

It's all described and motivated in http://sylvainhb.blogspot.be/2013/06/walking-on-platforms.html