vendredi, janvier 23, 2015

Les vieux tout doux

Eh bien, il y en a, des choses marquées "à faire" depuis les années. Plus de 20 pages si je veux les imprimer sur papier. Ce sont principalement des améliorations pour les éditeurs, comme la possibilité de définir les zones de collision et les points-tests dans un éditeur graphique, aujourd'hui (enfin) très actuel.

La plus ancienne des fonctions manquantes du Level Editor -- les propriétés des blocs -- serait plutôt revu en "appliquer la combinaison graphisme/propriété à l'ensemble du niveau", et il faudrait aussi que je permette aux descriptions de blocs d'être partagées entre plusieurs scripts de niveaux.

Enfin, il y aura la liste des projets devenus hors-propos ou qui ne sont plus à l'ordre du jour pour mon projet actuel, rebaptisés "wish", comme l'éditeur 3D de bangbash ou l'intégration de RunME dans la PAlib :P

Mais la palme du PlusVieuxToutDouxDuMonde, c'est sans doute la possibilité de partager des tiles d'un bloc à l'autre (les blocs faisant 2x2 tiles), qui apparaît dès les premières réflexions sur l'animation des blocs en 2005 et marqué "need" en 2008 (traduisez: fonctionnalité jugée indispensable pour l'avancement du projet, qui permettrait de redescendre à 66% du nombre de tiles actuels). La possibilité de poser des quarts de blocs dans LEDS aidant, évidemment... Dans l'immédiat, j'aimerais plutôt pouvoir enregistrer des morceaux de niveau pour capturer des objets complets (qui apparaissait 6 mois plus tard, puis revient à la charge).



Voilà. J'ai fait un peu le ménage dans ce qui datait d'avant 2013 ... je ne suis pas sûr que ça m'ait vraiment fait progresser pour améliorer le niveau des expériences en cours, par contre. A suivre. Comme toujours :P

jeudi, janvier 15, 2015

Pixel Prospector dug "Drop Wizard"

Très chouette découverte grâce à PixelProspector: Neutronized revisite un monument du gameplay: Bubble Bobble. Tableaux fixes, quelques monstres et on passe au tableau suivant quand ils sont tous éliminés. En tombant de l'écran, on réapparait par en haut -- et pareil pour les ennemis. Il faut toujours deux actions pour se débarasser d'un ennemi (comme dans Apple Assault ^_^): une étoile magique et un coup de genou. Dans l'ordre.

I'm glad I found +PixelProspector back. Just in time to discover "Drop Wizard", a really nice game by Neutronized, adapting Bubble Bobble classic gameplay into something definitely more strategic. You still have to dispatch all the cute monsters out of the screen. You still need one long-distance attack _plus_ one close dash to definitely get rid of a monster. You still wrap around the screen vertically. But this time, you cannot jump. You cannot shoot any time, either: you only shoot when you land. If you don't want to end up trapped, you'd better plan your moves and analyze those of the monsters.

Mais "Drop wizard" est beaucoup plus stratégique. On ne peut pas sauter, et le personnage ne lance une étoile que lorsqu'il atterit sur une plate-forme. Il faudra anticiper bien davantage son parcours pour éviter de se retrouver bloqué, pris en sandwich par deux ennemis... un peu comme dans space panic. Mais il y a une échappatoire. Un mécanisme de combo qui n'est pas sans rappeler les carapaces de koopa de Super Mario.

There's one (funny) way out, hopefully. When you dash into a monster, it rolls to the next wall. While rolling, it can clump into others and chain into a combo. Trust me, this one is a true gem.

Le bon tempo

J'essaie de rendre les manipulations de taille-crayons plus dynamiques. Quelque-chose qui soit plus adapté au gameplay du "school rush" ...

Compared to "Anniversary Level", the tempo of the game has quite evolved. Current animation of Bilou grabbing something on the ground with his eyes going "whoo! Look what we've got here" doesn't fit the action anymore. With the ink rising and pushing pressure, having to stop on a blador to GRAB it is a pain. It would be neat if we had the opportunity to grab it on the fly, just after we stunned it. That the best I can think of to "make things snappy".

J'utilise la nouvelle fonction "édition des zones de contact dans AnimEditor", mais ça n'a pas encore l'air très au point ... entre les dumbladors qui s'envolent quand je tente de les ramasser, les animations qui s'interrompent prématurément et les éponges qui glissent des mains de Bilou. Ce serait sans doute une bonne idée de prévoir une amélioration de l'InspectorWidget pour permettre de visualiser ces changements de zones de contact.

Yet, things are not quite working as expected

  • [todo] why is left-grab not playing ?
  • [todo] how could I end up with an editor that damages animation when opening them ? I need unit testing here.
  • [done] bladors should skyrocket when I try to grab them.
  • [todo] why do I have anims with over 10 'control: done' statements ?
  • [todo] make sure InspectorWidget can report areas masking.
  • [think] export over WiFi has become too complicated. Plan a multi-connection approach
Unrelated (except for timing) todo items:
  • [todo] allow meta-information about tiles to sit in a separate file, included by level scripts.
  • [todo] allow spawn point (and possibly spawned GOBs) to be modulated by some global game variable (checkpoints, difficulty settings, etc.)
  • [think] a simple map editor for PC would be better suited to those "surgical edits" required in current development cycle. That or easier-to-upgrade runME with the cycle moving back to DS device.
  • [todo] print and deep-review todo items to establish battle plan for 2015.

samedi, janvier 10, 2015

Keen: ClipToWalls()

Pour éviter que Keen ne passe à travers les murs ou ne reste bloqué en bas d'une pente, les niveaux attribuent à chaque bloc de 16x16 pixels un ensemble de propriétés. C'est la technique des "tiles" bien connue de la plupart des lecteurs. Ce qui est particulier dans le moteur d'ID software, c'est la possibilité de définir séparément l'animation, la pente de sol, le comportement en tant que bloc spécial (valeur du bonus, couleur de la clé, etc.) et la polarité du mur.
Plus fastidieux à définir que le simple remplissage en tant que bloc solide de mon éditeur de niveau, mais heureusement, dans Keen, il y a presque systématiquement correspondance entre graphisme et propriétés, et l'éditeur de niveau peut automatiser tout ce qui n'est pas passages secrets.


Levels in Commander Keen are build out of tiles -- 16x16 pixels squares combining a graphic content and some in-game properties. In ID's engine, the tile number (*map) is used to index a set of arrays that are present in game definition, and read at game startup into tinf. NORTHWALL, in the snippet above, is the offset to the array holding information about how the tile behave as a floor. Is it sloped ? can we move through if we're sliding down a pole ? tinf[NORTHWALL+*map] tells you that. And only that. If you'd like to know whether there's a bonus of some value, you should definitely check another "slice" of the tinf array.

Interestingly, the behaviour routines won't immediately react to wall/floor/ceiling collision. Oh, well, character will immediately be _clipped_ to that wall. Aligned, if you prefer, so that it doesn't enter a rigid structure. But the alignments that were performed are saved into four boolean variables: hitnorth (floors), hitsouth (ceilings), hiteast and hitwest (walls). Behaviour routines like KeenAirReact use this information to adapt the current state (stop falling when reaching the ground, for instance).


La notion de "polarité des murs" est intéressante. Lorsque le personnage se retrouve partiellement dans un mur, c'est cette polarité qui décide si Keen est repoussé vers la gauche ou vers la droite. On aura donc jamais de situation comparable au "zipping" des jeux NES où Megaman se met à traverser le niveau à vitesse super-sonique lorsqu'il rentre dans un mur simplement parce que les programmeurs se sont contenté de "si on est dans un bloc solide, on repousse Megaman sur le tile suivant". Evidemment, ça suppose que les murs font toujours au moins deux tiles de large.
A Zipping technique in Megaman. Propelled at 1 tile/frame or so.

Another interesting thing is that walls have polarity. They're either left wall or right walls. And they're not plain ground either: you could fall through a wall if there were no floor on its top to prevent you from entering there. And it's actually more resilient against zipping through walls. Unlike Megaman, Keen wouldn't be propelled to the (arbitrary coder decision) right if he ends up into the wall from the left while heading left. A wall on Tuberia knows whether it is an east wall, no matter the direction, speed and age of the Commander. And if it's an East wall, it will push you to the East, back where you're supposed to belong to. That makes ID software's collision handling the perfect opposite to Epic Megagames' cando() function.

C'est intéressant de voir que ID software a choisi ici une technique radicalement opposée à celle d'Epic Megagames. Pour Jill of the Jungle et Xargon, Epic faisait en sorte que le personnage n'entre jamais dans des tiles qu'il ne peut pas traverser (grâce à la fonction "cando"). Pour Keen, think() peut potentiellement faire rentrer un personnage dans un mur, mais le déplacement via ClipToWalls() le repoussera automatiquement dans la bonne direction, sans risquer de se faire piéger par un demi-tour de dernière minute comme dans Mario Bros.

mercredi, décembre 31, 2014

Ciao 2K14.

Pour ce qui est du blog, 2014 fut certainement une année un peu maigre. Inévitable, j'imagine, suite à mon changement de boulot, et surtout la contraction du temps de midi. C'est un boulot qui me plaît, avec des registres, des modules kernel et des transferts DMA, ce qui veut dire aussi que je suis moins en manque de code à la fin de la journée de travail.

I hope you haven't been deceived by the lowered posting rate in this year. I changed to a job with more registers, more kernel drivers and DMA transfers. The environment gives less room for lunchtime-hobby-coding, but I enjoy it a lot. Hopefully, the list of things to adapt to have daily tasks flowing in this new chapter of life reaches completion.

Du point de vue de l'aventure de Bilou, toute l'année aura tourné autour de la transformation -- sur la proposition de Pierrick -- d'un niveau d'essai en jeu d'arcade du type "cours vite!" avec l'encre qui monte. J'ai eu le feedback de Kirby Kid qui a attiré mon attention sur l'importance d'un règlage plus fin des paramètres pour ajuster les monstres et les niveaux.
J'atteins aussi des interactions nettement plus sophistiquées (enfin ;) notamment avec les dumbladors empilables sur lesquels on peut grimper. 

Bilou's anniversary level has been converted into an "arcade" game currently featuring 3 levels, with a target, a challenge and a "game over". I got precious feedback from gameplay expert Richard "Kirby Kid" Terrel. I'm doing my best to proceed with tuning issues he pointed out. I'd like to have a 4th level before I come with a neoflash release (if neoflash ever resume doing coding competitions). Rushing pendats, stacking bladors, stunned inkjets, raising ink swamp, floating spongebops ... Nothing like "deep ink pit" has appeared, but things are definitely shaping towards something that better fits the levels I designed for the School Zone.

Il n'y aura pas eu beaucoup de progrès du côté des éditeurs -- la panne de mon cybook au début de l'été n'y est certainement pas étrangère. LEDS s'en tire un peu mieux avec un mode de prévisualisation sympa pour les blocs spéciaux mais qui, en pratique, est peu utilisé (les descripteurs de blocs ne sont pas présent dans tous les niveaux :P). Le système de "copier/coller" d'écrans entier s'est avéré bien utile pour les nouveaux niveaux de SchoolRush. Par contre, le mécanisme prévu pour éliminer les objets hors-cadre lors d'un redimensionnement de niveau n'est arrivé nulle part.

This time, there hasn't been much evolution to my edition tools, I'm afraid. They barely adapted to the new needs identified for the game conversion. I reached a data size where WiFi transfer are impractical and I use the emulator much more than earlier in the project. RunME isn't really following up all those game engine/character behaviour evolution. I'm truly focusing on producing the next game, although it's not going fast.

Oh, granted, I may have played more others' game, too. With two Zelda games to discover, a good deal of Fez and some backlog of Nintendo 3DS games. Washing up sessions tend to be longer since I discovered AfterBit and Speed Game vlogs... And I hope another 3DS game will be tested soon, although it's an old friend.



Avec un nouveau Zelda, la découverte de WindWaker, les documents d'origine de Rick Dangerous et les sources de Commander Keen, j'ai été gâté. J'espère pouvoir faire bientôt le point sur le cadeau inattendu reçu à Noël.

dimanche, décembre 28, 2014

Keen ClipToSprite()

This is the code snippet for the ClipToSprite() action that results in pushing monster/keen interaction when the monster stands on the left. The code adapts characters expected motion (which differs from its computed speed and is transient) so that intersections between characters hitboxes remains empty.

The condition highlighted with yellow puzzled me at first. In fact, we'll note that leftinto == xmove (the difference between characters speeds) when they were exactly side by side at the previous tick. Would there be 1 pixel distance between them at the previous tick, we'd end up with leftinto == xmove -1. So what it really implies is that we won't get pushed at high speed if we fall down just on the left of a carrot running to the right with a large leftinto value (e.g. if we fall on carrot's head and move down through it).

Une seule fonction -- ClipToSprite -- permet à Keen de s'aligner sur d'autres objets en définissant un nouveau mouvement qui s'exécute en plus du mouvement prévu par la fonction think(). Le principe de base est de calculer de combien de pixels les deux personnage s'interpénètre et de repousser celui qui n'est pas solide par un déplacement opposé.

Il est important pour comprendre ce code de se souvenir que le moteur de jeu travaille en plusieurs passes et que tous les personnages ont déjà leur nouvelles coordonnées quand les tests de collision ont lieu. Les valeurs originales de xmove et ymove ont donc déjà été appliquées. Par la "condition jaune" (leftinto <= delta_xmove), on s'assure que Keen ne sera repoussé par le bord d'un objet que s'il n'avait pas déjà dépassé ce bord à l'étape précédente (mais sans que la collision n'ait lieu parce qu'il était au-dessus du chariot, par exemple).

The ID keen engine is multi-pass. That means when ClipToSprite() is invoked as result of keen/courier contact, setting keen.ymove=0 doesn't prevent keen from falling, because he already moved down in the previous step. Instead, it prevents him from moving down again as result of the carrot collision. This design avoids issues with moving platforms without requiring a re-ordering of the objects evaluation.
This is also made possible because objects have a separate react() function to align against level structures.


Pour les plate-formes, des tests verticaux font suite aux tests horizontaux également présents dans les "monstres pousseurs" comme la carotte véloce. S'il est surprenant que le déplacement horizontal de Keen soit exactement celui de sa plate-forme (et pas Keen.xspeed + Cart.xmove comme le voudrait la physique), c'est qu'un premier mouvement a déjà eu lieu. Il ne reste plus qu'à appliquer le mouvement de la plate-forme à Keen, le repousser vers le haut et surtout s'assurer qu'au moment de l'appel à KeenAirReact, la logique du jeu pense qu'il y avait un bloc solide sous Keen. C'est le rôle de la variable hitnorth qui conserve le type de tile qui a interrompu le personnage lors de la phase de déplacement.

Characters that push you but you can't ride ends after those horizontal tests. For carts, platforms and Keen4e's bouncing red ball, you'll also process the "bottominto" test. Note that since we already moved Keen according to the direction keys, setting keen.xmove=cart.xmove and invoking ClipToWalls() again -- which includes applying (xmove,ymove) to (x,y) coordinates of Keen -- combines cart's move with Keen's own move as expected. Hacking the hitnorth member will make KeenAirReact() function believe that some solid ground was found during move/clip step.

mardi, décembre 23, 2014

Kirby 3D

Du sympa et *beaucoup* de récup dans ce nouvel opus de la chiquelette rose la plus populaire du monde Nintendo... Variante simple mais efficace des power-up centimes de vie à récolter: la couleur indique la valeur (comme les rubis d'Hyrule) et de mignonnes petites fleurs dévoileront un bonus quand on passe devant.... Allez, bonne nuit :)

I completed tonight the Kirby 3DS game I borrowed to my brother a couple of month ago. Quite cute, as one could expect. The depth exploring system doesn't add much to typical 2D gameplay (not more than offering 2 screens, at least) but it doesn't ruin the experience either. They put effort in the boosted vacuum mechanic, that's quite obvious. Using the colour of 'coins' to indicate they have higher value worked better than glueing 10 coins together. Taking note ...

Par contre, avec ses interactions avant-plan/arrière plan, son gameplay 2D dans un environnement 3D et sa structure très géométrique (contrairement à Donkey Kong Returns, beaucoup plus organique), Kirby Triple Deluxe est certainement le jeu qui vous donne la meilleure approximation de ce que j'avais en tête quand j'ai commencé à étudier le phong shading pour faire un Bilou à base de 3D, en 1998. Et les pieds de Kirby qui ne se plient pas quand il bouge vous donne aussi une bonne idée de pourquoi je n'ai pas continué dans cette voie-là après les premiers essais de rendu dans un modeleur.