Monday, May 28, 2012

sponge-hands-and-ink-and-mockup

Here I stand, guilty of doing low-level debugging where the situation wasn't calling it. Now that I have a "test level" for the school zone, it's very tempting to start writing more monster code. I thought I could easily hack a Sponge Bop test while *deline was asleep this afternoon, but for some reason, only the first frame of the animation did show up. The rest was just rubbish: Bilou feets and similar stuff.

Was my data corrupt ? Was there some nasty bug related to 32x32 sprites in the revised game engine (there shouldn't have been?) I scanned virtually everything, but the data I was (painfully) reading in DDD made little sense.

I finally decided to hack some debugging output in my .spr to .png converter to see what "pages" were containing, and I turned suspicious over the following entry:

4x4=1020,1036,1052,1068,1116,1132,1148,1164,1180,1196

1036 ? 1052 ? that's a bit surprising for a sprite number ... when I looked at the shape of the spritesheet, the obvious (finally) hit me: the DS can only enumerate 1024 tiles for sprites. No more, no less. Everything on the fifth column of the spritesheet can be nicely viewed in the SpriteEditor, but cannot be used in games. It's not even the first time I encounter the problem >_<. I think I'll need something in the Sprite Editor that warns the user about this when saving a tileset. And I'll have to figure out how I solved the problem last time...

Oh, and it's been over one year that I'm longing for a "draft" tileset and a "animation" tileset in .spr files ... I think it's now time to add this to Sprite Editor ...

developments:

  • [done] enable up to 4 SpriteRam;
  • [wish] show how much tiles are used in each SpriteRam (other than pressing L+START to enable debugging, I mean), and which spritepage should be moved to compress the tileset.
  • [done] shrink spritea.spr so that it can be fully used by the engine;
  • [next] enable 16x16 <-> 32x32 sheet conversion;
  • [next] find a way to use the ink animation in the level;
  • [done] produce a warning when saving a file where sprites or blocks set has too much tiles;
  • [done] allow hint on which tile is used when producing tilesets with spr2png.pl

regression tests after the edit:
  • [ok] change the spriteram used for a page
  • [ok] save and load file with more than 2 spriterams
  • [---] use the "scan colors" and "color remap function" (actually, nevermind: it operates on the working sheet, not on the SpriteRam itself).
  • [ok] use the "compact tileset" feature (doesn't it just reduce the 'watermark' when last tiles are no longer used ? couldn't that be automated with load/save ?)
  • [fair] use the "color zap" (dirty bit and warning message "file your sheet first" ?)
  • [fixed] copying page only works for "blocks" pages.
  • [fixed] cursor-assisted deletion of objects doesn't behave as expected with 32x32 sheets.
I'm a bit surprised to see the "SpriteRam*" pointer so scattered all over the code... I might have to change them into "spriteram_no" here and there so that only some core component deal with sprdata[i] directly.

Palette animations and 4096 colours

I've seen the use of multiple palettes on DS first in the Tetris Attack source code.
The game used to use multiple palette to handle "shading" of the game when you're K.O. It involves using one of the VRAM slot (usually VRAM_E) as a extra-palette place, but you need to map it back as LCD to enable modifications ... so it's not convenient if you're planning to do palette animations.

it's completely OK to switch the VRAM bank you're using for ext palette to LCD mode -when in vblanking- to access it, the DS doesn't care when it's not drawing the screen. Just remember to set it back to ext palette mode as soon as you updated its contents. Using multiple palettes, for instance for sprites, could be useful if you want to have enemies with different colors but just store their frames in memory once (/Sverx :)


That's good to know. I won't have to drop palette animation altogether if I start using multiple palettes for "painting" books in the school. If I'd be checking gbadev more often, I'd know for quite a while.

Wednesday, May 23, 2012

LevelModel | MonstersManager

wow. Loading a level with monsters in LEDS is far from being a simple story, right now. LevelModel is the internal representation of your level content, and it can extract the "gob% = state% (%,%)" statements, but it has no idea how to render GOBs. That's the job of the MonstersManager ... and to some extent, it makes sense.

Once this is laid out, it comes more naturally that LevelModel should also be responsible of storing gob% := (<expression>) statements in Monster instances it creates, so that MonstersManager no longer has to worry about that... And I'm glad I took the time to map the situation, because my previous (jump-to-code) "design" would have been a highway to hell.

Tuesday, May 22, 2012

Dumblador turns back

Here it comes. Some final tweaks on dumblador's "walk to the right" animation last night and I now have a CompoundGob that turns back when it encounters a wall (how sweet ^_^) Dumblador avance et fait demi-tour. L'occasion de revenir sur le fonctionnement des personnages dans mon moteur de jeu. Accrochez-vous un peu: le 'gobscript' est conçu plus comme un modèle de bytecode en ASCII que comme un véritable langage de programmation, mais comme dumblador est tout simple, ça reste lisible (j'espère ^^") Please bear some GobScript since it's still a pretty simple and straightforward monster:

anim1 = spr:0
anim2 = spr:3
statL0 :anim1 {
using walker
selfmove
test 0 (2,4)-(8,14) 0001
area 0 (0,0)-(8,4) 000A
}
on commence par importer des animations depuis le jeu de sprites (il suffit de connaître leur numéro, ici n°0 pour avancer vers la gauche et n°3 pour avancer vers la droite) et on leur donne des identifiants locaux (anim1 et anim2, respectivement). Le comportement de dumblador va maintenant être construit comme une combinaison d'états réutilisant ces animations: L0 pour avancer vers la gauche et R1 pour avancer vers la droite.
statR1 :anim2 {
using walker
selfmove
test 0 (2,4)-(8,14) 0001
area 0 (0,0)-(8,4) 000A
}

statL0->statR1 on fail (100 :0)
statR1->statL0 on fail (100 ~ :0)
statL0->statR1 on hit0 (100 :0)
statR1->statL0 on hit0 (100 ~ :0)
end
The only thing you have to remember about your spritesheet is now the position in the animation slots where your things are. That's slots #0 (walk left) and #3 (walk right) for me. You give them animation identifiers for your monster (1 and 2, resp.) From that, you can define states walk left and walk right. Each state defines an animation being played and a behaviour. The combination of using walker and selfmove means that DumBlador will move forward at constant speed, according to what's defined in the animation, but not faster than the value defined in its internal variable v0 (x speed)
Vous pouvez penser à un "état" comme à une action que fait un personnage: s'il est dans l'état "avancer", il fait son animation "un pas en avant" et utilise un morceau de code C++ un (contrôleur) identifié via using walker qui reprend les tests "y-a-t'il du sol? dois-je suivre une pente?, etc.". Le mot-clé "selfmove" indique que c'est l'animation qui définit de combien de pixel on avance à la fois pour une meilleur synchronisation. On reste donc par exemple dans l'état "vers la gauche" tant que tout va bien. Le contrôleur génère un "fail" lorsque le personnage arrive dans un mur. À ce moment-là il va falloir choisir un nouvel état. Les commandes du types statdepuis -> statvers on fail [condition] (action) permettent de construire les transitions qui seront testées dans ce cas-là. Ici, l'action consiste simplement à écrire une valeur constante (100 ou -100) comme nouvelle vitesse horizontale (variable n° 0). "walker" is what's called a controller for the GOB. It can also report events and indicate when it failed to perform the desired movement. The two on fail statements indicate transitions from "walk left" to "walk right" and back when it's no longer possible to move forward. That could be due to a wall or the end of a platform. With this simple script, dumblador "has no way to figure out". Another part of the current behaviour is that dumblador turns back when Bilou jumps on his "head". This is achieved by the two on hit statements. It's still necessary to provide the (relative) coordinates of the hitboxes manually. Here the "hit" statement corresponds to a passive area in the state definition, which is seen in dark cyan in the Inspector widget. "000A" is the bit mask that corresponds to Bilou's stomping action or AppleAssault's attacks. I could have used "0002" to make DumBlador impervious to punch attacks and reacts only to stomps. Enfin, pour compléter le comportement décrit par chaque état, les instructions test|area (coin1)-(coin2) avecqui donnent les zones de collisions (rectangulaires) qui vont permettrent d'interagir avec les autres personnages. La partie un peu "magique" se situe dans le nombre hexa avecqui. Pour chaque "type" de collision, il faudra choisir un des 16 bits disponibles et faire en sorte qu'au moins deux zones de collisions (p.ex. le corps de blador et celui de Bilou) utilisent le même bit (0001) mais dans un test pour l'un et dans un area pour l'autre, pour refléter l'asymétrie frappeur-frappé du moteur de collisions. Finally, the curious (100 ~ :0) are GobExpressions. It's a minimalist encoding of "set var[0] to -100". It works as those RPN calculators, where you type "2 40 +" to compute "40 + 2". Only simple parts are used here: push the constant 100, negate it (I don't have negative constants so far ^^") and assign it to variable 0 (mnemonic: Pascal uses := for assignments). Reusing a constant here is required as the walker controller is allowed to alter the speed in the final steps towards the wall. Would you like to give it a try yourself ?

Sunday, May 20, 2012

FLITS again

Okay, let's keep tools-fusion for later on and focus on the impending walk of Dumbladors. I had them stepping blindly yesterday, and now, I'm trying to use the "walker" controllers (the one that can follow slopes and so on) to control them, so that they can be placed like Applemen rather than suffering woodworm-like bugs.

I had some more "FLITS" bugs, with dumbladors disappearing into the void of the toroïdal space (?) as soon as they hit a wall 0_o

The good news is that InspectorWidget was already much more capable than I thought, despite its cryptic user interface:

  1. (1) touch here to set a "breakpoint on bounding-box intersection
  2. (2) once a monster appears, click its "name" (2 first letters of BLador.cmd plus current state number) to give it the inspector focus.
  3. (3) click the coordinates of one monster to "disable" it from execution (or resume its execution).
  4. (4) click the focused gob's "name" bar to switch between iGobControllers display or cdata (GOB's private variable used in GobScript) display.
  5. (5) touch "p*" or "a*" to enable the rendering of collision areas.
  6. press L to step to the next frame (slow-motion) or L+START to resume normal play (with breakpoints active).

After fixing the "silly sunday bug" (giving a positive v0 speed for a state that moves to the left :P), I think I identified the reason why blador disappears: the "deficit" for step-based walk increases by v0 at every frame, but I also have "delay xxx" instructions in my animation, which makes the GOB to accumulate deficit move over time. When it comes to turn around, it is first "wrapped" to the place it's supposed to be (not very ideal)... and that happened to be off-screen this time ^^"

Thursday, May 17, 2012

Fuuuuuusiooooooon


The more I use AnimEDS for the Bilou project, the more it becomes obvious: I will have to merge SEDS and AnimEDS into a single program, so that one can easily touch up or re-center sprites used in animations, or easily preview the new sprites on the defined animation. Easing the switching between the two tools will not do it. Integration is really what's needed.

This post will collect thoughts and battle plan for this huge refactoring attempt ... I won't get into it before I have a first school zone demo running.

SEDSAnimEDS
* drop the dumb 'animation editor'
* leftTable could be dropped between two "restore()" of the fileWindow
* make onion skin possible, but keep OAMs for the rightTable
* solve the memory management bug.


Jusqu'ici, avoir AnimEDS et Sprite Editor dans 2 exécutables séparés était plutôt bénéfique. Je pouvais complètement foirer une "release" AnimEDS sans pour autant perdre la possibilité d'éditer des p'tits sprites sur ma DS (ou pire, de corrompre les fichiers .spr). Mais à l'usage, il est clair que devoir basculer d'un programme à l'autre est fréquent pour avoir une animation correcte. Rien que sur Dumblador (pourtant élémentarissime), j'ai du faire des va-et-vients pour re-centrer les pieds, histoire que l'animation passe sans heurts. Alors commencer une animation avec des boules en guise de pieds et mains puis "affiner" en redessinant quelque-chose de plus rayman-esque ... je n'ose même pas imaginer 0_o

Je vais donc devoir fusionner les deux ensembles de "fenêtres" dans un seul et même programme, ce qui va nécessiter un fameux effort de mise à plat du code (UML? ô UML, pourquoi es-tu UML?) histoire de garder un état cohérent entre les "feuilles de travail" pour les sprites.

Je pense que ce sera pour les grandes vacances, dès que j'ai une démo du niveau "school zone" qui est partie sur dev-fr.org.

(PS: ce post est en "brouillon" sur le blog depuis septembre 2011 ...)

Yeah! Blading time!

Wow. I had no idea it would go so fast! Less than 24 hours after the proper alignment of dumblador on screen, I can now have it walking around in a level. Many things remain, such as making it detect edges and turn around, just like applemen and woodworms. That will be a next level. Here's the updated AnimEDS for download, although I'm pretty sure it needs more feedback hints before it becomes user-friendly.

Eh bin !? ça n'aura pas trainé. Je sentais bien que je n'étais pas loin de "dumblador qui avance", mais de là à deviner qu'en une soirée, ce serait réglé dans AnimEDS et qu'avec juste 2 heures de plus dessus ce matin (dont un peu d'ajustement de sprites), je pourrai le faire avancer aussi dans le moteur de jeu ... C'est pourtant le cas. Chouette. Je vais pouvoir commencer à ajouter la définition des zones de collisions entre sprites ... ou alors je garde ça pour un programme annexe encore un moment ?

Wednesday, May 16, 2012

Where You See is Where It Is

Bon, on récapète.

Là, j'ai AnimEDS, LEDS et le moteur de jeu qui sont d'accord sur la manière d'interpréter la position d'un personnage composite dans un niveau. Ça n'aura pas été sans mal.

Well, it's a milestone: I can precisely place compound GOBs in a level. Neat. Now, if you don't mind, I won't translate all the tracking and focus on the impeding future: defining moves of the compound GOB in AnimEDS as well ... and then have them walking :)

A few weird things occured with this dumblador development, which should sound "normal" to any game developer: a monster that cannot be seen but hurts you nonetheless, or that can be seen at position A while it hurts and can be bop'ed on at position B ... and also a monster that "walks" without leaving position A and suddenly appears at position C when his "step" movement is done (distance between A and C being totally impossible to predict from the observation of the movement).

No need to say that in some parallel universe, dumbladors seems to have super-natural powers that make Bilou's job quite more complex. According to Bouli, that'd be because they're following elliptic curves, but on a toroïdal and discrete space...

J'ai même le début de la définition des déplacement, quoi que ce soit encore très "courbes elliptiques en espace discret toroïdal", si vous voyez ce que Bouli veut dire ... non ? bon, bin Dumblador reste sur place pendant presque toute l'animation, puis arrive d'un coup à une position improbable qui n'a pas grand-chose en commun avec le déplacement qui était prévu, donc:

  1. faire en sorte d'avoir en mode émulateur (sur toutes mes machines) une animation de déplacement, histoire d'éviter de re-faire l'animation à chaque test;
  2. éliminer les effets du type "la position de la tête n'est remise à jour que si la tête est déplacée (relativement) dans l'animation.
  3. garantir que l'info du type "verouillé sur le membre #3" est maintenue à jour quand on se déplace le long de la timeline.
  4. trouver le bug.
  5. Faire en sorte que les repères de positions dans LEDS restent corrects quand on se déplace dans le niveau ^^"

Monday, May 14, 2012

Voo have a problem

Okay, there was enough to do over the week-end to support an Internet service downtime, but it was still a pain to see that brand new Voo modem rebooting for 48h, completely out of my control, with only a few 5-minute window where I could commit some code to my svn.

Hopefully enough, one of my systems at home had a pretty freshly updated code base to work with, so I could fix those solid-box-definition issues and bring AnimEDS to a point where I can start toying with embedded movements... at last. Let's see if I can make dumblador actually *move* by the end of the week (and get that modem fixed, btw).

Monday, May 07, 2012

devil in the details

Mouais. J'aimerais dire "#çac'estfait", mais en fait non. J'ai bien la possibilité de définir une première zone de collision dans AnimEDS, la possibilité d'aligner le bord inférieur sur la map dans LEDS et le game engine qui regarde tout ça sans broncher, je garde un désagréable goût de bicarbonate en bouche.
Il m'aura fallu plusieurs aller-retours dans LEDS pour pouvoir faire l'ajustement, les monstres se désactivant à plusieurs reprises. De plus, le minuscule dumblador affiché sur la map ne permet pas de se rendre compte si le positionnement est correct, ni de le sélectionner avec précision. Affaire à suivre.

I'd love to call it done, but let's be honest: it's not. It's merely a hack that allowed me to have *one* dumblador properly aligned after countless "there-and-back" games in the level editor. I'll have to do some polish before it could be of any real use. Much polish. "devil is in the details".
  • [done] now that I can select dumbladors with their real sizes, I can't select Bilou anymore, who looks to have (0,0) dimensions -- fixed.
  • [fix] DPAD-positioning is somehow fixed ...
  • [workaround] but I still need to visualise the solid box of the monsters, not only their thumbnail.
  • [done] monsters are rendered where they have been designed to stand.
  • [done] BoxWidget becomes immediately responding to DPAD commands after the 'bx' button has been clicked.

Tuesday, May 01, 2012

BoxWidget ... beyond the code.

I missed a "svn commit" this morning, so all I can do is some mock-up to think about how the "BoxWidget" will be used to define hitboxes in AnimEDS... It seems like I'd rather not jump into implementation immediately and think a little bit about the options: boxes are defined on a per-animation basis, not a per-frame basis, so maybe they could be edited on the FileWindow rather than on the AnimWindow.


Bon, bin quand on a pas synchronisé son code à la fin du week-end, on peut toujours se faire un p'tit mockup pour voir comment le nouveau widget "définir une zone rectangulaire" va pouvoir aider à la définition des zones de collision. Mock-up pas totalement convaincant, d'ailleurs. Fort confus, même si je ne vois pas bien comment faire mieux. Ça me paraissait une bonne idée, au départ, de juste "suggérer" la zone de collision en marquant ses coins, mais la plupart des problèmes de collisions rencontrés jusqu'ici provenait d'un recouvrement entre zones. Du coup, pouvoir en visualiser plusieurs d'un coup ne serait sans doute pas plus mal...

It could also be interesting to be able to visualize more than one box at a time, similarly to what happens with InspectorWidget: many "collision bugs" were due to improperly overlapping windows. That could require another form of display