mardi, décembre 29, 2009

Xmas Checkpoint

Je ne vais pas vous faire le coup du changement de palette pour avoir une forêt aux feuilles toutes blanches de neige, ni affubler Bilou d'un gros bonnet rouge et blanc ... Mais joyeux noël et d'avance une bonne année 2010 quand-même.

A défaut de "release", voici l'état actuel de mes trois applications:

  • le sprite editor, dont j'ai un brin amélioré l'édition de palettes.
  • le level editor, avec son interface revue et corrigée.
  • le moteur de jeu, avec support des pentes. J'ai aussi essayé d'un peu ajuster les collisions avec les ennemis. Vous pouvez donc vous amuser *aussi* à écraser les appleman.
I'm afraid I'm not going to show you Bilou with a red, floppy hat in snowy woods this time. I'm afraid you won't even consider as a "release" the snapshot of my three major DS tools available from the link above. Okay, the Sprite Editor has (slightly) improved palette editor, featuring readable RGB and HSV values. Okay the game engine better supports slopes and appleman has an improved behaviour.
But before I go for a "real" release (that is, an extended level that really showcases slopes), I'd like to improve my level editor so that it could handle .cmd files -- and especially graphically represent and edit monsters.

Je voudrais parvenir assez prochainement à vous faire une "nouvelle release" (comprenez, greendemo avec un 3eme stage) qui fasse profiter pleinenement de l'ajout des pentes. Mais pour ça, il faut que je progresse encore un peu sur l'éditeur de niveau, histoire de lui permettre de gérer les monstres présents dans le niveau. Grosso-modo, il s'agit de permettre graphiquement l'édition des lignes du genre "gob4 :state8 (300,240)" qui positionne un nouveau monstre et définit son état initial. La bonne nouvelle, c'est que puisque j'ai "extrait" les comportements dans des fichiers .cmd séparés lors de la dernière release, celà devrait réduire le nombre de lignes à traiter. L'inconvénient, c'est que je n'ai pas de liaison entre "state8" et une image représentant, p.ex. Funky Funghi à moins de me retaper le parsing de ces fichiers séparés (pour identifier les images associées aux animations associées aux états :P)

There's just a silly little thing to figure out for this: how am I going to know what graphic should show up as "state8" in a gob-description-line without importing the whole GameObject.cpp file into the level editor ...

mardi, décembre 22, 2009

Tracking Memory Bugs

malloc et free sont des outils formidables, mais quand on s'est un peu embrouillé dans son code, ça devient vite une plaie. Je m'explique: le système d'allocation dynamique ne fonctionne pas "magiquement": il conserve autour des blocs libres et occupés des informations : taille de la zone, prochaine zone libre, etc. Elles sont évidemment importantes lorsqu'une zone libre est allouée ou qu'une zone occupée est libérée, mais elles servent aussi lors d'opérations de "compaction" (pensez au defrag du disque windows, vous n'êtes pas loin). Si on dépasse l'espace alloué pour une zone A, on peut facilement aller écraser les informations de contrôle d'une zone B. Du coup, on perd le lien entre la cause de l'erreur (le code qui gère A) et ses conséquences (erreur lors de la libération ou de l'allocation de B).

Dynamic memory allocation is a key aspect of programming that both open new horizons and puzzle newbie programmers. Unfortunately, it can also become a real nightmare for the seasoned programmer who has overestimated his skills, even just temporarily. malloc and free are not built from magic out of the void*, of course. They are algorithms that use small memory headers to maintain internal information about which block is free, how big is what, etc. This information is used when allocating/freeing blocks, of course, but also in the process of coercing small, contiguous free blocks into a larger one to avoid excessive memory fragmentation.
The problem is, that if code responsible for block A trashes information about block B (e.g. due to a buffer overflow bug), the error remains latent until block B is used again, and by then, you completely lost the knowledge that A is the faulty guy.

Ca fait un moment que je traine un bug de ce genre dans runme, sans trop savoir si c'est mon code qui est en faute ou celui de NTXM, le module player que j'y ai intégré. Bug plus ou moins facile à reproduire d'une révision à l'autre, mais que je sais être lié à une erreur de gestion de mémoire grâce au "crash code address" indiqué sur l'écran "guru meditation". Ces dernières semaines, j'ai pas mal avancé dans la gestion des erreurs et j'ai maintenant une fonction die() qui peut reprendre la main en cas de guru meditation ou d'exception C++ non traitée. Elle peut à présent tenter de retourner au moonshell, mais aussi afficher le contenu de n'importe quelle zone de mémoire de la DS. De quoi étudier la situation de manière un peu plus méthodique.

I know that I've got such a bug in runme (hence the lack of releases and the extensive use of gedsdemo). It's reappearing now and then and then hides again for a few releases. I've already proceed to a few code sanitization steps, but it doesn't look like i've been effective. With my recent findings (__cxa_terminate) and the introduction of a die() loop that replace main() after a guru meditation has been notified, I've given myself a memory inspection tool that let me follow "valid blocks" (starting the list from entries in __malloc_av_, the entry point of free lists) up to a faulty block that precedes the one where malloc/free() triggered an exception due to a wrong size.

Now, the goal is to trace back the owner of this memory block ... to be continued.
Un papier ... un bic ... pas mal de patience et la DS branchée sur chargeur ... C'est parti.

jeudi, décembre 17, 2009

512K! Time for Diet Code!

Quand j'ai lu l'article de Cearn sur la taille des binaires C++ pour NDS, je dois admettre que j'avais un léger sourire en coin. C'est bien connu: C++ a besoin d'un "run-time", cet ensemble de code quasiment inévitable qui permet au langage de tourner parce qu'il fournit, p.ex. la gestion des exceptions, etc. Par contre, quand j'ai vu hier lors de la dernière mise à jour de runme que l'exécutable avait gonflé jusqu'à 512K, j'ai cessé de sourire.

Sur la balance du code DS, pour moi, 512K, c'est "Va courir!"

J'ai ressorti la page de Cearn (l'auteur de TONC, ma première référence en programmation GBA) et tenté d'améliorer sa technique pour savoir d'où cette augmentation subite provenait. Bon, déjà, je compilais toujours sans aucune optimisation (-O0, pour faciliter le debugging de ces dernières semaines). On redescend vers les 420K. Ouf. Je continue, passant de la sortie de nm au désassembleur pour trouver qui appelle les fonctions que je n'ai pas écrites moi-même, genre d_print_comp ou _dtoa_r ... A force de creuser, j'ai fini par identifier deux fonctions à la base d'une bonne part de l'overhead. Tirant avantage du mécanisme de liaison des programmes aux bibliothèques, j'ai remplacé ces fonctions par du "code creux", court-circuitant ainsi un bon 40K de run-time support:

 /** these are heavy guys from the lib i want to strip out. **/
extern "C" char* _dtoa_r(_reent*, double, int, int, int*, int*, char**) {
die(__FILE__, __LINE__);
}

extern "C" char* __cxa_demangle(const char* mangled_name,
char* output_buffer, size_t* length,
int* status) {
if (status) *status = -2;
return 0;
}


Et très sympathiquement, Cearn lui-même s'est montré plutôt intéressé par mes résultats.

Nice! How did you find these? That is to say, how did you find out these are the ones at the top of it and can safely be removed?

There are also two others that I found out about recently: __cxa_guard_acquire() and __cxa_guard_release(). They were introduced when I tried to create a local const
array using data from a global constant instance that used templates. I'm assuming these call __cxa_demangle() at some point, but I can't be sure.

Voici donc ma réponse, "comment je m'y suis pris":

$sylvain> nm -S --demangle runme/arm9/runme.arm9.elf | sort -k 2 | grep '^[0-9a-f]\+ [0-9a-f]\+ [^B] ' --color=always | less -R

is how I find the "heavy hitters". I know that __cxa_* is run-time support. For _dtoa_r, I was suspicious because the disassembled code featured many calls to builtin_(insert arithmetic function name here)* things, while i'm not doing any FPU things internally. I tried replacing all the *printf with *iprintf and *scanf with *scanf and then I realised that at some point, both function actually called the same internal function that contains the full logic for floating points as well (maybe it's due to vsniprintf and that iprintf would be just fine).

I then got clued from the library content that "dtoa" is likely "double-to-ascii", and decided to replace it with a "runtime error report" function. So far, it didn't affected the functionality of the code.

The story for __cxa_demangle was more complicated. Initially, the function i was suspicious about was d_print_comp. Again, i tried disassembling and tracing back "who calls that", but it turned out that noone actually called it (that is, it is a virtual function of some sort, called only through a pointer and the content is statically defined). then i scanned lib*.a for a hit on d_print_comp (DS/dka-r21/arm-eabi/lib/libsupc++.a, if you ask), who revealed the symbol was present from cp-demangle.o, where d_print_comp is a "static" (internal) symbol, and __cxa_demangle is the only "external" symbol. I further googled for information on __cxa_demangle, and found http://idlebox.net/2008/0901-stacktrace-demangled/cxa_demangle.htt, where I found the error codes, full function prototype, etc. I gave "status=-2" a try with a dummy exception, and all of sudden, it reports 16iScriptException to be caught, while the code shrunk by ~100K. Bingo.

Similarly, i identified __cxa_terminate() which i succesfully replaced using std::set_terminate(my_terminator) who is responsible from handling uncaught exceptions. I don't feel like just abort()ing a DS program, so now when I got that, I fall back to a "press A to return to moonshell, B to download software upgrade" menu.

Yet, I'm not hot about killing __cxa_guard_acquire() and __cxa_guard_release(). They are required to ensure you got a lock (guard) on the static initialisation. They're defined in guard.o, and from what I see of nm output on libsupc++.a, they rely on throw, unwind, class-type-info, etc., but not __cxa_demangle directly. Even if they would, i did not _remove_ __cxa_demangle here, just replaced it with a "oh, sorry. I cannot demangle that for you. How about showing it raw to the user ?"


mardi, décembre 15, 2009

Slopes, Cont'd.

Initially, I thought handling of slopes would be well-documented and tutored on the Web. The reality is that it is still a poorly-covered subject, that often puzzle hobby game programmers. I remember of discussions where developers (i.e. Gregg) claimed "think twice before adding slopes to your game. If you can go without them, do it without them". And I wasn't quite satisfied with the implementation proposed by Tony Pa in "Tile Based Game Tutorial" series, either. Tony listed a number of slopes that are not allowed (i.e. anything that isn't a 45° straight line) and arrangement of slopes on the map that lead to bugs or glitches.

I intended to overcome these limitations in my own implementation, and I'm in the process of cross-checking to which extent I got it working. I'm still sticking with 45° items right now, despite adding other shapes should merely be a matter of pushing more data[] in the engine and extend iWorld::groundheight() so that it looks for those new shapes.


SMB3, un des premiers jeux de plate-forme à introduire des pentes, va doucement sur ses 20 ans. On pourrait s'attendre, du coup, à ce que la gestion de terrains pentus soit un sujet largement traité dans les tutoriels, mais c'est loin d'être le cas. Quand j'ai attaqué la question, même le tuto de Tony PA "Tile Based Game Tutorials" ne m'avait pas satisfait. Il ne prenait en compte que des pentes à 45°, mais en plus, certaines combinaisons conduisaient à des bugs ou des "glitches" désagréables. Pour construire mon petit algorithme, je me suis donc appuyé sur un tableau "groundheights[]" pouvant être étendu avec n'importe quelle forme de terrain : marches, dunes, pentes de degrés divers, etc. Notre seule limitation sera le nombre de bits disponibles pour indiquer de quelle pente il s'agit. Et pour trouver l'algorithme correct, j'ai appliqué les fonctions élémentaires que je m'étais données (ground_height, next_tile, etc.) sur chacun des cas qui peuvent être rencontrés et que j'ai maintenant reproduit dans un recoin du niveau 1 de la greenzone. C'est presque fonctionnel, à l'exception du cas "e" (la pointe) où Bilou se "coince" et ne peut être débloqué que par un saut (l'appleman, lui, a déjà trouvé la parade et "saute par-dessus" l'obstacle !)
The reason why I want slopes is level design : they allow a regular "walker" ennemy to find itself in a better tactical position (above you -- "in your functional blind box", would say Kirby Kid), as anyone who played Commander Keen knows. Until a "sloped level" is properly built, I altered the part of my level 1 that features appleman with all possible slope combinations. Walking right-to-left, you'll encounter (g), then (h) and (e) near the wall. So far, only (h) causes coding trouble: Bilou just stop at the top of that "spike" and refuses to move any further. The Applemans happily work around this situation by either turning back (if Bilou is not in sight) or jumping over the spike to attack Bilou. Cases (f) and (h) are automatically covered by the cando() tests that complement hotspots in my engine.

The algorithm I presented a few weeks ago is still mostly unmodified, but it had a major defect: it only moves you along a slope. The fact is that it should also inform the caller whether 1) we are on normal ground (and thus will fall if we can) 2) or on a slope (and thus don't fall) or 3) if we just couldn't move because there's something in our path. A bit more critical thinking is needed here to lay things down properly.

Encore un peu de patience, donc, et je vous livrerai l'algo revu et corrigé. La principale difficulté est qu'il avait été pensé uniquement pour suivre une pente existante, mais que j'ai aussi besoin de savoir si on est toujours bien sur la pente ou si les tests de chute habituels peuvent à nouveau être appliqués.

dimanche, décembre 13, 2009

Go Create !

Finalement, la DS ressemble terriblement à ces micro-ordinateurs 8bit de mon enfance, où un jeu était écrit en 1/2 journée, sans se prendre la tête. Les graphismes symboliques n'ont rien perdu de leur charme, ni les petits sons blip-blops de leur éclat.

Alors les gars, "go create!"

Laissez donc tomber le repompage de sprites, les RPG online trop ambitieux et compagnie. Faites simple, mais faites marcher votre imagination, comme l'ami Jayenkai.

By many ways, the Nintendo DS looks very much like these 8-bit microcomputers of my childhood. Back then, you could write a game on a single wednesday afternoon (and use a whole week-end to make it work properly :) You don't need to be a top-artist if you embrace symbolic graphisms. You just need good taste and creativity. So go ahead ! forget your FPS and MMORPG. Think small, but CREATE. You don't need sprites ripped from some commercial production. Be more than a fan: be yourself.

jeudi, décembre 10, 2009

Enfin, des pentes qui marchent !

C'est encore un peu mystérieux, mais ça commence à fonctionner! Petite démo téléchargeable (non-jouable, par contre) de mon "cas d'étude pour le débugging". Je vais tagguer le CVS pour qu'on puisse retrouver les sources correspondantes.

Je dis "mystérieux", parce qu'au départ, l'appleman semble "hésiter" à prendre les pentes, chose qui n'a pourtant pas été programmée comme telle. Je soupçonne un effet de bord des vitesses inférieures à 1pixel/frame, sans avoir pu creuser complètement.

Howdy! It starts working (though still subtly mysterious). Rather than trying to shoot you a full-blown Youtube Video, i used byzanz-record and some javascript to provide you a nice "video" teaser (hover the picture, you'll see ;). On the other hand, you're welcome to grab the running .nds and try it on your favourite emulator or linker.

Descente (aux enfers ?)

J'ai beau avoir pris le temps de tester conceptuellement et méticuleusement le petit algorithme de gestion des pentes que je présentais la semaine dernière, je me bats toujours avec mon code pour l'intégrer au game engine.

Le plus intéressant dans ce combat, c'est sans doute que jusqu'ici, aucun des problèmes ne provient directement de l'algorithme ...

Détails du Standard
J'en parlais tout dernièrement: prendre pour acquis et "standard" un comportement que l'on a rencontré sur une plate-forme est une erreur facile à faire pour qui utilise le C ou le C++ dont la définition est pleine de "comportement indéfini" ou (pire?) "définis par l'implémentation".

Interaction avec l'ancien code
En particulier, le système qui contrôle le déplacement pas-à-pas et les test-points contrôlés par les animations peuvent conduire à des décisions contradictoire du système de contrôleurs qui appelle doslopes(), d'où l'introduction d'une commande "move x *" pour ne placer une contrainte que sur le déplacement horizontal et laisser le déplacement vertical libre.

Fournir les bons arguments
L'algorithme reçoit vitesse et position en pixels. Dans mon moteur de jeu, pourtant, certains éléments travaillent en 256emes de pixels (pour mieux gérer les vitesses) et d'autres directement en tiles. Le système de types de C++ étant ce qu'il est, on a vite fait de prendre l'un pour l'autre et d'avoir une implémentation incorrecte parce qu'elle essaie instantanément d'envoyer le joueur à l'autre bout du niveau au lieu de le faire monter d'un pixel.

Le code de base n'était pas prêt !
Comment ça, la pente gauche et la pente droite ont les même propriétés ? Et le bloc semi-perméable ne laisse pas passer les monstres ?

Je ne serais pas surpris que ce soit le genre d'erreurs que rencontrent couramment ceux qui programment "en entreprise" et doivent déployer telle ou telle nouvelle solution dans une base de code pré-existante assez large. J'en suis réduit à "sortir l'artillerie lourde" :

  • d'abord vérifier que le nouveau code ne pose pas de soucis au éléments plus anciens (est-ce que les 2 niveaux fonctionnent correctement si je n'y mets aucune pentes ?)
  • construire un test-case minimal (un seul appleman, placé directement au bord d'une pente)
  • suivi méthodique de l'exécution (ddd avec du code -O0, placer des breakpoints dans toutes les fonctions succeptibles de modifier l'état de l'appleman, inspecter le "stack trace" pour comprendre pourquoi ces modifications d'état se sont produites).
  • Corriger "à la volée" les calculs erronés ("set value ..." dans ddd) pour poursuivre le déroulement du programme le plus loin possible sans passer par un nouveau cycle de compilation (j'avais plus fait ça depuis Crazy Brix, tiens !)
  • Procéder par élimination. "Une fois l'impossible éliminé, ce qui reste -- même improbable -- est forcément vrai, mon cher Watson".
Encore un peu de patience, donc. Quand les causes seront parfaitement identifiées, il restera à chercher une manière élégante d'intégrer les nouvelles contraintes au moteur de jeu ...

mercredi, décembre 09, 2009

-Wconversion

Ca doit faire bien 10 ans maintenant que je pratique quotidiennement le C (ou un de ses "dérivés"), et il y a des jours où je crois honnètement avoir fait le tour du langage. Pourtant, les pièges restent bien présent, en particulier compte tenu du fait que je cesse de le pratiquer dans un seul environnement. Usermode, kernel mode, programmation embarquée sous ARM, etc. A force d'avoir utilisé uniquement gcc-linux-ia32 pendant un paquet d'années, il y a des particularités que j'ai fini par prendre pour des vérités. Un exemple: ce code m'a valu bien des misères avec la gestion des pentes :

#include <stdio.h>
char n=-1;

int function() {
return n;
}

int main() {
printf("char -1 is %i\n",function());
return 0;
}
Just realised while looking at the actual value of some ARM register that my code cannot work properly if i try to add 0xff to a 32-bit value to decrement it. I swear i've been using a char though. And that was precisely my mistake. I overlooked that, unless ints, chars signedness isn't defined by the C (or C++) standard, but rather by the platform's implementation. And x86 and ARM implementations differ, for that matter. So the code above might display "-1 is -1" or "-1 is 255" depending on the target you compile it for, and even worse, it will compile without complaints (even with -Wall) unless you explictly request -Wconversion on the command line. Read this "scatter-gather thoughts" to learn more about it.

Dans ma tête, "un char = un byte signé = -128..127" et bien sûr, comme je l'ai appris il y a longtemps à mes dépens. Et bien sûr les "int", dont on ne sait jamais trop bien la taille, sont signés aussi. Eh ben il faudra que je relise plus attentivement le Kernighan&Ritchie, parce que je me suis retrouvé avec 255 sortant de function() ! En ajoutant (à la recommandation de Cyril) -Wconversion à mon code, j'obtiens enfin un avertissement du compilateur :
test.c:2: warning: negative integer implicitly converted to unsigned type
En fait, à relire ce très bon article sur "scatter-gather thoughts", je prends enfin pleinement conscience de mon erreur: un char peut être signé ou non-signé par défaut, en fonction de ce qui est préférable (le plus optimal) sur une architecture donnée. Sur x86, c'était donc char = int8_t et sous ARM (au moins avec le devkitpro) char = uint8_t.

see-also : signs from hell by Conarac

mercredi, décembre 02, 2009

Handling of slopes.

Okay. It might be a little pathetic ... I've spent some time figuring out an algorithm for properly handling slopes in my platformer, but i didn't found the time to properly inject it into code, so I just speed-typed it in a draft post. One of the key ideas is that you won't try to adjust frame t's horizontal speed to the slope you have to cross, but instead defer the effect of climbing dy pixels to the next frame's horizontal speed. This allows a one-pass while all my previous attempt had to iterate until they find the position you'd actually take given the slope you have to climb.

  • dx : horizontal speed for this step.
  • tile(x) : returns the tile holding a given coord.
  • end_of_tile(x, dir) : last pixel of tile containing x ends when you go towards dir.
  • next_tile(x,dir) : first pixel of the tile after tile containing x when you go towards dir. next_tile(x, dir) == end_of_tile(x, dir) + (dir>0)?1:-1
  • ground_height(x,y) : in the tile under (x,y), the height of the ground at specific location (x,y). 0 means "this is a sky tile", there is no ground to stand here. -1 is the lowest pixel of the tile, at -7, you should be on the highest pixel of the tile, and at -8, you should actually not be on this tile, but on the lowest pixel on the one just ahead.
Since we don't know how the slope "continues" to the next horizontal tile, we have to "pause" at the end of each tile in order to compute the Y coordinate we'd have then. When dx < TILE_SIZE
new_x = x + dx
while (tile(new_x) != tile(x)) {
gh = ground_height(end_of_tile(x,dx),y);
if (gh!=0) y = end_of_tile(y,1) +gh;
x = next_tile(x,dx);
}
Now, we know what height we have when we enter tile(new_x). We just have to compute where we'll end in that tile and we've finally done.
if (ground_height(new_x, y) == 0) {
new_y = end_of_tile( next_tile(y,1), 1) + ground_height(new_x, next_tile(y,1));
} else {
new_y= end_of_tile( y, 1) + ground_height(new_x, y);
}
Quick Note : the 'x' and 'y' are copies of the object's hotspot position, and the result of the algorithm is the new_x, new_y position at which the GOB's hotspot should be moved. I still have to figure out where cando(new_x, new_y) should be applied to ensure walking slopes do not allow one to move through walls, etc. I also have to figure out how to integrate this properly with the OO model I have so far : what is under the responsibility of iWorld, iGobController and GameObject.


C'est par manque de temps que ce petit algorithme de gestion des pentes se retrouve sur ce blog plutôt que dans le code. Une des idées simplificatrices de base (par rapport aux essais précédents) est qu'il n'est pas nécessaire de traiter immédiatement l'effet de la pente sur la vitesse horizontale (je ne sais pas vous, mais moi, je vais généralement moins vite en montée qu'en descente). A la place, je peux utiliser l'élévation du sprite à l'instant t-1pour ajuster sa vitesse horizontale à l'instant t... Le résultat, c'est que le calcul de la position actuelle peut être effectué en une seule passe, se basant exclusivement sur la fonction ground_height() qui retourne pour chaque position horizontale dans un tile pentu la hauteur de la pente depuis la base du tile.

ancien post-scriptum: je ne vous ai pas oublié, mes lecteurs francophones (près de 50% d'entre-vous). Vous aurez peut-être compris que ce billet fait office de "pseudo-code retapé en vitesse et commenté". Le temps de loisir est distillé au compte-goutte ces temps-ci, entre les dents de *deline et les grands rangements de la maisonnette qui en a bien besoin.

mardi, décembre 01, 2009

The Measure of Mario - Coins, contd.

In reply to Kirby Kid's "Measure of Mario : Coins"

Afaik, the "dragon coins" of SMW were used to compute "how much in % you completed the game", and it was the first nintendo game to provide such "comparable" scoring system. That is, claiming in playground that you completed SMB3 with 9,999,999 points doesn't tell much. Claiming that you completed SMW at 103% brought you respect from your classmates.

Star coins in NSMB are also a money for buying game save, so they is a tight balance for the travelling gamer between taking the risk to collect one (at the cost of one life) or having to shut down the game without being able to save your progress because you haven't got enough star coins to do so and are running short of lives/time to beat the next fortress. Afaik, this additional mechanism was first introduced with "kong coins" in DKC2.

Kirby Kid avait passé en revue les pièces d'or de Mario. Elles servent essentiellement à encourager le joueur à sauter à des emplacements autrement plats, à explorer, à récompenser les chercheurs de secrets et les preneurs de risque. Selon moi, c'est à partir de Super Mario World que Nintendo introduit un comptage du "pourcentage de finalisation" du jeu, qui était plus facile à comparer que le score final en nombre de points. Ce qui comptait, c'était ici de rassembler les pièces-yoshi.

On retrouve quelque-chose de très similaire avec les pièces-étoile de NSMB. En plus du côté "je t'en bouche un coin" du jeu complété à 103%, les pièces étoiles permettent d'acheter des sauvegardes à n'importe quel moment. Pour celui qui ne joue pas à 2 mètres d'une prise électrique, celà rend les pièces-étoile particulièrement importantes, succeptibles de décider si oui ou non le jeu peut continuer. On voit le challenge de récupérer la pièce-étoile d'autant plus significatif.

Red coins were first seen in Yoshi's Island, but not with that "timely challenge". They're somehow a mixture between dragon coins and P-switches that divert you from your main mission (beat the level) with an extra challenge that you cannot read in advance. I think they add indeed a layer to the game, but the result is that 1UP get too cheap in the game. I somehow regret that NSMB doesn't have something like a "hard core mode" where such "red coins challenge" would be the only way to get a 1UP.

SMB3 and the following have indeed much more ways for you to get "a 1UP for free", such as hitting a yoshi-containing ?-block when riding yoshi, finding a starman in an area where there is enough koopas, etc. Compared to the Great Giana Sisters where collecting 100 diamonds is virtually the only way for you to get a 1-UP, i barely find myself "hunting for coins" in the later Mario games, where they are devaluated. In a fresh game, i'd be tempted to reinforce the role of those "suspended lives". I agree with the fact that such bonuses should "read" as the level-designer's favourite path rather than "the one path to go". And insisting on the fact that the player should collect them all (like in Rayman 2, iirc) is a painful way for "extending" the lifetime of a platformer.

Les autres pièces spéciales de NSMB, ce sont les pièces rouges. Je les ai vues la première
fois dans Yoshi's Island, mais dans ce cas, il ne s'agit pas d'un défi d'atteindre un certain nombre d'objectifs en un temps limité. C'est une façon de détourner le joueur de sa mission principale (finir le niveau) avec un challenge alternatif, mais plus difficile à lire, puisqu'elles ont presqu'exactement le look d'une pièce classique, avec juste un reflet rouge.

Pourtant, leur effet est réduit dans chacun des jeux parce que le jeu offre de nombreux autres moyens de gagner bien plus de vies d'un coup. On en manque normalement jamais. C'est une grande différence des Mario depuis SMB3 avec, p.ex. Giana Sisters -- où récupérer 100 diamants est presque la seule façon d'avoir une vie supplémentaire. Les vies, dans les Mario suivants, sont assez dévaluées.

La question de la dévaluation des 1-UP pose la question "comment récompenser le joueur". Ils étaient une approche facile dans un environnement où le joueur manque de 1-UP pour terminer le jeu. Des sauvegardes et des continues, c'est une autre façon de procéder. Les power-ups, encore une autre, ou débloquer l'accès à des niveaux supplémentaires (le monde caché de DKC2, et les oiseaux-bananes de DKC3). Aujourd'hui, on a des trophées (à obtenir à partir de coquillages "secrets") assez artificiels, qui personnellement ne me convainquent guère. Je leur préfère la façon dont Professeur Layton permet de découvrir l'histoire sous-jacente au jeu, explorable en-dehors du jeu de base.

I also note that, between "regular coins" (suspension) and dragon/kong coins (no suspension, but exploration required), there is an intermediate approach that doesn't appear often in Nintendo platformers but that i've seen e.g. in Prehistorik 2, that is suspended-but-unique "coins". You'll find letters E-X-T-R-A or B-O-N-U-S in a level, and each level has the 5 letters. They are suspended from one level to another, and you'll get a "bonus rush" when you managed to get them all, though getting a second O when you already have B-O- -U-S doesn't make a difference,but instead tease you. Seeing the 'U' when the last letter is a 'O' also hints the player that he's been missing some nice part of the level (like seeing that the first star coin you encounter is the rightmost one in NSMB).

Ultimately, coins pose the question of "how do we reward the player". 1-UP is an example, continues/saves another one. Power ups obviously another one, as well as enabling access to bonus/extra levels (e.g. Krem Koins enabling access to the Lost World in DKC2) or alternate ending (banana birds in DKC3). They are typically devaluated in modern games, and replaced by more "artificial" rewards such as trophees (secret seashells in ZMC opposed to secret seashells in Zelda : Link's Awakening). Note that Professor Layton also provided a nice idea of using "collectibles" as a way to tell the story at a pace that fits the player rather than forcing it to the player, interrupting the gameplay with tale telling or tutorial levels.

En-dehors du monde de Mario, j'aime beaucoup le système des lettres B-O-N-U-S de Titus (prehistorik 2), qui ont un côté "suspension" comme les pièces de Mario (e.g. on peut étaler la récolte sur plusieurs niveaux du jeu) mais aussi un côté "lettres KONG" ou "pièces dragon"  (il faut les récolter toutes pour que quelque-chose se passe). La différence entre les lettres KONG et les pièces dragon, c'est qu'elles sont uniques. Voir le 'N' après le 'K', c'est le signe qu'on a loupé un secret entre les deux, alors que toutes les pièces-dragon de SMW sont identiques. Avoir un nouveau 'U' alors qu'il ne nous manque qu'un 'N' provoque un titillement du joueur dans prehistorik 2: ça fait deux niveaux d'affilée que tu as manqué le 'N'. Si tu ne fais pas demi-tour pour mieux chercher, toutes les autres lettres B-O U-S que tu trouveras dans la suite du jeu ne te serviront à rien jusqu'à ce que tu trouves un 'N'.

"How I mastered the butt-stomping attack to break thin platforms and access my treasure rooms, by Funky Funghi" could be teared apart in 5 pieces, scattered around the forest, so that Bilou has to collect them to get a hint on the presence of another attack technique, more secrets, etc. "How Pencils soldats suddenly appeared all over the place" similarly scattered fills the backstory in a nice way if player is "clued" on what happens, but not boringly "told". Give Small World a play, you'll know what I mean.

lundi, novembre 30, 2009

With Great Powers ...

As I was reviving the idea of a full Bilou game, a few years ago, it became obvious that the "super-powers" we designed initially weren't very good gameplay-wise. The original game design from Bilou was that every defeated boss let you recover a "magic stone" that gives Bilou an extra ability that you can use at any time if you collected enough "magical bonuses". Some part of the level (usually containing 1UPs or similar bonuses) could only be explored on a second (or Nth) pass when you obtained more powers than the one you've got initially.
Les super-pouvoirs que Bilou reçoit en battant les boss étaient un des éléments déterminant du "game design" de Bilou au début du projet, en '94. Avec le recul, il ne reste pas grand-chose de la liste originale qui me paraisse toujours approprié. En particulier, l'idée qu'ils permettraient de revisiter le monde 1 pour y trouver des 1UP cachés en volant ou en nageant ne me séduit plus depuis que j'ai du faire la chasse aux electoons dans Rayman 1.

By that time (just before Rayman came out on the PSX), we though it was a brilliant and novel idea for our game. Now that I've played Rayman and DKC end-to-end, I know that revisiting levels without purpose (that is, just for powering up) is not exactly fun, and that doing it because you can't access the final boss otherwise is just frustrating, especially when you realise that it doesn't matter how well you played the first levels, you'll have to play them again.

Il y a quelques années, je m'attelais donc à "repenser" ces power-up sous un angle nouveau: les magic stones transforment des objets "ordinaires" en bonus qui activent un pouvoir particulier, plutôt que d'avoir une jauge de magie unique multi-usage. Ca me paraît une meilleure approche pour construire des niveaux intéressants. Par contre, parmi les nouveaux pouvoirs que j'entrevoyais ici -- essentiellements axés sur un changement de matière pour Bilou -- très peu on trouvé grâce aux yeux de mon frère. Comme j'ai également appris entretemps que c'était exactement le principe de "Within a Deep Forest", je sais que je devrai encore refaire une passe là-dessus.
I also wasn't very convinced by the "one kind of magic bonus to fuel them all" approach, because it doesn't allow nice level designs. Especially, if the player use all its magic points to fly around the level when he's not supposed to, he won't be able to swim that pool to move further. And if you have "respawning" magic bonuses, the best jumping puzzle might be spoiled by the flying power (or the shooting power, or anyone of those). The idea then came that the magic stone would reveal power-specific magical bonus, so that the level designer can decide what you can use when, but with a time limit that makes them more like the Starman in supermario than e.g. the fire flower.

A comment on pixelation made me start this alternate set of super-powers, where the material Bilou is made of is affected, making him light enough to walk clouds, hard enough to break stones or bouncy enough to do superjumps. My brother didn't enjoyed the idea too much, though, so this isn't the definitive list anyway.

jeudi, novembre 26, 2009

En attente de commentaires ...

I'd like to do a second pass on the pixel art of Bilou game. Here are a few references i collected.

Une scène du niveau "Chameleon Sting" de Mega Man X. Alors que j'ai tendance a avoir le même éclairage sur tous mes blocs de terre, les artistes de Capcom rompent la monotonie en changeant la quantité de lumière que les rocs reçoivent. Certains sont plus dans l'ombre, d'autres bien éclairés, etc.
A noter aussi qu'ils se sont simplifiés la vie pour le "roc de fond" en utilisant une couleur sombre et unie de laquelle ils font simplement ressortir quelques détails, et bien sûr avec un soin particulier apporté aux "bords" de cet environnement.
Enfin, la "machinerie dévoilée" joue le même rôle que les slugs juniors endormis dans Commander Keen: attirer l'attention du joueur afin qu'il ne cherche plus la répétition du reste des blocs.
Chameleon Sting's level in Megaman X wasn't really convincing for his trees, but it has an interesting approach for rocks. I tend to give all the rocks of a wall equal amount of lightning, but the artists here advantageously altered the "depth" of indivudal rock by varying their shading. One will also note single-color cave background that is compensated through higher level of detail at the edge. The unveiled machinery plays a role similar to the sleepy slug in Commander Keen : focus the player's attention on an usual part of the background and make him forget that the whole rest of the scenery is repetitive.

Tout autre genre, un morceau de "pekka-kana", que j'aurais presque tendance à utiliser comme contre-example. Trop flou, usage abusif des filtres, un arbre dont le tronc ressemble à un mur, etc. Pourtant, j'avais été assez séduit par les feuilles en blocs dans la jungle de Kirby Squeak Squad. On verra bien si mes "plate-formes feuillage" donnent de meilleur résultats, mais personnellement, j'aurais tendance à éviter. Ceci dit, ce sont des screenshots de la version 3, toujours en développement, donc pas forcément qqch qui sera conservé.
I once thought (after playing Squeak Squads) that applying a leaves texture on the shape of a tree could make it look like a tree while providing easier to use gameplay elements (regular platforms). Peka-Kana demonstrates how wrong i was. I take it as a counter-example.

Une approche simplifiée, pour un jeu d'arcade que je ne (re)connais pas: c'est le level design qui prime avec des "rampes" boisée et quelques troncs dans le décor pour faire bien. On cherche tout juste à donner un thème, pas vraiment à placer le joueur dans un environnement virtuel crédible. Le choix de couleurs est intéressant, ceci dit. Les "boules" répétées le long des "rampes" amènent de la variété, sans être très convaincantes.
In arcade games, you might end up with trees that are just "abstractly suggested" rather than actually represented. In this (unknown) game, they pushsed it to the extreme with ramps of green-and-wood texture (nice textures, actually) that exactly fit the needs of the gameplay. Everyone knows no tree behave like that, but you all know there aren't flame-throwing slugs around, don't you.

Et ici, la forêt dans Blork Carnage. Avec les arbres-palissades, le cushion-shading et les dégradés abusifs. Bref, tout - mais alors absolument tout - ce qu'il faut éviter. Ca ne vaut rien. Nada. Que dalle. Et je sais de quoi je parle: c'est moi qui l'ai "pixelisée" il y a 13 ans :P
Just to make it clear, this one is *not* reference material. It is the worst "woods" i've ever drawn in a freeware game dubbed 'Blork Carnage' some 13 years ago. A piece of nostagly to remind me "the errors that shouldn't be reproduced".

Tiens, ben voilà Keen, justement. Quand on parle du loup...
Pour de l'EGA, ça reste impressionnant, comme résultat. Je regrette un peu l'absence complète de vert, personnellement. Mais il y a quand-même le "désaturage" des couleurs quand on s'éloigne de l'avant-plan. Et un décor pareil, en parallaxe, ça serait difficile à gérer. Mais il faudrait que j'essaie de parvenir à quelque-chose de semblable juste pour l'exercice, tiens. Au moins une fois.
Noticed how Commander Keen artists managed to have trees de-saturating when they're far away ? okay, maybe it would have looked even better with something else than plain black, but that was EGA palette. Though such backdrop is a technical nightmare for parallax scrolling, i should try to do something alike at least once, for the sake of the exercice.

Autre ambiance, sur un mockup de Ben2theEdge pour une forêt sombre, presqu'en décomposition, où les racines vont servir de plate-formes. A noter à l'arrière plan un feuillage particulièrement réussi et particulièrement économe en couleurs. J'ai tendance à souhaiter une symétrie dans les détails en haut et en bas des zone de feuilles, mais en fait, c'est l'inverse que je devrais faire!
Sudden mood change, with a mockup drawn by Ben2theEdge. Dark woods, almost falling in pieces. Note that here, the "ramps" that fit the need of the gameplay can immediately be identified as giant roots. I also love how efficient the foliage of background trees is for a reduced set of colors. I tend to draw trees with an equal amount of details upwards and downwards. Ben proves here that this is not necessarily the way to go. I hope one day SEDS will be able to let me work on background like these.

Doremi Fantasy, plus pur style "chambre de *deline", avec le classique (presque stéréotypé) sol géométrique, des troncs bien verticaux et éloignés de sorte que l'on puisse sortir le grand jeu du parallaxe sur les différents plans de verdure.
Ca a beau être simpliste à l'extrème, je ne cesse de m'émerveiller du rapport effort/résultat que ce genre d'artiste parvient à obtenir.
Okay, definitely at the other side of the spectrum, Doremi Fantasy provides the stereotype of geometrical ground, fully vertical trunks and parallax-friendly layers of "green hills" that suggests you're somewhere between two layers of the Master Forest. I'm impressed by the effort/return ratio such artists achieve.

Le summum dans le genre est sans-doute atteint dans "Trip World" sur game boy. Rarement vu une forêt aussi bien rendue sur cette petite console. Le jeu étant exclusivement à scrolling horizontal, ils ont pu se permettre un feuillage sur une seule bande (mais qui s'assemble admirablement bien, je le reconnais).
And if you apply it to the monochrome display of the Gameboy, "Trip World" is likely the best you can get. I've rarely seen such a nice forest on that little beast.

Tout autre style: un projet en cours de St0ven, sur pixelation.
Si je ne suis pas terriblement fan de l'approche "sol monochrome", je dois admettre que St0ven maîtrise particulièrement bien le genre et, de par l'ouverture plus importante du reste du décor, s'en sert pour planter une ambiance plus opressante. pour le reste, la taille de l'image et le niveau de détail en fait presque une peinture. Ici, arbres et rochers sont hyper-réalistes. Aucune chance que je parvienne jamais à ce niveau, mais le choix des couleurs m'avait paru particulièrement intéressant.
In a very different style, here comes a mockup by st0ven (found on pixelation). I must confess i'm not a big fan of the "single-colour-ground" approach, again, but the way it is used here to improve the "oppressive" mood has something that definitely deserve respect. Every tree is unique and realist. I even expect something like a FlashBack gameplay. I doubt i could ever achieve that level of perfection at the scale of a videogame, but i especially loved the colours choice.

Pas vraiment dans la forêt, mais "green" quand-même : les Giana Sisters sur DS, premier monde. J'avais été agréablement surpris, au moment des "previews" du jeu par la variété amenée dans les rochers, jouant sur deux niveau de profondeur pour amener de la variété dans le décor. On remarquera aussi qu'ils ont optés pour un look "passerelles à peine en équilibre" plutôt que "masse de terre ferme", ce qui offre en fait bien plus de possibilités pour rompre la monotonie ...
The DS version of Great Giana Sisters brings in some interesting mix in level structure. While the original game was essentially made of "bricks world (and the game play haven't changed much), they gave the "non-playable ground" a very organic look by introducing holes of varying shapes.

Ambiance proche de "chaos engine" pour un mutant de AlexHW (aka. "Final Redemption").

A nice "Chaos Engine" look in this mockup by AlexHW. Not very comfortable with that technique of having the middle of the tree "plain dark", even if I admit it lets you build the tree the way you want with quite few tiles ... a technique vierbit mastered impressively.


Dan Fessler, dans un remake hypothétique du (lamentable) jeu NES "Bible Adventures"... avec une utilisation intéressante de rochers presque "multi-couches" qui forme des plateformes plus pyramidales que rectangulaires. Dan opted for quite rocky ground, rather than just "dirt", which provides quite an interesting design of "stacking" rocks -- pyramidal platforms rather than just parallelipipeds, in some sort. First layers (near the top of the platform) are very close to each other and highly darkened, then they'll get more and more spaced as you take your distance (also, it's less and less "interesting" and shouldn't draw the attention too much in that area). You'll note that despite one would usually draw rocks with a grey tone, Dan opted for a reddish tone with yellow highlight and purple shades -- the usual choice for pixel artists who will reinforce gradients with a hue shift. Bunches of leaves are interleaved with more regular "falling grass" to form the border of the green platform. The "flat" area could still be improved, but it already gives a good look. Note that the same bunches of leaves will be reused for the trees, more or less. Note, too, that the foreground is mostly a "generic green-and-rocks" tileset: ony the huge tree on the second plane and the background define the area as a forest. Dan did a stunning job on that tree, using scarves to give it character and providing volume without falling in the usual "wooden snakes" approach. Maybe one could regret the over-angular look of the roots. I think they echo nicely the angular look of the rocks.



Et enfin, un mockup de Henk Nieborg, le maître incontesté de la discipline, pour la version DS (en cours de préparation) de Shantae. Diversité, Précision, Raffinnement. Il a également utilisé des plate-formes en roc surplombées de végétation basse, avec des teintes plus "classiques", mais s'offrant le luxe de rehausser de plusieurs groupes de fleurs et deux "niveaux" de végétation. Les "touffes de feuilles" font 16x16 ou 32x16 à vue de nez, le "pattern" sur les rochers un bon 64 pixels de large.
Nieborg rehausse son niveau d'éléments plus larges et non-jouable (colonnes et figure de pierre)

mercredi, novembre 25, 2009

Grab me, shoot me.

Tout ça est parti d'une relecture des "notes de développement" que je prenais il y a maintenant 10 ans (eh oui) en préparant mon "Ultimate Game Maker". Si les approches que je voulais suivre sont complètement obsolètes, en revanche, j'avais bien creusé les "scénarios pièges" qui définissent généralement la limite entre un moteur de jeu "spécifique" et un moteur générique. En l'occurence, ici "Bilou attrape un ennemi assomé et s'en sert comme arme sur d'autres ennemis".

Coding in a hospital is definitely not an easy job, but there was at least one thing i managed to do during that "free" time when sleep didn't want to come : re-reading those design notes on the "Ultimate Game Maker" I wrote down 10 years ago, organising them, pin-pointing good and bad ideas. Despite virtually all the technical details now fall in the "BadIdeas" category, there were a collection of "gameplay scenarios" that i can reuse to ensure my current game engine is generic enough. The specific "use case" I further studied was the ability to grab an ennemy and use it as a weapon against other ennemies.

Du point de vue "mécanique", je joue sur la possibilité de définir des propriétés aux zones de collisions. Un appleman normal possède juste la propriété "stomp" qui permet à Bilou de l'étourdir en sautant dessus, une fois étourdi, il possède aussi "pick". Au moment où le joueur enfonce le bouton "poing", on ajoute une zone de collision à Bilou qui teste la présence de "picks". En réaction à ce genre de collision, l'état de Bilou aussi bien que celui de l'appleman sont modifiés. Le même genre de mécanisme avec une propriété "shot" est utilisée pour provoquer le passage de l'appelman de l'état "transporté" à l'état de projectile. Tout va bien.

First, I need to translate the scenario into states-collisions-properties elements of my current engine model. With the recently introduced per-collision-area properties, it goes quite well. The appleman doesn't receive f_pick collisions unless it is in the 'stunned' state, and Bilou doesn't test for f_pick collisions unless the player triggers a punch. Similarly, the 'carried' appleman can receive f_shot collisions that turn it into a weapon agaisnt other ennemies. This was tricky in the UGM model because the class of a Gob (bonus, character, ennemy, shot, object ...) stricly defined what collisions could occur. The new model is much more tolerant and it's only your mind that decide the appleman is now a ennemy and then a weapon. Of course, the controller used in "carried" state aligns position and speed of the appleman to those of Bilou.


Sauf que ... comment au juste vais-je "ramasser" l'appleman ? Je veux dire, le mouvement des mains de Bilou et celui de l'appleman doivent être synchronisés pour que ça marche. C'est le genre de problème qui n'apparaît pas dans Blues Brothers avec une seule étape d'animation et un seul objet à ramasser (la caisse), mais je veux profiter de l'absence de bras & jambes de Bilou pour améliorer les animations, même si j'ai 15 ans de retard sur Rayman pour l'implémentation ^^"

The tricky questions surprisingly came from rendering the actions. How do I define the intermediate frames of Bilou grabbing and shooting the appleman ? It wasn't much of an issue in 8-bit (chip'n'dale) or 16-bit (Blues Brothers) paltformers, because you had at most one frame and one object to carry (the crates). But I'd like to take advantage of the 'limbless' nature of Bilou to improve animation (despite I'm 15 years late compared to Rayman :P)

Plusieurs lignes de conduites sont apparues par rapport à cette réflexion:

  • les sprites d'un Gob composé doivent permettre de définir l'emplacement des zones de collisions, ainsi f_pick est toujours associé à la main droite de Bilou (tout au long de son déplacement) et f_shot à son pied droit.
  • l'ancrage d'un Gob (appleman) à un autre (Bilou) doit aussi pouvoir être défini par rapport à un sprite précis. Ainsi, quand Bilou abaisse sa main pour shooter dans l'appleman, l'appleman suit le mouvement
  • deux "commandes" permettront de définir des animations plus fluides : "goto" et "auto", qui permettent à un sprite d'utiliser une paire des variables du Gob comme vitesse pour atteindre un point de référence donné.
It resulted that in such 'composite GOBs', the individual sprites (hands, feets, body) should be allowed to govern the position of (some) test areas, and serve as anchor points to other 'carried' GOBs. Moreover, we'll love to have "automated" animation statements such as "go to (x,y) using GOB variables v4 and v5 as horizontal and vertical speeds", rather than giving explicit position for every sprite at every frame.

I could have alternatively opted for multi-gobs (that is, Bilou *shoots* a hand that come back at him), but that would have terribly complicated further interactions.

ps: i gave the official google "readmore" feature a second try on this post and i'll drop it: the RSS still provide the full post, making "read more" not that useful. Plus it will doom my backup strategy. Only "ranting" posts will have "read more" stuff.
pps : yeah, that means i'll really have to work on that "modular animation" add-on for SEDS.

lundi, novembre 23, 2009

jeudi, novembre 19, 2009

November Checkpoint

I see coming another Square of Time where all i'll be able to do is small step improvements. Working on the "remaining 10%" and so on. So here's the "achievements" that happened lately.

  • Dynamic Sprite Priorities is implemented and tested.
  • fixed a nasty memory error (bad_alloc thrown) that occured on level loading in runme
  • provided a "action-step-report" system that keeps "logs" unprinted until something "goes wrong", in which case it can dump the whole history of the failed action.
  • fixed the "push-tiles-across-layer" option of the level editor.
  • "prev / next" buttons in the level editor now skip spritesheets (and thus only show tiles)
  • Odd-bonuses obviously had a dormant bug. fixed clearblock().
Bion, j'ai plein de chose à faire dans la maison, donc le tir d'étoiles, assomer les pommes pour les porter "façon koopa shell" et tout ça, ce sera pour plus tard. Je me contente de "petits pas". Voici les quelques dernières choses sur lesquelles je me suis attelé.

dimanche, novembre 15, 2009

Opérée


Fameuse épreuve passée pour notre petite *deline. Elle subissait une intervention à coeur ouvert destinée à corriger sa tétralogie de Fallot aux cliniques St-Luc. Après 4 heures d'angoisse, nous l'avons accompagnée tout au long de sa récupération et nous sommes rentrés le 11. Tout se passe pour le mieux. Son "p'tit coeur tout réparé" lui a valu une mention "très bien" auprès des docteurs. Maintenant, il lui faut se réhabituer à notre rythme de vie ...

Merci à vous tous qui nous avez soutenus le long de ce chemin.

Il nous reste à veiller encore un peu plus sur sa santé, puisque pour les 6 semaines à venir, elle sera particulièrement démunies contre les maladies de l'hiver, et une infection risquerait d'entrainer des complications.

I'm glad to announce you that the heart surgery planned on *deline happened last week, and it happened very well. She's now back at home with her (exhausted) parents to slowly resume the "normal" live (you know, the one where you sleep during the night ;)

mercredi, octobre 21, 2009

You're gonna die! Dieee() Hahahaaa }:-D

First step into a more complete development environment on the DS: a small die() function that will play the role of a top-level exception catching for both C++ and hardware exceptions. You'll learn more about it in future posts. Of course, it relies on a modification of arm9/gurumeditation.c in libnds:



static void defaultHandler() {
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);
vramSetBankC(VRAM_C_SUB_BG);

SUB_BG0_CR = BG_MAP_BASE(31);

BG_PALETTE_SUB[0] = RGB15(31,0,0);
BG_PALETTE_SUB[255] = RGB15(31,31,31);

consoleInitDefault(...);

iprintf("\x1b[5CGuru Meditation Error!\n");

// ... here comes registers extraction, and stuff.

die("guru",0);
}

rhaatchet & clank

Jusqu'ici, mon Cyborg de frère m'avait surtout fait tester du "concept game" téléchargé sur PSN. Le week-end dernier, entre deux dessins pour mes p'tits n'veux, je n'ai pas pu résister à la tentation d'essayer moi-même le Ratchet & Clank que Ciji avait mis dans sa PS3 pour eux... Et je m'étonne qu'il y ait tant de lacunes à ce jeu.

mardi, octobre 20, 2009

Sprites à Priorité Dynamique

Voilà typiquement le genre d'environnement qui était pénible à construire avec l'ancienne version de LEDS et qui devient simplissime avec le curseur de copie et autres nouveautés.
Mais c'est aussi le genre d'environnement qui pose problème au moteur de jeu dans sa version ".999" parce que j'utilise les deux plans de tiles comme arrière-plan alors qu'à d'autres endroits dans le jeu (p.ex. quand Bilou est dans un arbre), il se situe en fait _entre_ les deux plans.

Bushes like these were typically the kind of background that was a real nightmare to build with the previous release of LEDS. And with the "copy cursor" and "pull to foreground" modes of the new prototype level editor, it's really easy and funny. On the other hand, it was also the kind of background that challenged the game engine. It is acutally built by "flattening" the two layers in such a way that i never need more than two tiles at a place, and yet simulate much more planes. Unfortunately, as i wish some elements to hide Bilou (such as walls and trees in secret places), Bilou actually stands "between" the two layers i'm using here, while in this specific case, he should be on top.

Le résultat, c'est que souvent, les pieds de Bilou étaient masqués, comme sur l'image ci-contre, soit parce que j'avais oublié de repasser l'herbe en arrière plan, soit parce qu'il y avait déjà un autre élément de décor par-derrière l'herbe.
Quand j'avais lu les spécifications techniques de la console Genesis, j'avais été plutôt étonné de voir qu'il n'y avait que deux plans de tiles. Or, Sonic est parfois devant le décor et parfois derrière (sans compter l'image de fond, bien sûr, qui occupe le 2eme plan). Le truc, c'est que contrairement aux console de Nintendo, la Genesis permettait de définir pour chaque tile si les sprites avait priorité ou pas pour l'affichage.

As a result, it is common to see Bilou's feet hidden by the grass in the latest demo, either because i forgot to move the grass to the bottom layer, or because it has to be on the front layer due to some other object (e.g. vines or bushes) in the background. I'm trying to address this by reproducing in software the technique used in the SEGA Genesis, as illustrated in Sonic II.

Je vais donc tenter de reproduire cette approche en software: puisque je dois tester les "flags" de chaque tile lors du déplacement de Bilou, je peux assez aisément en ajouter un qui force Bilou (et les autres sprites) à "passer par-devant le décor" quand ils sont au moins partiellement en contact avec ce tile-là. Ca risque bien de compliquer un rien l'édition de niveaux, mais ça devrait valoir la peine...


Despite the Genesis had only two "scroll layers" (against 4 for the DS), it dynamically evaluated tile-to-sprite priority: while all the tiles involved in the "tunnel" on the picture above were on scroll layer A, Sonic can be hidden by the "front pillars" and still seen in front of the dark checker tiles. I'm unsure whether this implies that rings in Sonic were sprites, though. I cannot change the DS hardware, but i can mimmic this technique by having some of the tile attributes (F_RAISER) that forces any sprite that hits it to appear above all the "scroll layers" rather than between "F scroll" and "B scroll" for this specific frame. It's still to be tested.

mardi, octobre 13, 2009

Ziggy, il s'appelle Ziggy ...

Unfortunately, I'm not the author of these graphics (2Dhero is), neither am I working on a nintendo DS port of this indie game running on Game Maker for Windows. So why does it appear here ? because I'm studying it as much as I can: character design, graphic style, palette choice, composition. This is truly a platformer gem that we are offered here, reminiscence of Superfrog (with so much better gfx!)

Je ne peux pas m'empêcher de comparer Ziggy à Superfrog. C'est un excellent exemple de "jeu d'auteur" où la passion (et le talent, je dois l'admettre) nous apportent un jeu d'une qualité rare en ces temps de crise. Vous l'aurez compris: il ne s'agit pas de graphismes à moi, ni même d'un homebrew pour DS (dommage, d'ailleurs). 2Dhero nous a concocté son jeu "en indépendant", avec le Game Maker pour Windows, comme beaucoup d'artistes du pixel le font ces derniers temps.

De mon côté, j'étudie tout ça avec beaucoup d'intérêt ... palette de couleur, composition de la scène, style graphique, etc. J'imprime en gros plan, je colle des petits extraits dans mon agenda, etc. Je ne "relookerai" probablement pas complètement Bilou pour suivre le "style ziggy", mais comme je coince sur les lianes et les fleurs ces temps-ci ...

A noter au niveau gameplay une "attaque aérienne" qui permet de sauter horizontalement en plus du saut ordinaire, ce qui ouvre presque plus de possibilité que la "glissade", plus classique.

Oh, et pour ne rien gâcher, il tourne dans wine...

dimanche, octobre 04, 2009

L'île des colons -- that was Bilou RPG

Je m'offre un petit moment de nostalgie... En cherchant "Bilou et son astro-flasheur à perforation en vrille" pour mon frère, j'ai repris une série de captures d'écran d'un de mes plus gros projets en QuickBasic: Bilou's Quest. Un Zelda-like dont j'ai retrouvé une version sur diskette en fouillant chez mes parents il y a quelques semaines de ça. Le montage n'est pas génial: à l'époque de la sauvegarde, j'étais alors occupé à remplacer le joystick par la souris, ce qui fait que la version récupérée a besoin de la souris pour diriger Bilou et d'un gravis 4 boutons pour parler, soulever, etc. Bref, j'ai triché en démarrant les fichiers .BAS directement.

I'm taking you back 15 years ago, with a couple of screenshots i've taken this morning. Here comes the island that Bilou explores in Bilou's Quest -- one of my largest QuickBasic games. I recovered a backup on a floppy a couple of monthes ago and finally managed to make it run again ... well, sort of.
I agree there isn't much to be proud about anymore: that's just a nostalgic memory, and the first occurence of Flower Power and Badman in pixels.

Du coup, je goupille comme je peux, je remplace les écrans auxquels je n'ai pas su accéder par des captures de l'intérieur des maisons et des grottes, etc.

Bon, je vous le concède, en dehors de l'aspect "nostalgie", il n'y a pas grand-chose à en retirer. La maniabilité était innomable, les graphismes de Mystic Quest sous GameBoy sont bien plus réussis, le scénario manque cruellement d'inspiration, etc. Rien qui ne justifie que je cherche à en tirer une adaptation DS, quoi.

Mais en son temps, c'était chouette de bosser dessus ;)

Et puis, ne lui retirons pas sa valeur essentielle: c'est un projet qui a été transformé en un jeu. Compte tenu de la quantité impressionnante d'idées de jeu que "la farde PPP" contient, Bilou's Quest a tout de même le grade de "réalisation", même si ça ne couvre que 10% du scénario prévu (à la fin du niveau 1, le joueur n'a pas encore d'épée et ne peut se défendre qu'en lançant des pommes). Merci à mon frère Piet de me le rappeler.

Bilou's Quest (extra screenshots)

 Screen title used software-rendered fonts for better readability, and featured small cutscene with a "soldier bubble".


The "quest status" screen was equally black-background, and drew inspiration from "Zelda: Link's Awakening". You could pick two objects and assign them to the 2 buttons of the mouse ... but at this stage in the quest, you have no object yet. Your pal Bouli is shown in jail just above the load/save and the map icons.

mardi, septembre 29, 2009

IRQ_VCOUNT

Voilà exactement le genre de technique de programmation que j'ai toujours admiré sur les consoles (et les ordinateurs Commodore, qui d'une certaine façon, sont des consoles avec un clavier et un lecteur de disquette). Mettons que je veuille modifier au milieu d'une image les paramètres de l'affichage, comme la palette de couleur, la priorité et la position de tel ou tel plan du jeu, etc.

Pour un jeu vidéo, ça me permettrait par exemple de garder une zone de score fixe dans un jeu à scrolling, ou (*ze* cas d'école) de virer la palette dans les bleus à la hauteur du plan d'eau. Dans mon éditeur de niveau, ça permettrait aux boutons de rester affichés correctement malgré les choix d'afficher ou de masquer les différents "calques".

Bien sûr, ça peut être approché aussi sur les cartes VGA etc. à condition de bien synchroniser le timer avec les débuts et fin de ligne à l'écran. Et c'est là toute l'élégance de la "solution console": il y a une ligne d'interruption dédiée directement à cette synchronisation (donc plus besoin de timers), et dans le cas de la DS, on a carrément le luxe d'un registre supplémentaire qui indique si la ligne courante correspond ou non à une ligne indiquée.

void vcountirq() {

if (REG_VCOUNT<192) {
BG_PALETTE[0]=RGB15(0,0,31);
REG_DISPSTAT = (REG_DISPSTAT&0xff)|(192<<8);
} else {
BG_PALETTE[0]=RGB15(0,0,0);
REG_DISPSTAT = (REG_DISPSTAT&0xff)|(160<<8);
}
The 'vertical count matched' interrupt is a perfect explanation of why I prefer underground console programming against overworld PC games. It is a very simple mechanism that lets code be run when the graphic controller is about to process line Y, and yet it enables a wide panel of special effects in games. The most common "showcase" is for sure implementing water: at a given horizontal position, you swap the palette and provide a "bluer" one so that bottom of the screen looks sunken.
The above code snippet does just that in a "raw" fashion where only backdrop color is changed. Don't let yourself scared by those bit masking, combining and shifting. It is just another facet of computing that is *very* handy when dealing with hardware registers. If you think of your process as an "electronic process" taking place on an array of bits, it just becomes natural.

Une petite fonction toute simple donc, qui sera appelée "sur la bonne ligne" et qui change les couleurs (ici, une seule, et entre deux valeurs pré-calculées, mais c'est juste pour la démo). Astuce, pour pouvoir être invoquée sur plusieurs lignes (plutôt que toujours sur la même) et donc remettre la couleur à sa valeur précédente, je change le numéro de la ligne où l'interruption doit se produire pour lui donner la valeur suivante dans l'interruption elle même (c'est le code avec REG_DISPSTAT).

Bon, okay, le code avec les & et les << fait un peu peur: c'est des manipulations de portions d'entiers pour modifier un byte qui se trouve en réalité dans la moitié d'un registre de 16 bits. La programmation DS est remplie de bidouille de ce genre. C'est de l'informatique d'avant XML, qui exploite pleinement la structure binaire des nombres.

Bien sûr, pour que ça fonctionne, je dois enregistrer la fonction vcountirq via la libnds (il ne suffit pas de lui donner le bon nom pour que ça se fasse tout seul. Vous rêvez, les gars!)
irqSet(IRQ_VCOUNT, vcountirq);

REG_DISPSTAT = REG_DISPSTAT|(160<<8);
irqEnable(IRQ_VCOUNT);
Vous remarquerez qu'on retrouve ici aussi les "manipulations bizarres" sur DISPSTAT, pour l'initialisation. Heureusement, le web est plein de gens qui passent leur temps à expliquer "X&FF = garder les 8 bits de poids faibles de X", etc.

Et pour désactiver l'effet,
irqDisable(IRQ_VCOUNT);
tout simplement.
J'aurais pu vous bricoler tout ça juste pour le fun ou parce que je prépare en secret un nouveau niveau de Bilou avec de l'eau. Malheureusement, je n'en suis pas encore là. Je suis dans une impasse avec mon éditeur de niveau, tout simplement: les boutons qui me permettent de décider si je veux voir ou non le fond, le plan principal, les sprites, etc. disparaissent ou deviennent illisibles par suite des manipulations pour changer la visibilité des plans. Résultat, rien de tel qu'un bon "virq" pour forcer l'affichage à "revenir" dans le bon état le temps de m'afficher la barre d'outils qui doit se trouver en bas de l'écran tactile...

Well, i wish i was posting this message because i'm working on an impressive water level. Not yet. I'm simply stuck with the UI of my level editor, where i need more layers than the console actually provide, so I'm doing a dynamic "horizontal split" when the user press "down" to access layer visibility setup. The hardest part is to integrate this properly in the GuiEngine (as usual with OOP >_<)

... Reste à intégrer ça dans le GuiEngine ...

edit: i managed to integrate and test on real hardware. It works, despite the amount of time taken by the operations confirms just "dma'ing" a new palette wouldn't work. The actual code for my 'vcount' handler is only 8 lines long (50 instructions, involving 8 stores to video registers), but it takes two scanlines to be fully processed. Plus, the first scanline shows a "flickering" area of some 24 pixels wide, suggesting that even the "companion code" that brings us from the interrupt to the "virtual void online" snippet is also too long. Maybe pushing it to ITCM would help ... and having the interrupt taking place one line in advance and forcing a busy loop until we encounter a HBLANK.

update: Pour la petite info, c'est maintenant intégré et testé sur la DS elle-même. Et ça marche, même si je suis surpris de voir 8 modifications de registres (les données sont constantes: ça se voit dans l'assembleur généré par le compilo) prendre quand même deux lignes d'affichage pour s'exécuter ! Je devrais peut-être essayer de mettre les routines concernées en ITCM, histoire de m'assurer qu'elles soient toujours en cache, ou qqch comme ça (sur du C++, ça va être coton, tiens!).