Showing posts with label collisions. Show all posts
Showing posts with label collisions. Show all posts

Saturday, August 15, 2026

Le fight club, version cflags

Last summer, I started reworking collisions in my game engine. The idea was to be able to define groups, so that e.g. collision-based communications between a stunned blador and its feet would never interfere with a bouncy branch and things it bounces away. Sure, there was way to do that already, by assigning them separate collision flags, but I've long ran out of these, forcing me to make weird groups and combinations.

Oui, parce que bon, ça fait maintenant un moment que j'ai ouvert la branche "new-cflags" dans laquelle on a la possibilité de définir des groupes de collision, et je m'en suis même servi pour gérer les portes, mais si je veux permettre à un appleman de rebondir contre les autres applemen tout en passant "à travers" les petits vers, je me retrouve devant un imbroglio avec la question épineuse "je le mets où, mon nouveau flag ? et est-ce que ça coince ?"

With recent work on the appleman, I wanted to make a distinction between "weapon-sensitive area, from a light object" and "weapon-sensitive area, from a heavy objects", but I wouldn't know how to encode it anymore. So far, the groups had only been used to implement the doors, but felt like it was time to define a new group, to deal with every collision that deals damage to entities in the game... which I finally decided to call the "fight" group.

Alors c'était l'occasion de se demander "et si en fait on faisait un groupe pour toutes ces actions qui retirent des points de vie à un des objets ?". Petit à petit, hein, en vérifiant qu'on ne casse rien (et donc, forcément avec une vidéo Screenshot Saturday où tout d'un coup, on passe à travers les branches au lieu de rebondir et à travers les pommes sans qu'elles ne se doutent de rien...)

Un nouveau G_FIGHT donc (j'ai un peu chipoté pour trouver le nom du groupe puis le Fight Club s'est rappelé à mon bon souvenir, tel une évidence inévitable). Peu à peu, les différents scripts-personnages sont convertis en nettoyant les vieilleries comme les scripts avec des valeurs numériques plutôt que des combinaisons de symboles. Puis arrive le moment crucial de réactiver la branche-qui-rebondit, et là, je me rends compte que garder dans G_FIGHT l'action principale F_STOMP, celle que Bilou utilise jouer à Super Mario assommant un goomba, ça ne va pas marcher. ça va imposer aux zones de collision destinées à être de simples plate-formes de faire partie du Fight Club alors qu'elles n'ont même pas de "points de vie". D'autant plus problématiques qu'on a aussi des flags du type F_PLATFORM pour, par exemple, empiler des taille-crayons sans qu'ils n'infligent de dommages.

It wasn't that straightforward ... I mostly broke everything first and then repaired things one after another. Last week, for instance, only the woodworm would still interact with Bilou. but now the code is cleaner and I think everything is repaired ... I may to a bit more time travelling to check the blador / tiled pencils interaction ... it looks like it isn't working as good as it did previously.

Then a few things just did not resume working, like jumping higher when you press the JUMP button while bouncing on a branch ... mostly because some hit areas needed to be duplicated and transition depending on them needed to be reassigned to the new area. Current GobScript doesn't make that easy to refactor. 

Ah, and yeah, below is a snippet of what the area collisions look like now.   

Heureusement, j'avais prévu de garder une portion des flags "neutres" (CFLAGS_LONE_FLAGS), valables qu'il y ait accord sur les groupes ou non. On pourra donc mettre la branche dans le groupe G_GROUND (qui n'existe pas encore) dont F_PLATFORM ferait partie et lui ajouter un "ah, oui, on prend F_STOMP aussi, même si ça ne fait pas vraiment partie du groupe". Certaines des zones de collisions ont dû être dédoublée (une avec G_FIGHT, l'autre sans), un petit défaut dans la réécriture quand ça arrive et on se retrouve avec des branches qui ne rebondissent plus aussi bien qu'avant ... un petit schéma, un peu de ddd et ça se remet en place. Le code pour tester deux zones de collisions ressemble donc maintenant à


 cflags GobArea::test(const GobArea *o, GameObject *g, cflags mask, GobCollision* gc) const {
     if (o==this) return 0;
     cflags group = mask & CFLAGS_GROUP_MASK;
-    if (group && (flags & CFLAGS_GROUP_MASK) != group) return 0;
+    if ((flags & CFLAGS_GROUP_MASK) != group) mask &= ~CFLAGS_IN_GROUP_FLAGS;
     if ((mask & flags & CFLAGS_EXPR_MASK)==0) return 0;
 
     // congratulations: you may compare coordinates, now.
     

edit: there was one last thing I had spotted that wouldn't work as expected: sticking dumbladors on pencil spikes. Especially, the dumblador might still wake up. So I dug the good old SchoolRush and checked what happened. Verdict: the dumblador *might* wake up there too, but it looks like it's much more unlikely to happen due to terrain geometry.

Sunday, June 14, 2026

Funghi vs. Appleman

Funky Funghi, un des plus anciens personnages de la Green Zone sur DS, vient de se ramasser une pomme. Jusqu'il y a peu, la pomme serait juste passée à travers, mais alors que ce blog va sur ses 20 ans d'existence, j'ai rajouté les quelques lignes de code script qui manquaient pour que l'interaction proposée début 2022 devienne une réalité: on va pouvoir dégager les champignons du chemin à coup de jets de pommes.

Throwing apples around is a bit more fun since they've started rolling when hitting the ground, but one thing has felt odd so far: seeing the appleman crossing the path of a bouncing Funghi without triggering any effect. Since early 2022, I had the idea that we could have Bilou *bouncing* when hitting the hat of Funghi rather than being hurt, and that implied of course that a thrown apple should bounce as well. It was now time to implement that.

Post by @PypeBros@mastodon.social
View on Mastodon

Ah, and yeah, I've played a good deal of the original Rayman, including throwing fist at bouncy plums and yin-yan balls to make them reach the place I wanted them to be. With all this new bounciness, Funghi is no longer just a hazard and you might actually want him to be *somewhere* rather than just wanting him away. Some of the initial ideas -- such as having the hat deflecting apples and only the foot being the target to hit if you want him moving -- quickly proved unpractical, and uselessly tricky to do, especially for a World-1 interaction.  

Bon, j'avoue que comparé aux "prunes rebondissantes" de Rayman, on est sur quelque-chose de nettement plus détendu: Funghi ne risque pas d'aller rebondir contre un mur et de revenir vous écrabouiller. Il ne reçoit que la moitié de la vitesse de la pomme et ralentit de moitié à chaque rebond. Juste ce qu'il faut pour passer d'une souche à "entre les deux souches" en une pomme. Mais ça devrait être marrant quand-même vu qu'il y a moyen de ramasser à nouveau la pomme et de la lancer une fois de plus. D'autant plus que maintenant, il va aussi être possible de rebondir sur le chapeau de Funky Funghi! (c'est peut-être encore un peu fort, d'ailleurs).

Post by @PypeBros@mastodon.social
View on Mastodon

La solution à laquelle je suis arrivé à coup de débuggeur d'expression est un rien intimidante ... entre autres parce qu'elle dérive de la collision destinée à faire rebondir Bilou, et que pour Bilou, je voulais que l'angle change selon qu'on arrivait plutôt sur le bord ou sur le centre de Funghi, comme s'il était vraiment un bumper de flipper. Pour ça, on va préparer deux valeurs et les combiner avec | dans le byte haut et le byte bas d'un entier 16-bit avant d'écrire le tout dans la 10e variable de Funghi avec :a. C'était rigolo avec l'appleman, mais ça devenait compliqué d'y ajouter le transfer d'énergie ... donc au final, pour l'appleman, Funghi agira toujours comme un mur qui absorbe 50% de sa vitesse horizontale et n'a aucun n'effet à la verticale. Et sur l'énergie absorbée, la vitesse acquise par Funghi est moitié moins importante que celle qu'avait l'appleman ce qui nous permet de calculer qu'un Funghi pèse à peu près 2 Applemen ;P

state0->state0 on found$apple [C_THROW we 0 >= w0 0 < & &]
                  (w0 2/ ~ $ff00 & w1 256 / $ff & | :a w0 4/ v0 + :0)
state0->state0 on found$apple [C_THROW we 0 < w0 0 > & &] 
                  (w0 2/ ~ $ff00 & w1 256 / $ff & | :a w0 4/ v0 + :0)

I ended up with something simple for Funghi/Apple interaction: half of the speed of the appleman (w0) is transferred to Funghi, and the other half is bounced back. But since Funghi is bigger, the half it takes only result in 1/4th of the speed. Not pushing him very far, I'm afraid. And well, that's only the part w0 4 / v0 + :0. All the things that happen before are reusing the "protocol" defined for throwing things where the thrower decides of the direction of the thrown object I already used for dumblador. The X speed and the Y speed will be set in the 10th register of funghi with :a and later used by the appleman with

$RTHROWN->$LTHROWN on hit0 [C_THROW wa 0 < &] (wa $ff00 & :0 wa 256 * :1)

Mais dans le code de l'appleman, j'ai quand-même gardé un code générique qui se contente d'appliquer le vecteur-vitesse qu'il trouve dans la 10eme variable de ce qu'il a heurté. ça pourra me permettre par la suite de faire en sorte que les branches rebondissantes puissent elles aussi renvoyer les applemen ^_^. Petite subtilité: la transition hit de l'appleman est testée avant la transition found de Funghi mais c'est found qui fournit le vecteur vitesse que hit va utiliser. c'est pour ça qu'il y a un test qui s'assure que wa < 0, pour que lors de la première frame de collision, l'appleman conserve sa trajectoire pendant que Funghi calcule l'impact. A la 2eme frame, wa est défini et la trajectoire de l'appleman pourra changer.

Et pour la collision Bilou/Funghi, c'est la distance entre le centre des hitbox (we) qui va déterminer l'angle de l'impulsion que Funghi va nous imposer, entre (-3,0), (-2,-1), (-1, -2), (0, -3) 

The idea inintially was to use the same behaviour as the one I coded for Bilou, where the angle you're bounced depends on the position you hit Funghi on. That's decided in Funghi's script with

state0->state0 on found$bilou [C_THROW we 0 >= &] (we 4/ 256* we 4/ 3 - $ff & | :a)

where we is one of the collision-specific parameter (rather than an internal variable of the collided object) containing the center-to-center distance between Bilou and Funghi, ranging from -24 to +24. At the very edge of Funghi, you'll get bounce horizontally by (+-3, 0), and at the center of his hat, you'll be bounced by (0, -3). In between, you could get (+-2, -1) near the edge and (+-1, -2) near the hat. And yup, it's got the overall expr_1 256* expr_2 $ff & | shape again to put both together in a single variable.

I still had to do something for how static Funghi looks when hit by something. My first attempt failed because transanims with characters that are made of a single sprite, and so far, Funghi was still like that. But behold, Funky Funghi 2.0, remade in AnimEDS after I separated hats and feet, so that I can animate it more freely, at in-between frames of some sort, etc.

It doubles the amount of memory it takes, but I think it's worth it. 

Il me restait un truc à régler: Funghi donne l'impression d'être en béton et que rien ne peut lui arriver même quand on rebondit dessus ou qu'on lui lance une pomme. J'aurais bien essayé de réutiliser l'animation "rebondit au sol" dans un sens ou dans l'autre, mais Funghi est toujours un "SimpleGob" premère génération, et je ne leur ai manifestement jamais ajouté le support des transanims.

Je profite donc de la p'tite canicule du week-end pour redécouper un peu Funghi dans le Sprite Editor, refaire ses animations dans AnimEDS et rajouter un "fait rebondir un truc qui arrive du haut" ainsi qu'un "fait rebondir un truc qui arrive sur le côté". Ce n'est pas spectaculaire, mais ça donne plutôt bien quand-même.

Sunday, July 27, 2025

Enfin des portes

 Si, si ... ce blog parle toujours de développement de jeu DS. C'est juste que les portes, c'est pas mal comme morceau et que les retours de camps, c'est pas mal chronophage. Mais voilà! J'ai eu un truc qui commençait à marcher hier!

Post by @PypeBros@mastodon.social
View on Mastodon

ici je vous ai mis la version rigolote. Quelques heures plus tard, j'avais fini par trouver les bons réglages pour que Bilou s'arrête à la porte de destination une fois sa vitesse suffisamment réduite.

Here's the first somehow-working attempt to move Bilou from one door to another. There are plenty of things still quite crude, and we don't even get the illusion that Bilou enters the door. It's based on another patch that allowed entering the end-of-level doors and on a new opcode that simplifies things by letting Bilou directly target the door's destination rather than aligning on the coordinates of the source door as it moved to the target as I initially planned.

If you can't see the clip from mastodon above, it shows Bilou flipping in front of a door (no enter animation yet ^^"), then zipping to the next door (no hiding yet) and then oscillating around the target door endlessly (wrongly assigned the speed division to a "found" event when I should have used "hit" instead ^^" -- that is now fixed). You'd have missed that it happens in the caves under the Hollow Tree, meaning that yes, I started remaking historical levels into LEDS so that I could test them.

All that also uses (and validates) a fresh implementation of revised collisions mechanism so that we can have collisions for "is there a door", "I'm done entering the door", "I've reached the target door", "I've left the target door", etc. despite the fact that generic flags are already crowded since SchoolRush. 

Les plus observateurs d'entre-vous auront reconnu un fragment de la Green Zone, plus précisément la fin du niveau de l'arbre creux, mais parcouru à l'envers. Donc oui, j'ai enfin commencé à reconstruire les niveaux historiques dans LEDS pour pouvoir les intégrer à Dreamland... ce qui a bien plu à J.L.N, d'ailleurs, qui en a profité pour réenfiler sa veste de chasseur de glitches.

La porte elle-même est finalement beaucoup plus simple que ce que j'avais envisagé grâce à un nouvel opcode: ATATTACH qui permet à Bilou de s'attacher directement à la destination de la porte (à laquelle la porte elle-même est attachée).  

Monday, October 21, 2024

bug on blador

J'ai tout les morceaux pour qu'on puisse à nouveau utiliser la map "anniversaire" de la school zone dans l'exécutable de "Three Rooms Demo" ... mais quand je veux le faire tourner, on ne peut pas vraiment dire que les choses se passent merveilleusement bien. J'ai cru au départ que c'était des choses que j'avais "cassées" en développant le "soft landing" des furblocks (la motivation n° 1 à faire revenir ce niveau *maintenant*, c'était précisément de vérifier l'absence de régressions). Et des choses qui vont de travers, il y en a quelques unes ^^°.

I wasn't 100% confident with the changes I made to special flags used to implement the furblock... So as I'm entering one week off, it seemed appropriate to ensure I have all the required material to revive a School Zone level where all the "monsters" are featured and ensure there aren't regression. So here's the "anniversary" level again.

Mais pour certains bugs, un petit voyage dans le temps avec Mercurial ne laisse aucun doute: ils étaient déjà là avant. C'est notamment le cas avec dumblador, et Bilou qui tout d'un coup se retrouve bloqué et immobile s'il lui atterrit dessus au moment où il devrait pouvoir servir de plate-forme...

Cette fois encore, c'est dans ddd que la réponse est apparue. Si le n° d'état renseigné par InspectorWidget (bi28) est pour "RIDLE", l'affichage de Bilou en train de tomber n'est pas un glitch: on a jamais su rester sur le taille-crayon parce que le contrôleur "onpath" estime qu'il n'y a plus de plate-forme... la faute à une modification pour crocform qui ne s'est pas re-propagée jusqu'à dumblador: le choix du bit d'état qui indique si on est bien sur une plate-forme ou non ...

And well, we do have some regressions, and some of them are quite older than the furblock modification. Today I repaired the ability to stand on a stunned blador, which should have been updated after I implemented the pyramid "collapsing" platform: it reuses bit-testing that allowed stacking dumbladors, but for obscure internal reasons, it did not use the same bit to declare that the platform is indeed valid. Tricky to find, but easy to fix.

Saturday, October 19, 2024

Newer collision flags

It wasn't as easy as I wanted to make the "soft land" mechanism work, and knowing that Bilou will have to attach to an invisible block for it to work with the "bumping branch" rather than to the branch itself will make it even a bit harder. The issue is to allocate new "kinds of collisions" in an already crowded 16-bit space.

That issue is not new: it already appeared when I wanted to add pick-and-throw mechanism together with get-your-feet-back while I was just done with direction tiles for inkjet. Each of these would require about 4 different type of "messages" exchanged between game object and I had barely more than 8 flags left... And the solution had been to use some combination of bits for the different things. So you'd still have 0010 saying "up" and 0020 saying "down", but that would only be valid in connection with 0100 that says "direction tile" and not with 0400 that says "interplay" or with 0800 that has been used to collect feet and animate ink level in pipes.

I've got notes already about addressing that, but they mostly question the fact that there are only two "casts" (read game object lists) in the engine: heroes and foes. And that actually address a different issue (collision performance) although they somehow partition "messages" as well: almost only Bilou can receive a "HURT" collision (well, blador receives it too from spiky pencils tiles so it can align with them and this is likely BadDesign ^^")

And well, introducing bridges and things like that could easily use more casts to keep good performances: both heroes and baddies will collide with bridges, but that doesn't requires baddies to test each others to work. And likely only part of the bridge internal.

I had collected a set of questions on my new notes, and one of the first one got its answer: despite we have only 16 bits communicated to the script about what truly happened in a collision, a full 32-bit number is stored internally. Some of the high bits are already use for special purpose such as "keep checking collision even after a first item was found" or "only consider objects attached to me", but plenty others could be used to restrict the scope, e.g. creating a private "namespace" between blador and blador feet so that the feet cannot accidentally attach to an inkjet (although maybe that's redundant with "must be attached to me"). It makes little sense to keep the upper 16-bits as "flags" and process them the same way the lower 16-bits are processed (a single matching bit and bingo, we have a collision) because we would be unable to identify which flag had a match. Did we caught fire ? or get smashed ? or got an electric shock ? no way to find out.

So the plan would be the following:

  • 8 "casts" (lists) seems a good number. Each test area indicates exactly which cast it tests. If we have casts Evil and Bridge and need to test both for STOMP, then 2 test areas are needed.
  • two three "control" bits: ALL, ATTACH and Reverse-ATTACH (fact-checked against Aug'25 commit)
  • 16 "flags" bit: a collision occur as soon as two masks have at least one in common
  • 8 10 "namespace" bit: a collision occur only if the two area exactly use the same namespace 
  • any other are reserved for future extensions.

Thursday, July 20, 2023

class AppleWalls : public WallTest, UsingApple

So I had managed to get shell/walls interaction mostly fixed. I thought it would be wise to get it run 'unit tests' to ensure nothing got broken by the fix, but it would do more than a few seconds of tests before failing. In Walls/Walk test , a fairly recent addition to MapTests.cpp, for which I do not seem to have any commit where that test is a success.

The test failure will produce patterns such as

--(120,0)+<-508,0>:[-224,0]     's/7f.f, S/fe08.0, '
--(118,0)+<-512,0>     's/7f.f, S/fe08.0, '
--(116,0)+<-516,0>     's/7f.f, S/fe08.0, '

--(114,0)+<-520,0>:[-232,0]     's/77.f, S/fdf8.0, '
--(112,0)     's/77.f, S/fdf8.0, '
--(110,0):[-240,0]     's/77.f, S/fdf8.0, '
--(108,0)     's/77.f, S/fdf8.0, '
--(106,0):[-248,0]     's/6f.f, S/fdf8.0, '
--(104,0)     's/6f.f, S/fdf8.0, '
--(103,0)+<248,0>:[0,0]     's/6f.f, S/fdf8.0, '

which I, too, find cryptic. I should at least inform future self that they are from saved "last state" array, dumped on exception like assert failure during the test.
  • (%{x}, %{y}) is used to report new coordinates
  • <%{vx}, %{vy}> is used to report a new speed
  • [%{dx}, %{dy}] is used to report new delayed step

Together with the arena layout defined by walls1, it can start making sense. For instance, x=104 is the lowest valid position before entering the two-# "wall" of the upper platform, where the Gob-under-test is walking. Granted, this is arcane and confusing, and it would deserve a review fix even if it is only for me. Possibly even more confusing are the `s/%x.%x` codes reported between quotes afterwards. These are generated from ChiefInspector::report() call from the WalkerController.

  • lowercase s indicates that there has been a change of tile considered in doslopes(). hex values are the coordinates of the hotspot used to decide that.
  • uppercase S indicates that doslopes() reported we're on sloped ground. hex values are the speed defined during doslopes().

Even with that wrong report of sloped ground fixed, I still have my 'follower' capable of entering walls. It happens when we stop next to the wall, but do not cancel the 'step value still needed'. More investigation needed.

With some break getspeed and cond BREAKNO (x >> 8) < 105 && cdata[GOB_XSPEED] == -520 in an epic gdb session, I could trace precisely what happens, one GobController call after another, and how the speed, delayed steps and the like where further processed.

The issue turned to be linked to the 'maxmove computation', an engine feature added in 2020 that scans the animation commands ahead of think() calls, so that we don't ask doslopes() to check for something different than what we'll actually do, else the vertical move and the horizontal move won't match and we'll end up out of the slope. The code that decides what to do when we detect a move must be cancelled assumes that the motion debt stored in the 'delayed step' variable has already been validated by controllers. In case of failure, we're safe to move *at least there*.

That was true with the "school rush" engine, where it was helpful to ensure we actually get close enough to the walls for the testpoints to work as intended. It is no longer true if maxmove shortened some of it. How precisely this can be fixed still has needed a bit more time to be figured out.

PS: while looking for the 'inspector codes' in my 2020-notepad, I got my eyes caught by a character stating "this is contemporary to maxmove introduction"... The line below reads "hopefully, no need to tweak things (how many loops are allowed while processing animation instructions, btw) too much. All it takes is building an animation list with enough 'I_MOVETO(+1, *)' commands before it loops."

PPS: of course, fixing things here broke things there, but mostly because code was wrongly assuming that some things (like vertical position or FAIL counter)

Sunday, February 06, 2022

tuning de branche ...

Ben ça aura été une semaine de gros débugging pour ... pas grand-chose au final. Enfin, la bonne nouvelle c'est que la branche rebondissante a bien évolué. On avait quelque-chose de peu logique le mois dernier, là, j'ai repris le contrôle.

Le premier truc, c'était de re-définir le timing des interactions entre la branche et Bilou: quand on tombe dessus, la branche s'abaisse d'abord, puis nous envoie vers le haut. Evidemment, c'est seulement pendant la phase "remontante" de l'animation de la branche qu'on a envie qu'elle puisse propulser Bilou.

Il y a déjà (et depuis longtemps) ce qu'il faut pour ça dans AnimEDS, mais j'avoue qu'au moment de créer l'animation, j'avais un peu oublié comment ça marchait. En fait, au moment où on active le mode 'box', la ligne du temps en bas l'écran permet d'indiquer dans quelles frames la boîte est active.

A week spent in debugging because I had forgotten how my tools define over what part of the animation a collision box is active. I had also forgotten that the same keyword 'area' describes the sensitive-and-passive role of a collision for 'sprites' and the offensive-and-active role for special blocks.

Well, at least, it's almost clean now. I should be able to build a new demo version next week-end. But first, I will have to make a check list of the things that are still a bit weird in the current state.

Mais quand j'ai voulu faire les essais avec cette nouvelle animation, plus rien ne marchait. Enfin, la branche détectait l'arrivée de Bilou, provoquait un rebond et activait son animation, mais impossible de se faire projeter en l'air.

Tout ça parce que j'ai mélangé deux mots-clés dans la définition des blocs interactifs (dont la gomme-qui-rebondit qui a servi de modèle à la branche): dans les définitions de machines d'état, area introduit une zone sensible (et passive) pour les collisions alors que test introduit une zone offensive (et active). Pour les blocs spéciaux, il y a un seul mot-clé -- area -- mais il définit une zone offensive. Je l'avais oublié. Du coup, j'ai passé les soirées en mode 'guru meditation' ... pour rien.

Je ferais bien une nouvelle démo pour fêter ça, mais il y a deux ou trois trucs louches aux entournures ... je vais repasser par la case "faire une todo list", donc.

I did some playtesting with J.L.N and *deline ... They mostly wouldn't fall off the branch (I made sure that one wouldn't happen too often), but it wasn't obvious to them how to make big jumps. The thing is you don't need to press JUMP when stomping the branch, but *when it throws you back*. Maybe I should trigger a 'super-throw-back' if player hit JUMP when stomping ? so you don't have to bother too much about bumper timings in level 1 ?

Friday, September 07, 2018

Retro-game mechanics explain: golden!

I just went into a super video (link below) about how collision code can be exploited in Super Mario World to beat the game faster. I'm not much into speedrunning myself, but I love whatever can teach us how those old games where built and what is the logic behind their engine. And tool-assisted speedrunning quickly gets quite deep into those subjects. So let's go.

C'est du tout bon. Une vraie pépite. Une fois encore, "retro-game mechanics explained" confirme que même quand on a pas l'intention de pratiquer le speed-run intensif, les découvertes des speedrunners sont une mine d'or pour qui s'intéresse aux techniques de programmation utilisées par les anciens de l'ère 8/16-bit. Et cette fois, c'est Super Mario world qui s'y colle.

When a tile is activated, it is deleted, and a sprite version of the block will displayed in its place. When that sprite returns to its initial position, it is removed, and another tile is set in its place (usually a brown block).
Well, that was an expected one. And as he explains, it was already used in SMB3 (and probably also back in SMB1). It is something I'd like to put into my own game engine as well, although the closest I have so far is simply the "mapanim" to replace tiles of a specific location on the map along an animation triggered by a collision.

Si vous avez déjà un peu cogité la manière dont les consoles nintendo construisaient leurs images à base de mosaïques de "tiles" et des "sprites" par-dessus, vous aurez aussi deviné que pour faire sursauter un bloc-question quand on le touche, il faut effacer le bloc de décor (à base de tiles, donc) et le remplacer par un graphisme librement positionnable (un sprite) le temps de son sursaut, puis remettre à nouveau des tiles pour le bloc transformé. C'est sympa d'en avoir une confirmation depuis l'analyse du code (et oui, j'ajouterai ça dans ma todoux-liste un de ces quatres).

The next one is a bit more unexpected.

Which tile is activated during sprite/tile collision is determined by a point that is a mix (blue) between the the sprite's position (green) and its clipping box (red). If that point is not within the tile that was activated [...] there will be block duplication.
Of course, that duplication is the whole point in SMW - Level End Glitches video by RG Mech EX:  the location where to spawn the bopping block and set the new brown block won't match the original question block location, which will remain unchanged. Interrestingly, this is partly because sprites that are located inside a solid tile are ejected outwards so that they don't get stuck. I always thought that would be mario-specific, but actually no: it applies to all objects.

Un peu plus inattendu: en plus de leur zone active -- la hitbox, en rouge sur la carapace -- qui doit rester en-dehors des zones solides du jeu, les objets ont un point unique qui sert à déterminer quel bloc a été touché. Si on cogne un bloc-question avec une carapace lancée vers le haut, c'est d'abord le carré rouge qui va renseigner qu'il y a collision puis le point bleu servira à trouver avec quoi il y a collision. Chose intéressante, tous les objets subissent le traitement "repoussé par les murs" qui autorise Mario à contourner un bloc lors d'un saut plutôt que de s'y cogner méchamment comme une Giana sister.

En revanche, le fait d'avoir mis le point-test en dehors de la zone de collision m'intrigue. Quelle est la raison ? ou est-ce juste un bug ? et cette possibilité d'avoir changé de position entre les deux opérations du test de collision (boîte et point) trahit-elle une optimisation du genre "on ne teste les blocs-question qu'une frame sur 4" ?

A few wonders ...
- is there a good reason for that blue hot spot not being with the red box (other than saving computation cycles) ? It just sounds like a bug to my ears, since the red box is what triggers the collision.
- is that "ejected first but still triggering the initial block" linked to some lower rate for the collision code compared to the motion code ?


And did you know ?

In order to reduce the number of distinct objects, some power-up blocks have different contents depending on their X coordinate on screen.
The limit on the number of objects you can have in a game engine is a old opponent. I know him a bit too well myself. But still ... Thinking of editing your level and having to shift that key pick-up one block to the left or one to the right so that it actually contains a key feels just mind-blowing. Naturally, it might not have been a big deal for Miyamoto's team who already knew player needs wide enough areas to move their avatar around ... and possibly went for "aha! guess which of those 4 ?-blocks hold a key and which are mere coins ;-)". But still. That's pretty unexpected.

Mais il reste le plus croustillant. Le truc que explique qu'un bloc supposé contenir une clé peut tout d'un coup donner des ailes à yoshi. Visiblement, je ne suis pas le seul à avoir choisi trop peu d'information par bloc dans mon format de niveau, et pour pouvoir représenter tous les power-ups et bonus possibles dans Super Mario World, l'équipe de Myamoto a choisi d'utiliser la position du bloc au sein du niveau pour choisir quel objet serait offert au joueur. Pas la position absolue, hein, mais le fait qu'il soit sur un bloc pair, multiple de 4, impair, etc.

C'est à la fois génial et complètement déroutant. Dans un commander keen, je ne me serais jamais attendu à un truc du genre "si le bonus est au 2eme étage du building, alors c'est une glace à 2000 points. S'il est au 1er c'est un donuts à 1000 points et au 3eme un nounours à 5000". Mais ici, dans un contexte où les blocs-question sont souvents présentés alignés comme les gobelets d'un jeu de hasard, le truc prend tout son sens. Il reste à considérer le "numéro" stocké dans le niveau non plus comme un identifiant d'objet à créer mais plutôt comme l'identifiant du générateur d'objets correspondant. Au moins, ils ont évité les contraintes du genre "un niveau peut offrir soit les ailes, soit une clé, soit un ballon, mais jamais une combinaison de ceux-ci."

Friday, March 30, 2018

Aladdin Sources Analysis

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

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

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


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

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

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

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


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

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


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

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

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

edit: for more background information about how things actually came into existence, seeing tools and teams in action, don't miss the excellent "Splash Wave" episode on the game.
 

Sunday, December 03, 2017

cando grow?

Not so easy to fix the state machine bug about Bilou entering ceiling And that is mostly because it is not in the state machine itself. It happens because Bilou has two sizes, and the "HIT" state is one where he is big. Since"HIT" is a state that you can reach from almost any other state, we can't really "fix" the problem by conditional transition.

So I took my code back and started looking around the call to CompoundGob::setbbox() for inspiration.
1. the "grow'shrink." mechanism can be nicely separated along axes. I shall try it on the vertical axis alone so far.

2. I should use cando() function to detect potential issues with virtual movements before I actually change the size. That should be much easier to have something where we check whether we could move up by dy (actually grow upwards) and align coordinates if we can't.


Corriger le problème dans la machine d'état de Bilou pour lui éviter de se manger le plafond se révèle plus compliqué que prévu. Et pour cause: le bug n'est pas dans la machine d'états. Du point de vue du moteur de jeu, Bilou a deux tailles (de bounding box) différentes, et l'état "touché" est un des états où Bilou est grand. Du coup, puisqu'on peut se faire toucher dans à peu près tous les états, il n'est pas vraiment possible d'éviter un changement de taille par une transition conditionnelle.

Il faut donc que je révise plus en profondeur le système de changement de taille (setbbox). Heureusement 1) je peux traiter les axes horizontal et vertical séparément; 2) je peux me servir du système "cando()" pour sonder le terrain avant de procéder au déplacement. 3) si je sais corriger jusqu'à la prochaine frontière de tile, je peux itérer pour étendre à des redimensionnement plus conséquents (à vérifier plus tard).

3 . That will work for small resizing, and .can be extended to duck/stand for.human-sized characters  with a simpe - iteration. I haven't tested that, because all shrink/grow changes in Bilou change size by a mere few pixels. And I presume it wouldn't be completely satisfying for something like granting mushrooms for Super Mario. For one thing, the code assume that it is possible to grow. It won't catch the situation where small Mario would get a mushroom in a narrow corridor of bricks.

Tout ça m'aura demandé pas mal de gribouille pour m'assurer que je comprends bien tous les cas de figure et éviter de devoir mettre un grand nombre de tests excessifs pour traiter plusieurs micro-cas (parce que certaines valeurs sont nulles dans certains cas). Peut-être même que je n'ai plus besoin, désormais, du système qui transmet des consignes d'alignement (avec le centre, le bord supérieur ou inférieur) ... mais ça, ça demande à être prouvé, et on verra plus tard.

It took quite some sketching to find the right adjustments and avoid ending up with tons of tests to find all corner cases, but instead do the right computations, taking advantage of the fact that some terms are just zero in some cases. I think I might even no longer need hints about which side we should align against, but I prefer keeping them at the moment, until I get the proof that they indeed turned useless.

fix

Friday, November 17, 2017

most vexing (gobscript) parse


My scripting language is far from perfect, but this one is really the most vexing issue I have encountered so far. I mean, it is hard to debug, counter-intuitive, and it feels more like a question block turned into a thWomp and smashed you. It gave no clue, no warning, you did nothing wrong, but still get smashed.

The core of the issue lies in how collisions are handled:

  • There is always an active "collider" and a passive "collided" object. 
  • collider area is allowed to expand to any size (within int size limits). 
  • collided area will only be checked if the object's boundary box intersected with the active area.

The third constraint comes from performances considerations. But unfortunately, it means if you create a passive area larger than the object's own size, nothing will complain, you will see boxes intersecting, bar no collision will ever occur.

I'll need to take some time to add warnings

Monday, October 23, 2017

out'm'up

The inscene'99 visit had been quite as success, especially regading the 100K game competition. My brother and I then tried to figure out what we would do on the next instance - obviously, our chance of making a good game were higher than making a big demo like the one envisioned for "samedi, tous in my home".

A comment by my peer "Gedeon" about how it was a shame that crazy brix was only using boxes for collisions pushed me towards the implementation of some pixel-perfect collision routines. I remember I wanted to do some pinball game, but after all, it was decided to go for a shoot-em-up. Our secret "games to do" folder had a long list aborted shmups, from the "Polycosmos" conversion of "space Mission", the failed "cosmowars" on RSD Game-Maker, and the aborted "Bilou sky quest" ... Nothing getting any close to our childhood golden award "Warhawk". So I picked the "Tyrian" palette and started to pixel some ships.

Retournons au passage à l'an 2000. La Inscene '99 avait plutôt été un succès en particulier pour mon jeu 100K, Crazy Brix. Deux choses étaient claires: 1) on y retourne l'an prochain et 2) on a plus de chances de parvenir à refaire un p'tit jeu en 1 an que d'aligner une mégadémo sur le thème "Samedi tous in my home"... Gédéon m'avait motivé à dépasser le simple test de boîtes de collisions pour le prochain titre. ça pourrait convenir pour un pinball ou un shoot-em-up. 

Des shoot'em'up abandonés, j'en ai déjà une belle liste derrière moi, à l'époque: le Space Mission pour C64, la BD impliquant le polycosmos, le "Bilou Sky Quest" annulé par un verre de Franta et le Cosmowars foireux sous Game-Maker. Pourtant j'aime ça quand c'est bien fait. Que ce soit Warhawk ou Tyrian si il y a des étoiles et des barres de vies, je suis partant. Et comme j'avais la palette de couleurs de Tyrian sous la main, justement, je me suis lancé.

The assembly code for crazy Brix was quite horrible, and I remember taking care of increasing the quality of the organisation for out'm'up. Especially, frames-to-aminations, level layout, and to some extent the sprites behaviour were described in a "data-oriented" macros system that looked a bit like game script, but converted straight into binary pointers and values -- something that was apparently common in MegaDrive games development.

The second key development was to support a dynamic list of sprites, allowing fancy explosions, lots of shots and even powerups where crazybrix couldn't even accomodate for anything but a ball and a paddle. Two lists, in fact. This engine was the first time I decided to split objects in two separate casts to reduce the amount of collision checks required.

While the level design wasn't very inspired, the game received a brilliant soundtrack, a classic but good-looking starfield effect and Out'm'Up won the first place - although mostly due to the lack of significant competition.

Même s'il a disposé de 3 fois moins de temps que Crazy Brix, Out'm'Up a eu droit au niveau programmation à tout ce qui faisait défaut à son prédécesseur: des effets d'explosions, des power-ups, des sprites ennemis, des niveaux programmables.

J'avais même une structure 'scriptable' pour permettre à mon frangin de passer au level design une fois le travail sur le son terminé, mais il n'y a jamais montré beaucoup d'intérêt. L'aventure s'arrêtera donc une fois la première place de la Inscene y2k empochée. Ce sera mon dernier projet en VGA + S3M. mon dernier projet MS-DOS.

Saturday, October 21, 2017

drop your weapons

One of the most vexing gameplay bug I can think of in School Rush is that you can't enter inkjets when carrying a dumblador. That can easily lead to moments of panic for most reactive players and frustrating death for others.

I now have it fixed, by dropping the requirement of NO_WEAPON when entering inkjets, adding a "THROW" test area when being in the inkjet and ensuring that we DROP_WEAPON if this area ever gets triggered.

C'est probablement un des bugs les plus agaçants de School Rush: impossible d'entrer dans un encrier avec un taille crayon dans les mains (ouais, essayez donc vous-même, vous verrez). En phase de playtesting, ça donne lieu à des moments de panique frustrants pour les joueurs. Je corrige donc en retirant une contrainte sur une transition et en ajoutant un nouvel effet de bord.

What took me a bit too much time to figure out is that the impulse to drop an inkjet must be define as you enter the state with a F_THROW test area, and not on the found-matching-object transition expression (likely because the blador-side of the expression evaluation happens before the bilou-side expression).

Friday, September 15, 2017

Come back from the ink ?

Bon, entre les programmes d'activité des loupiots qui grandissent, les centrales vapeur qui tombent en panne et les interventions de réparation dans la maison, je me prends une petite paire d'heure pour essayer de corriger un couac avec le passage à un niveau vertical dans "School Rush": s'assurer que le jeu relance bien le niveau si l'encre nous rattrape.

Je dois  notamment éviter que Bilou ne puisse rester indéfiniment dans un encrier sous l'encre. Etre invulnérable dans l'encrier, ok, mais pas retomber immédiatement dedans quand il nous projette jusqu'à la fin de la batterie.

It is time to check what the vertical level looks like when we add rising ink in the mix. And the first tests show that there's quite some tuning required. The first mis-steps that my last playtester did led Bilou to be stuck in the ink, invulnerable, but also unable to keep on playing. One of them involved cycling between in-inkjet and hit.

I'm trying to make the state machine detect that we're in the ink and switch to a "swim up" state that would give the player a chance to get out. This is possible because ink has an additional flag that makes it possibly different from a regular hazard. All the engine requires is that the test on "hurts and is ink" precedes the test on just "hurts".


Se faire projeter par l'encrier, c'est "$HJUMP". L'encre, c'est à la fois F_HIT (blesse Bilou) et F_ISINK (fait flotter les éponges). Une réaction "normale" pour le personnage qui tombe dans l'encre serait d'essayer de nager pour en sortir. En particulier s'il atteint quelque-chose qui peut le propulser hors de l'encre. Ce sera "$SWIM", dans lequel on est insensible à l'encre mais qui repasse faire un test périodiquement et continue donc à consommer des points de vie tant qu'on est dans l'encre.

Il faudra aussi que je complète ce "swim" lorsque Bilou arrive hors de l'encre.

Sunday, May 15, 2016

En direct de Leilani's Island

Dans son petit coin de web, Ishi (Craig Forrester, auteur de Johnny biscuit et Princess Chardonnay entre-autres) nous prépare un adorable jeu en pixel art dont le gameplay rappelle assez bien celui de son jeu favori: Wario Land. Bien qu'étant un jeu de plate-formes, le jeu s'articule surtout sur la "roulade" de cette charmante protagoniste. Du coup,

  • la roulade permet d'aller plus vite que si on courait,
  • la roulade permet d'attaquer certains ennemis de face, plutôt qu'en les écrabouillants. En fait, tout comme dans SML3:WL, sauter sur un ennemi ne l'élimine pas. Faire une roulade bien.
  • le saut est volontairement limité (3 blocs, 4 en rebondissant sur un ennemi) pour qu'il soit plus intéressant de se servir de la roulade que de l'éviter.
  • Certaines plate-formes (les cordes) ne peuvent être traversées qu'en roulade. 
  • C'est en poussant sur ROLL en l'air qu'on lance l'attaque spéciale de la fleur de feu, et parce que ça fait bizarre que le bouton ne fasse rien dans certains cas, Ishi ajoute un "annulateur de saut" pour celui qui fait "ROLL" en l'air sans avoir ce power-up.

Hats off to Craig Forrester (Ishisoft) for his brilliant work on Leilani's Island. I hope he won't mind that I keep note here of the good design decision he took to have the game focus on one rich, interesting and funny mechanic: the island girls' ROLL move. If you really don't read French, I suggest you randomly click links in the French text to be sent to some of Craig's updates that I found especially interesting. I hope I'll be able to make Bilou's Adventure as rich as this. Btw, have you noted that Craig selected the 400x240 resolution of the 3DS ?  

Décision sympathique, c'est en ajoutant une fleur dans ces cheveux que Leilani augmente sa force. Et oui, elle peut aussi utiliser une fleur de feu. Mais ici il s'agira d'une attaque-roulée plongeante, capable de détruire des blocs. La combinaison de l'attaque-rodéo de Mario et le bouclier de feu de Sonic. L'utilisation de la diagonale lui permet de rendre les choses plus prévisibles (plus de timer "chéri, ça va couper"), plus faciles à viser (qu'à l'horizontale, en tout cas), et moins génant pour celui qui voulait simplement faire une roulade en atterissant. Bien vu.

Avec son mécanisme de "j'te roule en boule", le jeu permets plein d'aspects-puzzle, renforcé par les effets de réactions en chaîne sur les blocs. Oui, parce que -- contrairement à Bilou jusqu'ici -- le jeu assume pleinement l'héritage de Mario avec des blocs-à-bonus que l'on peut frapper verticalement ou latéralement, et d'autre blocs qui se fractureront si on les frappe assez fort. Cette histoire de réaction en chaine est encore renforcée par des séquences d'objets. Bref, côté éléments à coder, Ishisoft n'est pas en reste.

C'était déjà riche comme ça, mais Ishi nous rajoute encore les fruits, éléments interactifs un peu comparables aux carapaces de koopa, sauf que je n'en ai pas encore vu blesser le personnage. Et pour s'assurer que tout va bien, rien de tel qu'une map-unit-test qui montre en un écran toutes les interactions possibles entre les différents éléments du jeu.

Ishi nous dévoile aussi un truc que je ferais bien de réutiliser dans le développement de Bilou:  l'enregistrement de la trajectoire lors d'une scéance de jeu qui peut servir par la suite à placer des bonus précisément sur la trajectoire proposée au joueur (c'est très KirbyKid-esque, ça ;)

Il est aussi fréquent qu'il teste ses idées de jeu (ennemis, éléments spécifique à un environnement) dans un décor de blocs colorés tout génériques ... le graphisme vient alors plus tard.

Il me reste un gros morceau à lire: le système d'interaction entre les objets.


Object-to-Object collision has been recently revamped in Leilani's engine. In most collision, one object sends several "collision probe", to which the other object replies with either "NONE" or with a custome message indicating how the originator (typically Leilani herself or a rolling baddie) should react.  I think I keep preferring my own approach combining the active/passive roles of Out'm'Up with the CAN_xxx flags of Xargon Engine.

In my engine, I'd have

  • Leilani.walk { test F_COLLECT ; area F_HIT }, 
  • Leilani.roll { test F_COLLECT|F_ROLL } as well as Baddie.roll { test F_ROLL|F_COLLECT }. 
  • Shells would be passive and have only {area F_COLLECT }. Possibly you could need a second flag for F_COLLECT_PUP, so that only Leilani gets flowers. 
  • Spiky.walk would have { test F_HIT; area F_ROLL } : it actively hurts leilani, but may be rolled into.
those "test" and "area" commands may correspond to distinct hit boxes, allowing e.g. spiky to be rolled in the back, but still hurt in the spike.

Sunday, March 22, 2015

Au ralenti ...

J'avais noté de fort ralentissement sur émulateur dans le nouveau niveau. Il me restait malheureusement à constater que les ralentissement se produisent aussi sur la console, du moins pendant que les encriers projettent et si j'ai assomé beaucoup de dumbladors sans permettre à leur pieds de les rejoindre.

Slow downs are only fun when you control'm
The new level wasn't playing well on emulator. In fact, it was so slow (about 15 fps rather than 60) that I  could barely test it. I first thought my PC was unable to emulate it -- I had similar issues with e.g. Yoshi's Island DS when trying to get some snapshots. Running it on real hardware seemed to work fine until I started stunning bladors. With their feet as additional GOBs to animate, the system started to show slow downs occasionally (when jumping while inkjets throws drops). In my former level, I was 4 monsters, 16 drops and 14 feet under the threshold of what the 66MHz of the Nintendo DS can sustain.

Il y a 45 intervenants dans le nouveau niveau, dont 8 encriers et 7 dumbladors, soit un potentiel pour générer 30 objets de plus -- près du double. Oui, c'est risible comparé aux systèmes de particules qui peuvent tourner sur les PC moderne. Même pour 66MHz, ça ne devrait pas faire une telle différence. Mais voilà, à permettre aux gouttes d'encre de s'arrêter sur les éponges, à veiller à ce qu'un encrier puisse interrompre la marche d'un pendat ... bref, en ajoutant des interaction entre les ennemis et pas uniquement entre Bilou et ses ennemis, j'augmente les nombre de tests de collision, et il est possible que ce soit ce genre de chose qui fasse grimper la charge de calcul.

Is it the extra processing with of those monsters, with the animation parsing ? I could get up to 100 of them in Apple Assault, unless I try to make them react against each other (that would mean ~10000 collision checks). I do have some of those monster-to-monster tests, especially with ink droplets since I want them stopped by spongebops you carry along.
My plan so far to address this issue was to create sections of the game and to allow for collision testing only between objects belonging to the same section. Putting that into code lines didn't come out fluently, so I opted for the Commander-Keen inspired approach of freezing GOBs that are out of display range (that is, more than one screen away of what's on-screen). You can still try to collide against them when they're frozen, but *they* won't try to do anything. It was enough. It works well with the current game's flow. I'll revise it to something more generic later.


J'ai bien esquissé un système permettant de rassember les ennemis selon des sections, les collisions étant limitées entre personnages appartenant à une même section. Mais au moment de passer à l'implémentation, rien ne se combinait avec le code existant de façon souple. Je suis donc repassé à une technique plus traditionnelle -- le gel complet des personnages qui se retrouvent en-dehors de l'écran, qui était déjà à moitié implémenté (ils n'étaient plus traités par la couche graphique). Les listes à parcourir pour les collisions restent longues, mais c'est tolérable, d'autant qu'il y a moins de personnages qui provoquent des tests de collision. Et vu la constitution du code "GameObject::play", avec ses chaînes de contrôleurs et ses listes de commandes d'animation à interpréter, il n'est pas évident que la centaine de comparaison de coordonnées supplémentaire pèse tellement lourd par rapport à 5 objets supplémentaires à gérer entièrement.

There's an important effect of this mechanism. Yesterday, the position of a moving inkjet when you approach it depended on the time you took to reach it, from the start of the level. Today, it only depends on the speed you approached it for the last 2 screens. It will make learning of patterns easiers, as the level can be broken down into smaller chunks. It also means that the pattern of multiple GOBs (like two-inkjet-close-to-each-other) can now be manipulated by how you approach them... much like in Super Mario World.