Saturday, June 24, 2023

area %d = anim %x

Bon, franchement, je me demande depuis quand ce problème attend que je m'y attelle... que ce soit mon blog, twitter ou mon calepin, impossible de trouver une date correspondant à "oui, la plate-forme se reclappe, mais quand je me mets plus sur sa gauche, je passe à travers.

J'imagine que ça fait partie des choses qui m'ont motivé, fin du mois dernier, à faire en sorte qu'Inspector Widget marche à nouveau dans la démo "3 rooms". Et la visualisation des zones de collision m'avait porté à croire que j'avais mal défini les zones de collision dans l'éditeur, sur NDS.

At some point, I noticed that InspectorWidget wasn't working anymore in Dreams.nds ... not quit ideal when you're trying to add bits of gameplay to your years-long-under-development demo. Especially not when you barely manage to get more than 1 hour of gamedev at once. And after I managed to get it back, I suddenly remembered that seeing Bilou falling through the flipping platform was nothing new and that it was linked to the platform having bad hitbox.

Parce que oui, ayant la flemme de définir les zones à la main, cette 'crocforme' est (avec la branche-qui-rebondit), un des premiers objets du jeu à importer une zone de collision passive directement avec area 0 = anim ${FLAGS}, plutôt que d'en fixer explicitement les coordonnées comme j'ai fait avec Apple Assault et School Rush.

Sauf que sur DS, les hitbox sont parfaites. Rien à redire. Je re-transfère le fichier (le lendemain), des fois que j'aurais oublié que j'avais déjà corrigé le soucis. Je recompile (le surlendemain), je teste. Pas d'amélioration. On est samedi, je sors l'artillerie lourde: une nouvelle instruction `break` pour le langage de script histoire de pouvoir mettre un breakpoint dans ddd et commencer à inspecter le comportement du parseur de script pile là où ça coince: dans la boucle qui lit les commandes 'Define' de l'animation où on fixe les coordonnées de la zone solide et des hitboxes pour l'animation.

At first, I thought I had just messed the hitbox within AnimEditor for DS. But a few checks and WiFi transfers later, it turned out that no: the data (as far as the editor was concerned) was ok, but the game still would use the bad hitbox. Checking that on the parser would be darker than night -- or so I thought. This is how the parser now supports an additional "break" instruction that is a noop where you can set a gdb breakpoint while parsing things. These are not meant to be committed, of course, but it definitely helped.

A few 'next instruction' later, it turns out that my engine is using tool-absolute coordinates for the hitbox instead of object-relative coordinates. That is, when you edit an animation in AnimEDS, you don't only store how to use it in-game, but also what you need to edit it again in the editor, and that includes where to put each component on-screen in the editor. The character's center and the edition widget center may match, but they don't have to.

Le code qui gère ça n'est pas tout neuf: il a permis d'améliorer les hitboxes pour attraper les objets dans SchoolRush il y a près de 10 ans. Mais j'ai commis l'erreur de copier-coller le code plutôt que de faire une fonction "importArea" qui serait utilisée des deux côtés: pour les zones actives et les zones passives.
ça n'aura donc pas été trop compliqué de faire en sorte que les deux utilisent le même bloc de code (le bon) et d'avoir une plate-forme qui fait *enfin* ce qu'on attend d'elle :-P

J'espère que je passerai un peu plus vite de la découverte du problème à la recherche de sa solution, la fois prochaine ^^"

If I want to fix that, I need to track not only hitbox#n ORIGIN coordinates, but also those of the 'solid' box -- the one used to decide whether the object cando() something. then when one 'ORIGIN' instruction shows up for the area we're interested in, we create relative coordinates. Hopefunny enough, it turns out I already have code that does precisely that, because I have both active and passive hitboxes and I failed to avoid repeating myself when writing the original parsing code ... and later failed to fix both copies when I realised things were not going as they should have.

Thursday, June 01, 2023

Déveloper pour DS

Figurez-vous que le youtubeur Nathan Fallet vient de se lancer dans une série de vidéo "live coding" pour faire un petit jeu DS. Son idée est de prendre juste libnds et les outils de devkitpro et de faire une adaptation d'un petit jeu pour smartphone qu'il avait fait il y a quelques années. L'idée est assez proche de ce qu'aurait fait un nouveau venu sur un forum à l'époque, donc j'ai envie de suivre ça.

Son point de départ, c'est l'exemple 'animate simple.nds', où on peut promener un sprite sur chaque écran avec le DPAD en modifiant l'image utilisée par le sprite en fonction de la direction prise.

Pour ce programme, les développeurs ont choisi d'embarquer les images directement dans la section "données en lecture seules" du programme. Là où se trouveraient aussi les chaînes de texte si on avait un texte. Pas d'ouverture de fichier avec ce système-là, mais pas non plus la possibilité de prévoir de charger/décharger des images de la mémoire principale pour y mettre autre-chose. En même temps, comme la DS a 4 beaux gros Méga, on devrait être tranquilles pour un projet modeste.

Il y a un programme (que je n'ai pour ainsi dire jamais utilisé) qui prendra en charge la lecture du fichier .png pour le convertir en données brutes (des tiles et des palettes) exploitables directement par le hardware de la console. Par contre, ce programme ne voit qu'une seule image à la fois. Il peut ranger les couleurs dans la palette, éviter les doublons, etc. mais si vous lui donnez deux .png différents, chaque .png produira son lot de sprites supposant que sa première couleur est en position 1 dans la palette. Essayer d'afficher les deux à la fois donnera de mauvais résultats. Le programme d'exemple masque un peu ça avec son man.png et sa woman.png, parce que dans ce cas précis, chaque personnage est chargé dans la mémoire de son écran. L'un sur le chip "main", l'autre sur le chip "sub". Il ne sauront jamais se rencontrer.

Le programme d'exemple n'utilise que la fonction "sprites" du hardware 2D. Aucun des 4 plans de décor possible n'a été configuré. Mais ça ne veut pas dire qu'on soit condamné à errer dans le noir pour autant.

Comme dans le cas de la Super NES, chacun de ces plans serait scrollable indépendamment des autres et possèderait une "couleur transparente": l'entrée 0 dans la palette de couleur. Mais si aucun plan n'a donné de pixel à afficher, le hardware nous mettra un plan avec la couleur 0. Et cette couleur, elle est trouvée tout simplement dans la mémoire vidéo. Les fichiers d'en-tête fournis avec libnds nous donnent un symbole BG_PALETTE qui permet de lire et d'écrire dans la palette "décor" de l'écran principal.

Ce n'est pas la seule manière de faire, bien sûr. On aurait pu activer un plan de tiles, remplir un tile avec une autre couleur puis remplir la map avec ce tile. On aurait pu activer un plan bitmap et remplir 98K de mémoire vidéo avec la même valeur. On aurait pu activer le moteur 3D et mettre un gros polygone monochrome...

Déplacez le BG_PALETTE[0] dans la boucle principale, modifiez sa valeur d'une itération à l'autre, et vous aurez un fond "stroboscopique". Pour faire un dégradé, par contre, je pense qu'il faudra passer par le HDMA. Mais ça, je n'ai pas encore tenté.

Bon voyage, M. Nathan ;-)

Je note au passage que depuis le temps, libnds s'est dotée d'un mécanisme de gestion des ressources (allocation dynamique de mémoire vidéo pour les sprites) et d'une cache pour la mémoire vidéo contrôlant les sprites (permettant potentiellement de les mettre tous à jour pendant le vblank)

Sunday, May 28, 2023

special properties

While thinking at 'how to implement conveyer belts', I end up investigating some currently incomplete code in special blocks handling. They have correct implementation of their collision areas, but since the migration to the new tile types engine, they can no longer act as 'doors' because they all have the same cando() properties.

Au départ, je réfléchissais à "comment coder des tapis roulants" et je me retrouve à creuser du côté du traitement des blocs spéciaux: ils ont bien un support des zones de collisions, mais depuis le passage au nouveau format de maps, on ne sait plus les utiliser comme des "portes". Je m'explique.

Au moment de coder la démo 9-9-9, je voulais mettre un système "clé-et-porte": un petit bloc devait rester ... bloquant jusqu'à ce qu'on le touche avec au moins 9 pommes, auquel cas il disparaissait et devenait du "ciel": on pouvait passer. Mais pour faire ça, il faut qu'une pomme (à travers laquelle on peut marcher, tomber, etc) ait des propriétés physiques différentes d'une porte. C'est ce qui manque actuellement. 

Euh ... attendez. Non: on a déjà un scanf qui ferait ce qu'il faut ... reste à le tester. 

There's some code in the parser to change that, and some code in 'FlatWorld' to return BlockInfo::props when we encounter one ...

Wait ... BlockInfo::props is precisely what this last `siscanf()` updates ... so ... that means the code is not incomplete at all ? it just needs to be tested ?

Thursday, May 25, 2023

Battle Plan : Big Caterpillar

Maybe these should stay secret. Maybe I should make sure my blog doesn't spoil the game by having too much "strategy guide" items in it. Who knows ... Anyway, here's a few ideas on how being able to throw things or having the Big Punch power-up would affect the fight.

It keeps the 'crawl' and the 'tail attack' of the BASIC version. It keeps the 'changing target' where there's one segment to hit, and that's the one where the magic stone currently is. It simplifies the fight by assuming we're good hitting *the segment with the stone* rather than the one next to it "so that it pushes the stone in the right direction".

J'avoue, ça fait un moment que j'hésite à les poster, celles-là. Ce serait moins drôle que mon blog se transforme en soluce du jeu avant même qu'on ne puisse l'essayer, non ? Mais bon, en même temps, j'ai besoin de pouvoir me confronter à mes idées, les trier, les rassembler... et les cahiers à points ne sont pas forcément l'idéal pour ça.

Voilà donc mes notes pour le face-à-face Bilou/Big Caterpillar. Le seul boss qui ait jamais été codé dans la version BASIC. Je conserve la manière dont il avance en rampant sur le sol et son attaque "coup de queue". Comme je l'avais déjà dit, le fait que Bilou puisse depuis SchoolRush lancer des objets change la donne. Plus question de demander de frapper toujours la tête ou la queue. Mais si on doit viser un segment en particulier, il faut que ce soit évident pour le joueur. J'avais déjà envisagé d'utiliser la gemme magique comme illustration pour ça, mais avec un système tordu où il aurait fallu frapper le segment *suivant* la gemme pour la forcer à avancer, frapper le segment précédent l'ayant au contraire fait reculer. Plus rien de ce genre: frapper la gemme la fera avancer, frapper ailleurs sera sans effet.

Then I started trying to bring all that into one single state machine. The goal was to ensure actions taken by the boss would make sense and not lead to tricky corner cases. For instance, the 'tail attack' should only be performed if there's enough room in front of the boss so that it does not attack through the edge of the arena. (as a bonus, that also means that it won't do that attack in a case where that would 'trap' Bilou in a situation where taking damage is inevitable). But then there's a new 'coil' attack that is completely horizontal.

Mais à côté des "idées de gameplay", j'ai voulu regarder si je parvenais à rassembler tout ça en un comportement cohérent. A quel moment faire quelle attaque pour éviter que la moitié du boss ne se retrouve dans un arbre ou qu'une des attaques devienne complètement imparable parce que le joueur n'a pas la marge de manoeuvre nécessaire. C'est comme ça qu'on se retrouve avec une attaque supplémentaire, horizontale et dans laquelle le boss s'assomme lui-même (ou écrase Bilou) quand il arrive trop près des murs.

Saturday, May 20, 2023

Zelda Breath of the Kingdom

Parce que non, j'avais pas pré-commande de Tears of the Kingdom. Il faut dire que je n'avais pas été complètement convaincu par Breath of the Wild, même si je l'ai finalement acheté pour mon compte après avoir fini celui que mon frère m'avait filé. Au bout d'un mois et demi. Bon, je dis "fini", en vrai, ça voulait juste dire "vaincu Ganon". En mode de base. S'il est vrai que ce sont surtout les enfants qui ont en suite joué à "Charly Top Chef", j'ai quand-même repris la manette (malgré son joycon drift) pour trouver tous les souvenirs de Zelda et débloquer une fin un poil moins crispée.

Mais non, je n'ai toujours vaincu aucun Lynel, peut-être même aucun Hynox ni aucune de ces grosses bêtes. J'ai un peu tâté du dragon (excellente idée, ces dragons: une des meilleures du jeu) et des équipements inhabituels, mais sans chercher non plus à compléter la tenue archéonique ni rien de ce genre. Bref, j'aurais facilement pu continuer à faire des choses intéressantes dans le jeu pendant des heures et des heures, mais je ne l'ai pas fait.

Le fait que le jeu demande de passer de longs moment juste à "tenir la barre" en allant d'un point à un autre y a joué pour beaucoup, mais par-dessus tout, il y a le sentiment que ce serait immoral d'aller faire tout ce genre de tourisme alors que Zelda est toujours dans le château à essayer de confiner Ganon. J'aurais tellement aimé qu'avec un DLC ils nous rajoutent la princesse en PNJ que l'on escorte d'un village à l'autre comme suggéré dans la séquence de fin. On serait allé repousser un Lynel pendant qu'elle discute avec les Zoras. Ce genre de choses.

ça, ça aurait eu du sens. Mais à ma connaissance, rien de ce genre n'a jamais été proposé.

Bref, il aura finalement fallu que je me retrouve complètement HS, incapable de dessiner ou même de lire, trop assommé pour décider d'un truc à regarder à la télé. Alors pourquoi pas regarder Link se balader sur le plateau du prélude ?

Friday, May 12, 2023

using between

 

Bon, imaginer un gameplay sympa avec des ponts, c'est une chose. Mais comment programmer le comportement de ce genre de ponts au fait ? hein ? La bonne nouvelle, c'est qu'on a re-fait un passage dans une autre pleine de jeu avec encore plus de ponts, de cordes, et j'en passerelles et des meilleures. Une image comme ci-dessus, par exemple, suggère que Bilou est nettement plus massif que le reste des rondins, sans quoi il ne perturberait pas autant la répartition des charges sur la corde.

So, I now have fun ideas of how to use bridges in Bilou: Dreamland. Sounds like it could be a good idea to also figure out how I'm going to implement such things with my game engine... One thing is almost obvious: the bridge will be made of a set of independent-but-interacting game objects. I'm not going to try and make it one big animated object. If I did, I'd have to manually adjust collision boxes for segments for every frame. That's not what it is for. Same observation applied to the Green Zone boss: Big Caterpillar. And much like the bridge, Big Caterpillar is made of segments that must be able to interact to give the illusion of a single object.

Mais revenons un cran en arrière, voulez-vous ? Alors que J.L.N était face à son premier mini-boss dans Minish Cap, je me suis mis à prendre quelques notes sur le type de contrôleurs qu'il faudrait ajouter à mon moteur de jeu pour pouvoir faire quelque-chose de ce genre. Puis, sur la lancée, à ce que je pouvais en tirer comme conclusions pour la réalisation de Big Caterpillar, le boss de la Green Zone.

Il y a déjà une chose que le moteur de jeu de School Rush peut faire: c'est attacher un Gob (objet mobile fait de sprite(s)) à un autre, et utiliser sa position dans certains contrôleurs. C'est ainsi p.ex. que la chauve-souris-baie se dirige vers Bilou. ou que SpongeBop sait se balancer au bout d'un clou. C'est encore comme ça qu'on peut transporter un dumblador et que ses pieds le suivent à la trace. Mais dans tous ces exemples, on a un seul objet de référence et un objet référent. Quand il y a plusieurs référents (3 BerryBats) pour un même objet de référence (Bilou), ils s'ignorent les uns les autres. Or, pour permettre de positionner les segments internes de Big Caterpillar entre sa tête et sa queue, il faut les coordonnées des deux autres. C'est forcé :P

So since I've just been studying a bit how BC could be implemented, let's see what it can teach us. The game engine can attach one Gob to another one. That's how SpongeBop swings and how BerryBat aim at their target. It is quite clear that those "attach" links will play a role in animating BC, but the question is "who should be attach with whom, and why". When BC will be crawling, for instance, it is easy to make his head move forward during the "expanse" phase and then make the tail move forward (while the head is stopped) in the "contract" phase. As the distance between head and tail shrinks or grows, the location of other segments should adjust.

It is quite easy to find math expression for those locations based on head and tail coordinates. Unfortunately, that'd require *two* attach links per segment, and the game engine only handle one (including in expression opcodes to attach and detach things when collisions occur). I either have to revise the whole engine and break the assumption that One Attach Link Shall Be Enough For Everybody, or I have to come up with some clever way out. That way out could be how a between(...) controller could use not only the attached gob as a reference, but also *the one to which that is attached*. I.e. segments are all attached to the tail, tail is attached to the head and when segment is processed, it reads segment.attach == tail and tail.attach == head to get the data it needs.

Mais gérer deux pointeurs (un "avant" et un "arrière"), ça compliquerait méchamment les choses. Entre autres, ça rendrait difficile le choix de 'qui doit bouger le premier", et ça demanderait une série de bytecodes supplémentaires pour les opérations "attache, détache" des expressions de la machine d'état (que j'aurais cru plus nombreuses. [ ] à vérifier).

Let's see whether that can be enough. I also long for the opportunity to have swinging vines in some Bilou game. They could also be made of chained Gob through attach links. At least when they're hanging free. But as soon as Bilou is using them, I'd like the rules to change. Now Bilou's position dictates how one part of the vine behaves because of tension. If we can use collision to re-write attachments when that happens, that turns into a job for between(...) again.

Retournons vers le pont en faisant une halte à une autre mécanique que je tiens à faire apparaître dans Dreamlands: pouvoir s'accrocher à des trucs qui pendent. J'aimerais depuis longtemps que ce genre d'objet dans Bilou soit au moins aussi bien animé que dans le remake de Pharao's Curse par Lazycow. ce qui suppose

  • un mouvement ondulant lorsque la liane est livrée à elle-même (pas de balancé excessif à la DKC)
  • une certaine raideur quand Bilou s'y accroche (pas question de passer en mode 'corde ninja')
  • Si Bilou n'est pas au bout de la corde, l'extrémité libre continue d'agir librement alors que la moitié tendue est entièrement contrainte par la position de Bilou et celle du point d'attache.

Si j'essaie de dessiner des petites flèches rouges sur ce schéma pour représenter "qui a besoin de connaître la position de qui", on voit que la corde devra réorganiser ces liens quand elle passe d'un "état" à l'autre. Normalement, c'est jouable avec des collision. Et ça confirme mon intuition construite avec Big Caterpillar: le jeu, c'est d'avoir les deux points de référence (début et fin) qui sont directement liés l'un à l'autre (fin->attach == debut) et un contrôleur between() qui suit segment->attach pour trouver fin puis fin->attach pour avoir début et détermine la position-cible à partir des positions de fin et début.

Et pour le pont ? Va-t-il forcément falloir deux liens par bûche ? Je crois que non, mais ça supposera de faire apparaître à la volée deux objets "coincés" par collision sous la bûche qui a reçu un poids, chacun étant la fin d'une chaîne qui remonte jusqu'à un des points d'attache du pont. Et donc que la largeur maximale pour les zones de collision déterminera la largeur maximale du pont.

In Theory, it should be possible to do something similar with the bridge, at least to get a nice alignment of the logs when the force applied is so strong that logs' weights becomes neglectible in comparison. Yet, it's obvious there will be two chains here, both using the same "origin" location (the log on which the force is applied). I thought it could be fixed by creating temporarily two GOBs (red dashes) that are pushed by a collision box of the weighted log (red hatching), each being attached to either side of the bridge (red circles). "slave" logs that must just align would then attach to the dashed-GOB that's on their side (green arrows). But maybe we could even do it without the dashed-GOBs if sides attached to the weighted log and not the other way round.

Saturday, May 06, 2023

Funky 2.1 / A bridge to fun


Il y a bien longtemps (wow. 11 ans!), j'envisageais de faire un niveau vertical plein de Funky Funghis mais je voulais aussi quelque-chose qui soit plus original que la caverne aux champis de Commander Keen... Peut-être en utilisant un levier comme dans la BD.

Sauf que ... j'essaie que la forêt donne la sensation d'un environnement pas trop artificiel. Quelques statues, ok. Un puit, passe encore. Mais des planches-levier-d'acrobates ? J'aimerais autant pas. Bilou qui transporte des cailloux et des planches pour fabriquer lui-même le levier ? Je vais perdre les joueurs en route.

Back in '93, my brother had drawn a jumping mushroom, directly inspired from the Shadowlands of Commander Keen, and placed a few in the Green Zone. Back in October 2000, it became "Toadstool", the first ambiguous character, that is not 100% a baddy, but still not 100% friendly either. In February 2009, it became "Funky Funghi", and in 2012, as I tried to make it an area with plenty of them, again drawing inspiration from the Cave of Descendent of CK4, where we'd sometimes have one appearing from the top of the screen, sometimes have one catching our feet from below, and so on. There was also some places with something acting like a teeterboard, where Funky Funghi's siblings would (unvoluntarily?) help Bilou by propelling him upwards as they bop around.

J'ai tenté d'autres approches, inspirés par l'une ou l'autre vidéo let's play (mais j'ai oublié lesquelles ^^" de l'excellent Tiny Thor à venir), comme cette gelée/résine dans les branches d'arbres ... d'un point de vue purement mécanique, ça pourrait faire l'affaire. D'un point de vue intégration dans l'univers ... euh ... bof bof.

There was such teeterboard in the webcomic, and it is unclear where it came from. Bilou uses it for revenge. That could have been something Bilou made up on purpose.. who knows. But it would feel odd to see some in a green zone level, says my 23-years-older brain. I tried to come up with alternative recently, like this sort of jelly which could possibly work in another environment by which I can't convince myself truly works within a giant hollow tree.

Puis mes gamins sont passés à nouveau sur une plaine de jeu, et dans les plaines de jeux, il y a ces ponts suspendus sur lesquels les uns passent en courant pour faire tout trembler tandis que d'autres râlent, cramponnés aux rambardes, mais tentent de passer quand-même. Puis il y a ceux qui sautent pour tout secouer encore plus. Et dans ma green zone aussi, il y a des ponts (même si je n'ai encore presqu'aucun post qui en montre ^^"), bien qu'ils étaient assez peu marquant dans la version BASIC (en gros, certains morceaux pouvaient tomber. Voilà).

Mais pour "dreamland", ce serait assurément assez sympa que ce côté "un pont, ça tangue" soit exploité, et en particulier, que Funky Funghi puisse faire "voler" Bilou vers le haut si on a le bon timing.

So I'm glad my kids were on a playground last week-end, one with those bridges you run across to shake them more than the lad before did. There *are* bridges in the green zone already (I just haven't shown them to you yet). No sane level designer making a game a few years after Sonic the Hedgehog would craft a platformer without bridges. And I had thought of using them as trampolines, where you slowly get higher and higher by jumping again and again. Why not have Funky Funghi "use" them to propel you ?

Evidemment, laisson à Funky Funghi son côté "utile, oui, mais attention". Par exemple, si Bilou se contente de passer sur le pont, on peut très bien imaginer que chaque fois que Funghi atterrit, il secoue assez le pont pour que Bilou perde pied. S'il retombe d'un grand saut, il pourrait faire décoller Bilou -- plus ou moins l'équivalent d'un saut normal, mais pas forcément vertical.

Now, it's still Funghi. Even when it is useful, it remains dangerous. we could imagine that if Bilou doesn't "prepare" the bridge for being propelled, he would just be bounced away towards Funghi, but not much higher than a regular jump would have. We can also imagine that when Funghi makes just a small bop, it just makes the bridge shake and be somewhat stunned by that.

Bon, un truc pareil, par contre, il faudra l'introduire pour que le joueur puisse l'exploiter. Par exemple quand c'est Bilou qui joue le rôle de poids lourd et un autre personnage (au hasard, Appleman) se fasse propulser. Ça ne sert pas à grand chose, mais c'est rigolo. Ne pas oublier de mettre des trucs rigolos dans un jeu vidéo: on est là avant tout pour s'amuser.

It will take some training if we want the player to figure out things like that. And training requires some sandbox to experiment. So why not make sure that the player will have encountered easy-to-handle baddies (e.g. applemen) on bridges before and had the opportunity to realize that stomping on a bridge has side-effects. It shouldn't be too hard to make it fun and it's always good to have a few things that are just fun in a game.