Showing posts with label slopes. Show all posts
Showing posts with label slopes. Show all posts

Monday, August 24, 2026

The Lost Tiled Tutorials

Many of the tutorials I had encountered between SEDS and LEDS are now out of the web. Too bad, they were key material I sent readers towards when I did not feel like translating some lengthy explanation about tiled games in general. Since I'm editing some part of the blog as if it was going to be the Chapter 1: Tiled Games of a book, I found myself digging through archive.org to find the original material, print it and review it as much as possible. 

The most influential of them all was certainly the MC Kids big post. Where we have Greggman discussing *really* how they made the game back on NES days. It details several clever tricks that help making a quite-sized game on limited resources, some of which make mostly sense if you're on #6502 CPU (like having a set of byte tables rather than structures), but also the key gamedev concept of a "hotspot", a single point that will be used to model the position of the character on slopes.

It is also the most official one, as the post was originally written in 1992 for the Journal of Computer Game Design while the game was from 1992 as well. And since there was an official body publishing it, I can't quite just bring in many pictures of it ... but I guess it's fair to study it, take notes and then post my own notes about what was being said.  

Entre la création de SEDS et celle de LEDS, il y a eu pas mal de posts qui faisaient référence à l'un ou l'autre tutoriel sur la conception de jeux à base de tiles. Evidemment, là-maintenant, un grand nombre de ces tutos ont disparu d'Internet ... juste au moment où je me dis que ce sujet serait bien comme premier chapitre.

Le plus gros d'entre eux, c'était sans doute la présentation du code de gestion des collisions perso/monde dans le jeu NES M.C. Kids, auquel je faisais référence pour les lecteurs Anglophones en 2007 ... Pas trop question d'en faire des captures d'écran dans tous les sens par contre: le truc avait quand-même été publié dans un journal officiel à l'époque. Mais un scan de mes notes, ça, ça me semble honnête. 

The paper goes into significant details of how the levels are encoded (1 byte per 16x16 block, that is used to lookup the 4 tiles composing the block and its type among 100 possible types), how collision with terrain are handled (with direction-conditional testpoints and 5 collision functions per tile type). The author relates the fight to get that extra memory and how you could map the whole level into memory and thus make it easily explorable and transformable thanks to this, but how 8x8 granularity would have explosed the RAM space budget.

It is also completed with retrospective thought of the author about how they'd have inserted "beginning of hill" and "end of hill" types (helpful to save lookups in the slope management) and how they'd have made the "how do I move left[tiletype, xpos]" array providing absolute position rather than relative (+1, 0, -1) positions in a revised version of the engine.

Bon, par contre, près de 20 ans plus tard, il faut bien avouer que pas grand-chose de tout ce qui est présenté là ne se sera retrouvé dans le moteur de jeu de SchoolRush. Entre les ruses propres au 6502 et les choix d'implémentation que l'auteur lui-même indique qu'il n'aurait pas refait pareil rien qu'un an plus tard, il reste juste le concept de "hot-spot" -- réduire le personnage à 1 point quand il faut décider où il se trouve dans une pente -- et le conseil général que "mettre des pentes dans un jeu, ça complique significativement le code". Mais c'était intéressant à relire malgré tout.  

Retrospectively, most of it did not end up in the GEDS engine. With a 66MHz CPU, if you realize that you need one extra memory lookup to get proper slope implementation, you go for that extra lookup. And if you may need more than one, you just write a loop. It's not about being lazy or not, it's about getting the most of what you have. And nowadays, you could "easily" add some "start-of-slope", "end-of-slope" meta tiles by means of auto-tiling rules.

The second note-worthy series are Tony PA tutorials about tiled games development. These were for flash games, with full-running examples at each step and detailed ActionScript code. Smartly enough, Tony starts his tutorials with top-down playfield in which the character can move freely, and then step by step introduces gravity, moving platforms, ladders and finally slopes. 

I was enjoyed to start reading it as it featured a picture of Charlie the Duck (which may explain why I was researching about it in 2007 and certainly why I just posted about it, btw :P) and saying "Sure if our hero is a jumper-type of hreo, he could still [proceed forward in a stair-like, slopeless ground], but normal heroes are very happy if they can avoid jumping. It could have been a good resource, but I knew from the time I've spotted a drawing with "impossible slopes" that I couldn't derive from what was presented there.

Un autre tuto qui sortait du lot, c'était la page sur les pentes de TonyPA. On lui doit la phrase sur les héros qui sont content de faire des bonds (hmmm ... Jill of the Jungle ?) et ceux qui trouvent que les pentes, c'est bien. J'avais laissé tombé assez vite à l'époque constatant que plusieurs interdits allaient sans doute me pourrir la vie ... Il faut noter que Tony faisait ses tuto pour de l'ActionScript, ce qui lui permettait d'avoir une petite démo interactive dès sa page web, mais ce qui donnait aussi du code plutôt étrangeoïdal pour qui a fait un minimum de C/Pascal/Basic dans sa vie.

A côté de ça, il faut reconnaître à Tony que sa petite démo s'inscrivait dans la continuation d'autres tutos qui démarrent dès le niveau "sokoban" pour ajouter petit à petit la gravité, les échelles, etc. Par contre, les "combinaisons interdites" qui proviennent du fait qu'on désactive tout test de collision horizontal dès qu'on est sur une pente ? Le personnage qui logiquement grimpe des escaliers tandis que son image se déplace le long d'une droite continue ? Non. Je me félicite de ne pas avoir insisté. Mais il aura le mérite de m'avoir aidé à déterminer pourquoi je veux des pentes dans Bilou. Je n'ai pas la physique de Sonic, mais depuis Keen 4, les niveaux à pentes semblent plus intéressants que leurs équivalents tout à angle droit. Et une pente, ça permet aussi de faire prendre de la hauteur à un projectile du type carapace de tortue, en plus de rendre le niveau plus organique.

During this 2026 retro-review, I noted that most of the slope logic seems to imply that only the display of the character is aligned with the slope. The logic entity made of testpoints and hitboxes simply moves along a stair. The two locations are only re-aligned if we jump from a slope. It also bypass the problem of solid-ground-tiles-and-slopes by disabling all horizontal checks while you're on a slope. That explains some of the "forbidden tile combinations", and implies that if you make a slope in a path that is high enough for certain characters in your game but not all, the engine will completely ignore the fact that the monster you're fleeing through that narrow passage should get hit in the face and not be able to follow you. And that has been a critical thing to address for my games even before I started working with the Nintendo DS.

So why would you put slopes in a Mario game ? so that your koopa shell keeps flowing forward and not bounce back in your face. Here's why. (Or simply to make your organic level feel organic and not just look organic). 

A last one ? Hopefully, the video from Vblank Entertainment about the conversion of Retro City Rampage into ROM City Rampage is still online. It goes into details about what tiles are and how it allows a large world to run on a sub-MegaByte cartridge, what palettes are, why you need to care about not putting too many sprites per line and so on. If you ever need a primer to the "Retro Game Mechanics Explained" series and feel like sitting idle for 10 minutes, this is the best I can think of at the moment.

(The author ended up writing his own NES emulator full with debugging features and his own high-level assembly -- NESHLA hosted on sourceforge -- in the process)



Monday, August 17, 2026

Testing landing-on-slope

 

While reviewing the "tiled" chapter produced by my blogpress tools, I remembered that landing on a slope still produces odd effects from time to time. And then came an idea to reproduce these conditions in the unit-test environment.

Just as I was observing that the "dkp54" branch has been around from a long time and so has been the "cflags" branch and that "right, I promised myself I wouldn't merge things unless I've first checked they still pass automated tests."

And unfortunately, right then, the tests were not passing, they were crashing. First with an exception and then with a segmentation fault. The "offending" commit was something from 2025, a few lines that will report (rather than ignore) syntax errors within the GobState parsing.

It took some times to identify where the error was introduced, much less to fix it (hopefully) and so the 'dkp' branch (toolkit update) is now finally merged and the cflags can be just that: a branch propagating the new collision mechanism (and not a adventurous combination of features).

Sunday, December 17, 2023

tapis roulant!

Nous y voilà enfin! Après l'eau-qui-pousse, je peux vous présenter le sol-qui-pousse. Le point délicat, vous vous en doutez, ça aura été de mélanger ça avec des sols pentus. Ce à quoi je ne m'attendais pas franchement, par contre, c'est que être poussé pendant qu'on est immobile sur le sol se révèle plus compliqué à gérer qu'être poussé pendant qu'on avance.

The natural step after water-that-push-Bilou was ground-that-push-Bilou. It started quite nicely and it is finally working, but to reach that nice behaviour, there has been significant ups and downs, and especially to get it working on sloped ground. But hey, sloped conveyor ground was my trick to have one-way halls without getting too mechanical.

C'est que pendant qu'on avance, on appelle régulièrement la fonction do_slopes, voyez-vous. Et même si le sol annonce "je te pousse de deux pixels sur la gauche" alors qu'on est sur une pente, on finira bien 2 pixel à gauche et 1 pixel plus bas si nécessaire. Mais rien de ce genre dans l'état "immobile sur le sol". Et notre fonction do_slopes est prévue pour aller directement corriger la vitesse et les déplacement retardés du personnage, or l'idée du "sol qui pousse", c'est justement de manipuler la position dans un premier temps et de laisser le code qui traite la vitesse tranquille. Sans ça, sur un tapis roulant trop rapide, vous verrez Bilou se retourner pour marcher tout seul vers la gauche plutôt que de le voir s'échiner à avancer vers la droite tout en reculant malgré tout vers la gauche. Pas terrible.

Dans Rayman, j'avais pu voir qu'on avait deux types de pentes: des normales et des glissantes. Chaque angle et chaque offset est dédoublé avec un second type pour gérer les deux types de physiques. J'avoue que je souhaite plus de souplesse pour Bilou. Pas tant que j'ambitionne de faire un jeu plus complexe que Michel Ancel, mais surtout parce que je n'ai pas l'occasion de planifier l'ensemble du jeu. Je suis incapable actuellement de garantir qu'il n'y aura jamais plus de 2 types de sol dans un niveau ni 2 types de pentes. Le projet était donc d'utiliser des emplacements séparés pour encoder la pente et les propriétés du sol. Et quand je me suis mis à coder, c'était encore plus simple que prévu: quelque soit la position de Bilou sur une pente, il est toujours à l'intérieur du tile "pentu". Et donc pour trouver le tile avec le type du sol, il suffit toujours de regarder 8 pixels plus bas. Enfin ... presque toujours.

And that was the final test for my "neat idea" to "simplify" the level editor: use separate tiles to indicate the slope (if any) and the ground properties. Ground properties are always on the 'plain' part of the ground and the slope types sit on top of it. A new getGroundType function can probe through that slope layer to find the actual ground and return its numerical type. Then, each controller use that to index to retrieve relevant parameters, such as push vector, friction, or whatever needed. It mostly worked except on one spot: the precise location where Bilou's hotspot has just been shifted out of the lowest 'sloped' tile of a slopes stripe. It is now mid-air, with one 'slope' tile below it and the ground properties one more below. Then Bilou stops because it is no longer pushed but doesn't fall either. And if I try to hack that around, I end up with Bilou moved horizontally instead of following the slope because there's no slope to follow.

Comme toujours avec les pentes, les problèmes commencent quand le sol n'est plus dans le prolongement horizontal de celui utilisé une frame plus tôt. On se retrouve dans ce cas-là soit avec Bilou qui s'arrête au milieu de la pente, soit continuant à se faire pousser, mais à l'horizontale. 

Yet, when you walked on the sloped ground rather than letting yourself pushed by flowing sands, you wouldn't get any issues. The trick is that walking state already has a call to the do_slope function that keeps Bilou in contact with sloped ground despites of motion. The extra shift happened prior that call, so it would still re-align when Bilou's own velocity is applied. But there was no such call in "idle, standing" state so far. And the do_slopes function wasn't meant to affect anything but character's speed, while it should now sometimes affect temporary variables used to immediately apply a coordinates update.

And just in case that seemed too easy, a typo in the first coding sprint made me change accumulated step instead of actual speed, ruining the whole thing with Bilou digging into the ground. Then a bug introduced in the level editor broke some part of the level without me realizing it and I started suspecting wrong properties for the ground here or there.

But there we are. It's working, even though it cost me hopping back in time with Mercurial and re-doing the sprint one dash at a time, checking, committing, re-importing maps and the like. And about one week later, I finally shot a decent .gif of the behaviour so I could post it and write this stuff. It's been planned for so long. It has been through so many preliminary work and so that I'm gonna call it a milestone.

Bref, inspector widget, ddd, j'ai pu sortir toute la panoplie. Presque (pas les unit-tests, cette fois). Mais l'éditeur de niveau m'aura bien cassé le rythme.

(wow. Le soir est tombé, j'ai encore une machine à faire tourner et je n'aurai pas fait plus de homebrew de cet aprèm' libre que de vous raconter tout ça :-P)


Saturday, January 29, 2022

Les bonus s'emmèlent...

J'étais tout content de voir que Bilou savait aller dans l'eau, et je ne me suis pas rendu compte qu'il y avait un soucis avec les bonus: on pouvait carrément marcher dessus. Quelque-chose lié à la nouvelle propriété F_START_FALLING, sans doute ... sauf que non. c'est surtout lié aux bytes 'regarde à côté'.

C'est que l'ancien moteur de jeu utilisait le numéro du 'bloc spécial' pour décider s'il devait être solide ou non, et la fonction qui indique la 'hauteur du sol' essayait toujours de faire comme ça. L'ennui, c'est que j'ai utillisé les codes 0xfc à 0xff pour les fameux 'regarde à côté' qui permettent aux blocs spéciaux d'être des blocs, et pas juste des pavés de 8x8.

Jan.24. Managed to have Bilou switch to 'swim' state when it gets in contact with F_WATER tiles. It's as swimple as $LFALL->$INWATER on fail [H WATER ?] ...

Jan.25. It uses the new H GobExpression 'operator' that puts type of the tile under character's hotspot on stack, where it can later be compared with a constant flag (WATER). That operator will expect an elevation value on the stack first. like [2 H WATER ?] to tell "check 2 pixels above the hotspot"

But this is not the point of this post. The point is, looking at the video I captured (bottom left on the toot panel below), I noticed I was walking on disappeared apple before going into the water.

Jan.29. When you pick up a bonus, a MapAnim is spawn, that will edit graphics to get that sprinkling animation. It should clear "properties" as well, as soon as you get in contact with the collectible.

Yet, the change to newmeta changed what 'cleared' means on the map. Now, the clear value 0 means 'lookup World::properties[0] to know what you can do'. Another trap was that, when checking ground height, you might find yourself on one of the new "lookup one tile left" or "lookup one tile up" that are used to make special *blocks* of adjustable size (not just special mini-squares of 8x8 pixels). If that happens, code was still lacking "locate the actual block defining corner" and then use that special blocks's properties set to decide whether it has ground or not.

Pas le choix, donc: ici aussi, il faut retrouver le 'coin actif' du bloc spécial et aller chercher ses propriétés dans le BlockInfo correspondant.

Deuxième farce (voir l'animation): une fois le bonus effacé, il a laissé derrière lui un bloc à travers lequel il est possible de continuer à tomber, mais aussi de continuer à marcher. La faute cette fois au tableau des propriétés pré-encodées pour les blocs à définition indirecte (prévus pour les physiques particulières, essentiellement).

edit: if you don't have a twitter account, here's what the videos looked like:

Wednesday, January 12, 2022

Slopes Landed.

I think I got it working. There was one major flaw in my earlier design: stating that you can F_FALLTRHU a slope implied that you could no longer walk on it, as the walking controller tests for solid ground by checking whether it would be possible to fall down from the current location. Oh, not much. Just one pixel is enough to claim that you cannot walk anymore.

Là, je pense que je le tiens. Il y avait un défaut majeur dans mes premiers essais: considérer qu'on peut tomber à travers une pente en lui donnant la propriété "F_FALLTHRU" implique automatiquement qu'on ne peut plus marcher dessus: le contrôleur "walker" s'assure que le sol est solide en testant s'il serait possible de tomber à travers ... pas beaucoup: juste quelques pixels, mais si ça marche, ça suffit pour indiquer que marcher n'est plus possible.

Mais la technique utilisée pour les collision avec le sol (le "cando()") part du principe que on ne peut faire un déplacement que si l'entièreté de la zone concernée permet ce type de déplacement. On ne peut nager qu'entièrement dans F_WATER, on ne peut grimper qu'entièrement sur F_LADDER, etc. Mais donc un tile de type "pente" doit à la fois nous permettre de tomber à travers lui (au moins jusqu'à atteindre l'altitude du sol) et nous empêcher de commencer à tomber. Le seul moyen de réconcilier les deux, c'est de dédoubler le flag: avoir un bit indiquant "autorise le début de chute" (F_START_FALLING) et un autre "autorise de continuer à tomber" (F_FALLTHRU). Un pour le contrôleur de marche (walker), l'autre pour le contrôleur de gravité. 

But the technique used for terrain collision detection -- cando() -- assumes that we only do a move if we can do it over all the tiles covered during the move. That means the slope tiles should both allow us to fall through them (until ground height, at least), and not allow to start falling through them. To get that solved, I had to split the flag, having one bit telling whether we can start falling (F_START_FALLING) and one telling whether we can keep falling. Walker controller tests one of these bits, gravity controller tests the other one.

Then I had another issue, not properly computing the ground distance to see whether the move we cando actually remained over the ground. Let Bouli explain that...

We were at old position (ox, oy) and will move our 'hot spot' (the one that is kept in contact with the ground on uneven grounds) to (hx,hy). In order to know whether we're find with slope-landing, we compare hy - oy with the 'ground distance', which is construct with an appropriate sum of tile.groundheight() calls.

But groundheight() gives us a value relative to the bottom of the corresponding tile. -8 means the whole tile is solid. -2 means the first 2 pixels of the tile are solid, the rest is air. 0 means the whole tile is air. That was quite quickly remembered and accounted for. The other part to take into account is that the start of the darkblue 'vertical motion vector' may be anywhere within the first tile. If we're in the 4th pixel of the tile and the last pixel of the tile is solid, then only hy - oy < 3 are uninterrupted moves.

Mais ce n'est pas de ça que Bouli veut vous parler. Une fois le problème des propriétés réglé, il reste à calculer correctement la distance jusqu'au sol. Supposons qu'on parte d'une position (ox, oy) et qu'on veut déplacer le "hotspot" (le point qui doit rester en contact avec le sol dans une pente) en (hx, hy). Déterminer si on a atterri sur une pente, c'est comparer la distance hy - oy avec la distance jusqu'au sol, à partir des résultats de tile.groundheight().

Seulement, ce nous donne une altitude par rapport au bas du tile. -8 pour un tile entièrement solide, -2 pour indiquer que les 2 pixels du bas sont solides et qu'il y a ensuite de l'air, 0 pour un tile constitué uniquement d'air. Une fois qu'on a relu un peu sa doc, c'est vite traité. Par contre, le début du vecteur-mouvement (en bleu foncé sur le schéma) peut se trouver n'importe où dans le tile de départ. Si on est au 4eme pixel d'un tile dont seul le dernier pixel est solide alors seuls les mouvements tels que hy - oy < 3 pourront se faire entièrement.

Sunday, January 09, 2022

Land on Slope

There are many things that got fixed over my holidays, and many things that remain to be done before I can claim the 3 rooms "done". I had to pick one for the last week-end ... it seemed wise to pick something that I'd likely not have enough focus to work on during the evenings of a regular week. "Land on slopes" seemed the right one to pick.

Il y a pas mal de choses qui restent à arranger avant de pouvoir cocher la case "démo avec les trois salles de test", et ça malgré toutes les choses qui ont pu être corrigées pendant les vacances. Alors pour ce week-end, pourquoi pas s'attaquer à l'atterrissage sur des pentes ?  

So far, only the "walk" (behaviour) controller is aware of slopes. For the rest of the code, the world is all made of square tiles, and the properties of a tiles are homogeneous over that square. The result is that if you try to land from a jump on sloped ground, you're very likely to end up floating over the ground until you start walking.

Jusque là, le seul bout de code qui s'intéresse au pentes, c'est celui qui contrôle le comportement "marcher". Pour le reste du moteur, une pente, ce sont des pavés carrés comme tout le reste. Du coup, si vous faites un p'tit saut pendant que vous étiez sur une pente, il y a de fortes chances que vous finissiez en flottant au-dessus du sol ^^". 

J'ai passé la journée d'hier à faire des va-et-viens entre le débuggueur et le compilateur pour essayer de comprendre ce qui se passait, mais c'est sans espoir tant qu'on ne saura pas tomber à travers les tiles qui constituent les pentes... A cogiter.

I gave it a first try yesterday, opening the debugger, seeing what happens when I'm entering such a tile, designing a patch, compiling it, trying it, discover that it wouldn't work, refine it, and repeating the cycle. Over and over.

It wouldn't work. First because the slope tiles couldn't be fallen through. There's no need to try working around it: they *must* be made fall-through. Second because slopes are currently complemented by walk-through-but-don't-fall-through blocks that ensure smooth walk, but interfere with falling. I had that discussed with the Undisbeliever in the past, as it was a difference between our implementations, and the solution will be to replace them with a 'sloped' ground that actually is square.

But the ultimate reason of my failure is that I was trying to provide a solution for 'fall on the sloped ground' as if we'd have the last row of tiles all made of F_SLOPE tiles. The reality is much more diverse: there are so many 'corner' cases that we can hardly call them 'corners' at all.

Mais même avec cette nouvelle propriété et même avec des blocs carrés qui disent que "oui, en vrai, on est des pentes, mais des pentes plates. haha", ça ne marche pas encore. La solution que j'avais prévue suppose que le sol sur lequel on veut atterrir est constitué d'une brave rangée de tiles de type "pente". Mais comme vous pouvez le voir sur mon petit croquis, il y a pas mal d'autres combinaisons qu'il faudra aussi prendre en compte. 

J'ai une idée pour remplacer ça ... il faudra que je la teste. Elle n'est pas sans me rappeler (ce que j'ai compris) du moteur de Sonic, d'ailleurs: considérer toute la colonne de tiles par-dessus la nouvelle position souhaitée pour le "hot spot" et calculer la "hauteur du sol" dans cette colonne.  

I have a replacement design sketched, which I'll give a try in the afternoon. Amusingly, it looks a lot like what (I've understood) happens in Sonic the Hedgehog engine: consider the whole column of tiles on top of the desired 'new hot spot position' and figure out the 'ground height' in that column. Then make sure the move planned so far doesn't work past the ground.

Do that every time. If done right, it doesn't matter how many sloped tiles were encountered when checking that we cando() the move.

Well, that was the plan, but for some reason, it is not yet quite working. And for some (possibly other) reason, it managed to break walk-on-slopes despite my care to avoid so.

edit: fixed

Saturday, December 25, 2021

xmas21.zip

I finally posted something more on my sourceforge project: the set of editors (plus runme) as I've got them by this Christmas, invoking oven-hot slopes support.

I wish I could provide you with a meaningful 'change log' for this 'release' of my tools, but let's be

honest: there are over 20000 lines changed between then and now (assuming that you're only interested in what changed since last AnimEDS release in 2020). It won't be exhaustive, but I'll try to give you an idea of what's in anyway.

Sprite Editor

  • allows you to work on any file you've got on your memory card (including automatic backups put in /DATA/SEDS/*)
  • better 'colors zapping' implementation.
  • 'where is that color used?' now cleanly implemented in the PaletteWindow
  • (improved host tools sprdo and sprck are missing from this pack)

Animation Editor

  • allows you to work on any file you've got on your memory card. 

Level Editor

  • uses new map layout with dedicated PHYS layer (accidentally named 'SYHP' in the file >_>)
  • show the 8 color palettes when selecting a block in DRAW mode (A). Color paint mode now available on both foreground and background layers (trigger with L+A on the desired layer)
  • supports new any-sized special blocks with 'lookup arrows', features revised 'meta buttons' widget to select tile properties.
  • reads new 'slopes %d = "%x"' hint in .cmd or .gam files and populates the slope tiles graphics accordingly. Different maps can use different slopes or share them by importing the same .gam file.
  • supports spritesets and tilesets overlay commands

runMe (gameplay preview & WiFi transport)

  • Allows live-skipping of buggy script lines
  • allows auto-repair of the /moving/ directory
  • features new level map engine
  • supports script-driven song swapping with zik.file = "%s"
  • Improved modplayer playback speed stability
  • supports spriteset overlays (spr.more "%s" ...) and tileset overlays (bg0.more "%s" ...)

All these .nds files will work from anywhere in your flash card, but if you install them with their original *dlta.nds filenames at the root, you'll be allowed to hope from one tool to another with the embedded buttons (quit, LEDS, SEDS, MEDS) in the application welcome windows.

Saturday, February 13, 2021

Higher Order Blog ...

Of course, it's been a long time since I first read the guide to implementing 2D platformers on the Higher Order Fun blog. I believe it's been part of my research on how people implement slopes together with the MC Kids talk on implementing sophisticated platformers on NES. So somewhere around 2007.

Un tout bon blog, qui continue à nous proposer des conseils pour implémenter un jeu de plate-forme 2D depuis facilement 2007, époque où j'ai dû le rencontrer pour la première fois. Mais en repassant dessus il y a quelques mois (magie Twitter), je me rends compte qu'il y a encore un bon nombre de fonctionnalités discutées que je n'ai pas encore implémentées dans mon moteur à moi (échelles, plate-formes dont on peut redescendre), et d'autres qui sont en cours de révision (pentes, plate-formes uni-directionnelles). Je repasserai dessus dans les jours à venir (j'espère)

There's still a number of topics I haven't implemented in GEDS that are discussed in that post, including ladders, refined slopes and one-way platforms. So I finally printed it all so I could take the time to read it quietly and take notes on what was neat and what needs more thinking. That was over 6 months ago.

So unfortunately, the toughts aren't fresh in my mind anymore. I'll have to read them back *too* before I can talk about it more.

The blog post presented different ways to implement a platformer. Tiles-based, of course, as it was de-facto technique used on virtually all 8-bit and 16-bit consoles that used tiles-based hardware (and still an efficient technique on page-flipping hardware), but also vector-based levels (only mentioned, though) and bitmap-based. I wish there was more being said about the techniques used with bitmap-based level modeling, as I believe it was the technique used in Fury of the Furries. 

Mais une particularité de l'article de Rodrigo Monteiro, c'est qu'il ne se limite pas aux jeux basés sur des tiles: il discute aussi le cas des niveaux modélisés par des vecteurs (Braid), à positionnement forcé sur des tiles (Prince of Persia, Flashback) et basés directement sur un bitmap (Worms). Et là, je trouve qu'il a peut-être été un peu léger en oubliant de mentionner Lemmings. Bon, d'accord, l'article est bien plus ancien que le talk de Mike Dailly présentant le fonctionnement (et l'éditeur de niveau :P) de Lemmings 1 et 2. Bon, on est d'accord: ni Lemmings ni Worms ne sont à proprement parler des jeux de plate-formes, mais ils restent en vue latérale avec de la gravité, et à voir la physique du personnage dans Fury of the Furries, je dirais qu'on pourrait bien en avoir un troisième.

I also know for sure it was used in Lemmings for levels up to 5 screens-wide (that would be about half the conventional RAM available on an IBM-PC, btw). 

Since the storage space on floppies was limited, levels weren't stored as a large bitmap, though: they were stored as a set of 'paint brush A at location (X, Y)' plus a set of (deluxe paint) brushes -- think of them as 256-color stamps The level editor calls them 'rocks' and reports how much are used so far. Some brushes may even be used as 'clearers' instead of 'drawers', exactly like what is seen in the New Super Mario Bros. level editor.

A true puzzle for those who later had to port DMA Design's success title to tile-based consoles of the time, if you ask me. But it allowed complete freedom that was precious to design appealing and challenging levels. Lemmings 2 was later developed based on an editor that aligns everything on a grid to ease porting to Nintendo and Sega (?) consoles, and while it had charming new themes, I never felt them attractive the way the original Lemmings were to me.

A noter que traiter la map comme un gros bitmap n'implique pas forcément de stocker la map comme un gros bitmap. En fait, pour la majorité des machines d'avant le CD-ROM, ç'aurait été tout simplement impensable même avec des niveaux de taille modeste comme ceux de Lemmings (5 écrans). Pour faire tenir tout ça sur une diskette, les gens de DMA design ont construit leur niveaux sur base 1) d'une palette de 'brosses' (des bitmaps de taille quelconque provenant de Deluxe Paint) et une série de commande 'ajoute la brosse A aux coordonnées (X, Y)'. L'éditeur permettant d'ailleur de garder une idée du nombre de 'coups de pinceau' utilisés jusqu'ici dans le niveau.

Bon, vous l'aurez compris, cette technique bitmap pour les données d'un niveau marche surtout bien pour les jeux où l'environnement est complètement destructible (chose qui devenait moins vrai avec Lemmings 2, prévu pour consoles 16-bit) et à un style graphique plus proche d'un Rayman que d'un Mario. D'une façon assez amusante (mais foireuse), c'est aussi ce qui était utilisé pour la version BASIC de Bilou's Adventures ;-)

Sunday, May 24, 2020

Toujours dans les pentes ...

Eh oui ... on a passé la mi-mai, et je suis toujours occupé à faire tourner ces tests automatiques des pentes et à corriger l'algorithme doslopes et son intégration.

J'avais introduit un mécanisme "maxMove()" pour pouvoir obliger l'algorithme doslopes à ne pas essayer d'aller plus loin que ce que l'animation permettrait. Dans le cas contraire, il y a un désaccord entre le mouvement vertical prévu et le mouvement effectué, ce qui conduit le personnage à finir en l'air ou encastré dans le sol.

S'en est suivi une refonte de selfmove pour qu'il puisse retenir s'il y a eu un maximum appliqué (et forcer le même maximum au moment de la lecture de l'animation). Puis je me suis rendu compte que tout ça ignorait superbement les décisions du type "il ne peut y avoir aucun déplacement là-tout-de-suite", avec à nouveau des déplacement incohérents. J'essaie de le corriger, et du coup tout bloque à la première pause.

Bref, J.L.N me demandait si j'avais déjà commencé à faire des niveaux pour la pyramide, mais j'ai l'impression que ce n'est pas pour tout de suite.

edit: trouvé. Un excès de zèle au moment de lire l'animation...
edit++: les tests passent! il était temps! Je suis dessus depuis la mi-janvier... et à 23 minutes près, j'y était encore en Juin!

Sunday, April 12, 2020

Mekappleman

Le rythme 'confinement' me laisse paradoxalement moins de temps pour bilouter que mon rythme habituel. Malgré l'absence des activités de fin de journée des enfants, malgré l'absence de trajets en voiture. Entre les repas qui sont rallongés (intervention parentale oblige), les ballades de santé qui se transforment en promène-le-gamin, les mises au lit plus tardives et les débuts de soirées amputés d'un jt pour se tenir un minimum informé ...

Bref, dans tout ça, j'ai quand-même fait tourner un pseudo-dumblador sur le terrain simulé pour tester les pentes du nouveau moteur de jeu. Et je me suis retrouvé bloqué. Dans certains conditions, le code de gestion des pentes le fait entrer dans le sol plutôt que de le faire monter sur la pente. En cause, le mécanisme qui permet de 'mettre en attente' un déplacement le temps que l'animation soit prête pour un déplacement (qui peut être de plusieurs pixels d'un coup).

Some claim they can start new hobbies while they are covid-confined. I had a hobby already before, but not having to drive the car to the office and back doesn't really give me free time. Just more time tending the kids. But well, I still managed to find some time to have mecha-blador walking in virtual level and try new slopes for the revised game engine. Then I got stuck...

From times to times, slopes-handling code would makes mecha-blador enter the ground rather than climb up the slope. The cause turned out to be the trick allowing to delay a move until the animation could follow it (which may require we move several pixels at once). The tricky part is that dumblador animation mixes move-to and delay instructions. Trying to validate fixes in such conditions doesn't feel appealing, so I instead picked back the Appleman, who's walking animation is using only MOVETOs.


Là où ça se complique, c'est que le DumbBlador mélange ce genre de déplacement et des pauses. Difficile de s'assurer que les correctifs fonctionneront pour tous dans ces conditions. J'ai donc ressorti un personnage à l'animation 'plus simple', inspiré de l'Appleman.

J'ai aussi revu le mécanisme qui capture l'évolution des personnages, me basant sur l'idée que si ça ne t'aide pas à trouver les erreurs, ce n'est pas un unit test. Jusque là, testme BasicSlopesTests pouvait juste me dire que ça n'allait pas, et à quelles coordonnées le meka s'était arrêté. Le reste, c'était à faire dans le débuggeur.

Now, if I want it to be unit-tests like, it has to be useful to identify what goes wrong. So far, testme would just tell me at what coordinates the meka-sprite ended up. Everything else was to be done in the debugger. A new GameObjectState class can now collect information about how coordinates evolve and and what 'debug signals' controllers sent to InspectorWidget. Granted, $/ffff0001 is crude, but I can tell it is sent by doSlopes, which failed with (-1, 1) motion. It could actually be post-processed by a PERL script.

C'est un début, mais c'est clairement perfectible. J'ai donc un nouvel objet GameObjectState destiné à capturer tout ce qu'il faut savoir du meka pour les besoins des tests (à la discrétion du programme de test) et un ensemble de ces objets en guise d'historique (plutôt que 2 tableaux de coordonnées et un de vitesses). J'ai pu aussi intercepter les rapports destinés au ChiefInspector (relai vers l'InspectorWidget) et les capturer dans les GameObjectState correspondant. C'est un peu  
laborieux à décoder, mais ça devrait réduire tout de même le travail de mise au point. Par exemple ce '$/ffff0001' est émis par doslopes qui échoue un déplacement (-1, 1) et 'S/0' que GobWalkerController reprend la main avec des vitesses nulles dans les deux directions.

Saturday, February 01, 2020

DoSlopes

Ok. Ajouter des pentes dans un jeu de plate-forme, ça devrait ne pas être compliqué, mais dès qu'on modélise son niveau à l'aide de dalles pré-définies (toujours les bons vieux tiles), ça cesse d'être simple. J'en ai déjà beaucoup parlé (cf. le tag) mais sans jamais vraiment (reality check: si) présenter la solution que j'utilise depuis Apple Assault: la fonction doslopes.

L'algorithme derrière doslopes travaille uniquement avec le 'hotspot' des sprites, et veille à garder ce point en contact avec le sol tout en appliquant un déplacement horizontal donné (qui peut s'étendre sur plusieurs tiles). Il utilise aussi une fonction annexe -- groundheight(x,y) -- qui donne la hauteur du sol pour un tile du niveau à partir de la base de ce tile. On y reviendra.

L'algorithme est constitué d'une boucle optionnelle (en jaune) pour atteindre le tile de destination puis une phase d'affinage (en vert) qui calcule la position du sol pour la position horizontale voulue.
En sortie, on obtiendra un  déplacement vertical correspondant au déplacement horizontal donné. A chaque déplacement intermédiaire, doslopes s'assure que l'on est pas en train de s'envoyer dans un mur à l'aide de la fonction cando -- la base de la gestion des interactions sprites/niveau.


Le principe de base, c'est de regarder la hauteur du dernier pixel d'un tile pentu juste avant de le quitter. Il peut avoir une 'hauteur = -8' ce qui fait positionne le joueur juste au-dessus du tile (comme on le fait normalement pour du sol) et permet du coup de regarder le bon tile pour la suite de la pente au tour suivant.

Tuesday, January 28, 2020

Meka-Bilou au rapport ...

J.L.N s'est beaucoup intéressé à ce que je faisais, ce.tte.s dernière.s semaine.s sur mon ordinateur. Il y avait régulièrement une espèce de mini-niveau (en ASCII art) dans un coin de mon écran qui accompagne les rapports d'erreur du système de test des terrains pentus. Je suppose que ça attirait son attention.

Difficile d'expliquer le principe du unit-testing à un louloup de 7 ans, mais il y avait aussi mon carnet avec le même "niveau" et un bilou-mécanique prêt à le traverser. "Oh oui, c'est le tic-tac-Bilou de parrain! Tu mettras aussi celui avec un réacteur?"

Il ne croyait pas si bien dire. Après avoir reporté des paquets de coordonnées sur un schéma du 'niveau' et analysé dans ddd à quel étapes de la fonction "doslopes()" elles correspondaient, j'ai déjà pu mettre en évidence (et corriger) quelques couacs. Le fait par exemple que doslopes n'indiquaient "on est sur une pente" que si le dernier tile visité est pentu. Malheureusement, ça signifiait un glitch vertical au moment de passer sur le 'sol plat' à la fin d'une pente à plus grande vitesse parce que le code du GobController qui fait appel à doslopes pensait devoir "éviter un sursaut brusque"...

"Will you also put the rocket-meka-Bilou in your test ?" asked my 7-y-o seeing my debug notes for the on-going slopes unit test. He was closer to the reality than what the clockwork-Bilou sketch suggested. The virtual-meka-tester is now zooming at 8 pixels/frame and no longer detects it should start going down. But then the GobController in charge of 'walk-on-the-ground' detects there's no ground anymore and triggers a FAIL event. It should trigger a state change according to the state machine, but MekaBilou has none.

Now here's the catch: the GobController has not cancelled its move, and over every next frame, it will start flying off (so much for the little wheels I'd drawn) and only the 'killer' tiles I put in the sky can stop it. I'll need some more code to interupt tests when unexpected state transitions occur, apparently.

Mais le plus curieux m'attendait encore. Lancé à presque 8 pixels / frame voilà Meka-Bilou arrivé en haut de la pente, et par un malencontreux concours de circonstance (plus un p'tit bug), il ne détecte pas qu'il devrait redescendre. Le code du GobController, lui, constate bien qu'il n'y a plus moyen de se déplacer normalement sur le sol et génère un FAIL (qui doit normalement provoquer un changement d'état dans le comportement du personnage). Sauf que, désolé, les amis: meka-Bilou n'a pas de machine d'état, et le contrôleur n'a pas annulé le mouvement (il était possible: on ne rentrait pas dans un mur). Du coup, rien n'empêche le même mouvement d'avoir lieu au(x) cycle(s) suivant(s). Faisant fi de ses "roulettes", Meka-Bilou s'est mis à traverser le ciel à toute allure jusqu'à ce qu'il soit stoppé net par un 't'as rien à faire là' qui met fin au test.

Un peu de code en plus, et je peux à présent capturer les transitions 'on fail' dans mes cas de tests et tout arrêter quand ça se produit. Et ça me fait penser que, quand j'aurai réussi à importer le comportement des applemen et autre dumbladors dans mon environnement de tests, ça pourrait être intéressant de marquer certaines portions de tests plus sophistiqués comme "interdiction de tomber ici", "interdiction d'atterrir là" ...

Sunday, January 12, 2020

un p'tit pas de plus pour LEDS

 Bien. J'ai donc un éditeur de niveau capable de faire l'affichage des niveaux même quand j'utilise le "nouveau" format. Y compris pour les blocs spéciaux. J'ai encore un peu de travail à faire dessus (notamment faire en sorte que les boutons à droite de l'éditeur ne s'incrustent pas dans le niveau chaque fois qu'on les invoque), mais c'est plutôt bien parti.

Avec tout ça, et l'introduction de nouveaux types de pente, je me dis qu'il deviendra bien utile de pouvoir faire des cas de tests sur des micros-niveaux, avec un système capable de détecter qu'on arrive bien à passer les "obstacles" quelques soient les variations de conditions initiales (légèrement plus à l'ouest, vitesse qui n'est pas un multiple entier de pixels, etc.) sans pour autant entrer en contact avec les blocs supposés en dehors du trajet.

If I want to be effective with introducing more complexity in the sloped-ground engine code, I think I'll have to add some more unit-testing. I've learnt the hard way that sub-pixel initial position, interaction with step-motion and animation quirks can lead to broken slopes code, and that debugging that takes lots of time because it needs parts in-engine breakpoints and parts in-debugger breakpoints.

A micro-level with a few simple slope scenarios, easy control on the initial state that can be automated should help a lot. And help is welcome. Plus, it makes support for those slopes independent from Level editor updates, which is welcome as well.


Si ça peut sembler une perte de temps, vu que je n'ai encore aucun moyen d'ajouter des nouveaux types de pentes dans LEDS, ça ne serait peut-être pas si mal. En plus, dans un niveau comme celui de *deline, il est assez inconfortable de vouloir vérifier si la logique du code de gestion des pentes fonctionne bien comme prévu, parce qu'on est en permanence interrompu par ce que les autres "personnages" du jeu font.

Ça pourrait aussi être l'occasion d'introduire un truc qui me titille depuis un moment: une série alternatives de constructeurs pour les objets principaux du moteur de jeux (déclarateurs de blocs spéciaux, états du comportement des personnages, zones de collisions, etc) qui ne passent pas forcément par une analyse de bloc de texte ... et seraient donc un premier pas vers la possibilité de "compiler" du gobscript en code C++ (ou assembleur 68000 ?)

tempting to use that as an excuse to introduce an alternate set of constructors that could build objects without necessarily having to rely on text parsing. Like state.Using<GobWalkerController>("") rather than relying on "using walker;" processed by GobState::parse(). That would be handy for GBA or 16-bits ports of the engine, but it isn't easy to achieve with the current codebase, unfortunately. The core reason being that classes for the controllers are encap~insulated into a separate .o that was meant for 'dynamic linking' with the engine 'happily ignoring they even exist', except through a std::map of factories that can be invoked by controller names.

Thursday, January 09, 2020

Pentes et JumpThru

Je me rends compte qu'il y a un truc à propos des pentes que je n'ai apparemment pas convenablement expliqué sur ce blog. Voyez donc cette "capture d'écran" de l'éditeur de niveau avec les types de blocs qui sont révélés en bleu transparent. Il est assez facile d'y voir les zones considérées comme solides, et même la petite pente au niveau de la souche d'arbre (allez, je vous la marque en vert flashy). Puis il y a un troisième élément qui revient assez souvent, ce sont ces lignes horizontales, notamment dans la branche (je vous les marque en rose ou en jaune, petits veinards ;)

There's something I seem to have missed to explain about my implementation of slopes. Let's check this screenshot of the level editor, with the tile types revealed. Some areas are obviously solid (covered with fully-painted overlay squares). Some are slope (okay, I'll paint them in flashy green), and then there's a third recurrent item, identifiable with horizontal stripes (like the one painted in pink, on the tree branch).

Those striped tiles are 'jump-through' platformes, helping the player to climb up structures in the level. They are simply implemented by changing the "world flags" that we're looking for depending on whether we're moving up or down, by acting like ground if we fall down, and like air if we jump through them.


Elles correspondent au type "jump-thru", qui a la particularité de laisser passer ce qui monte mais pas ce qui descend. Aucune magie là-dedans: c'est simplement le "GravityController" qui ajuste le test effectué selon qu'on monte ou qu'on descend. Ce type agit aussi comme du sol sans agir comme un mur: si on arrive dans la branche par le bas avec une vitesse horizontale, on la conserve.

Là où ça devient intéressant (ou louche comme une passoire sans trous), c'est pour les blocs "jump thru" qui sont marqués en jaune. Non, il n'y a pas de passage secret, et on est jamais sensé sortir de la souche.

Now, there is another location with yellow jump-through blocks. This is no sweet secret passage. This is directly linked with the slope nearby. You see, depending on the controller used for a given state, we can decide to check for CAN_MOVE_THROUGH or CAN_FALL_THROUGH. And while we're walking along a slope, we don't try to fall.

Imaginons un instant qu'on ait un personnage occupé à monter cette pente. Il a un point de contact (en vert flashy) qui doit rester sur la pente, et une zone de collision (en bleu). Pour qu'un déplacement soit valide, l'ensemble des blocs sur lequel la zone de collision arrive doit posséder les bonnes propriétés -- par exemple "on peut passer à travers" ou "on peut nager dedans". C'est le principe de la fonction cando() dans mon moteur de jeu.

Mais dans le cas d'une pente, on voit clairement que la zone bleue peut "déborder" sur les blocs juste à-côté des blocs-pente. Si on les rends solides comme des murs, le personnage restera bloqué en bas de la pente... d'où la réutilisation des blocs au-travers desquels on sait tout faire sauf tomber.

(C) The UnDisbeliever
why we need more than slopes & blocks.
If we had instead a solid block at the end of the slope, the character would get stuck. It was perfectly detailed in the Undisbeliever's recent blog post on his SNES engine tile collision description. I tried to show the same with the zoomed screenshot, with a 'hot spot' in green, the 'cando(MOVE_THROUGH)' area in blue and the intersection of the jump-through tiles and that area with some pink-ish pixels.

I guess we could also have just used empty tiles for most of the rest of the slope (but the top, that is). I suppose I grew the habit of using jump-through tiles under slopes so that monsters work properly, especially those with check-points. If not, when they'll check a few pixels away from their hot spot whether there is still ground to walk on, they would wrongly consider they should turn back.

Ils permettront aussi qu'un personnage dirigé par une "IA" qui sonde la map autour de lui ne pense qu'il arrive au bord d'une plate-forme en trouvant un tile "creux" quelques pixels sous son point de contact.

Voilà, j'espère que c'était intéressant ;)


Wednesday, January 08, 2020

L'encodage de la physique du sol

Bon, j'ai pris un peu le temps de cogiter à ce dont j'ai besoin pour les nouveaux types de sols dans mon moteur de jeu. Je sais que je veux plus te types de pentes et plus de types de physiques, mais en relisant mes notes sur les pentes pour comparer ça à celles de Ishisoft, je suis tombé sur un os:

Si je veux des tapis roulants qu'on ne sait pas traverser, il me faut des tiles "physiques" avec la propriété "IS_WALL". Ou plutôt sans F_PLAYER_THRU. Cette propriété va être testée sur l'ensemble de la zone que le joueur (ou un ennemi si on regarde F_MONSTER_THRU) voudrait occuper après son mouvement.

I took some time to make a list of those 'special grounds' I'd like to have in my game engine. More slopes, that was an easy one. But while I was comparing my plan to Ishinsoft's tigsource post, I noticed a potential issue.

If I want to have conveyer belts that act as solid structures, I'll need physics-changing tiles that also have some IS_WALL property. Well, actually, that doesn't have F_PLAYER_THRU, since the 'cando' system allow move to some area only if a set of properties is true for all the tiles covered by this area.


Mais si je veux des plate-formes glissantes en jump-thru, il me faut des tiles "physique du sol modifiée" sans la propriété "IS_WALL" (donc avec F_PLAYER_THRU, cette fois-ci). Jusqu'ici, comme la physique "glace glissante" et "tapis roulants" sont dissociés, je pourrais m'en sortir en définissant aussi les propriétés de collision pour chaque type de physique.

I also want slippery jump-through ground, which requires physics-modifier tiles that act as plain air if you jump through them (so with F_PLAYER_THRU set). Hopefully, slippery ice and conveyer belts are distinct things, so I could just have per-physics-tile properties set and we'd be done.

Les ennuis commencent si je veux un mur solide avec des pentes et une physique particulière: à l'intérieur de la structure, il faut que des tiles avec 'F_PLAYER_THRU' pour qu'il n'y ait pas de murs cachés dans la pente. (Un vieux truc mais toujours indispensable avec 'cando' et ses flags) et sur les bords de la structures, il faut que je repousse les assauts des sprites trop entreprenant pour les obliger à attendre le sommet avant de s'installer.

ça me consommera 2 types de tiles physique. Ce n'est pas forcément un cas fréquent et on aura pas nécessairement besoin de dédoubler tous les types de physique.

Things really get nasty when you consider structures like Aladdin's sandfalls (we should have some in some Sonic games too). There are physics tiles (pushing the player to the left) involved and slope tiles involved. Some physics tiles will have to let the player thru, else they will get stuck into the slopes (that mostly happens in the middle of the structure), and other physics tiles will have to act as walls at the edge of the structure, so that aggressive speedrunners don't get through. I don't see shortcuts to workaround the situation: we'll need two separate tiles type here, despite they'll have the same effect on physics (push player to the left).


Du coup 1) il faudra regarder sous une pente pour avoir ses paramètres de physique et 2) ce sont les contrôleurs qui conserveront les informations dont ils ont besoins. Le coefficient de friction dans le contrôleur 'momentum' qui traduit les impulsions en vitesse et 'stopper', le fait d'être poussé dans un sens ou dans l'autre dans 'walker' et 'needground' etc. (on y reviendra).

Et pour afficher tous ces types (pas loin de 250 en tout), il me faudra plus que mes 16+n petits tiles utilisés actuellement. Je n'ai pas la place pour mettre tout ça dans la "zone libre" de caractères prévues pour les widgets (128 caractères, juste après les codes ASCII): je tombe dans l'espace prévu pour les écrans. Mais j'ai assez de place après les écrans. (oui, ça n'a rien avoir, mais j'avais besoin de le garder sous la main).

Friday, December 27, 2019

Leilani slopes

The tweet popped while I was doing mental checks on my revised tiled engine, and especially how it would handle more slopes than the previous one: Ishi(soft) finally decided to introduce slopes in Leilani's Island. And while I was looking for convenient ways to encode more than one array of tile_height[type][offset], I was surprised that his approach wasn't not requiring any such array.

Bon, le tweet est tombé comme un pavé dans la mare pendant que je réfléchissais aux problèmes que le mélange des pentes et des blocs à la physique modifiée risquait de poser: on peut aussi faire des pentes sans avoir pour autant besoin de modifier son moteur de jeu pour y mettre des fonctions du genre "doslopes". C'est ce que nous propose Ishi dans sa prose décrivant le moteur de jeu de Leilani's Island. Et que faire d'autre si on ne veut pas simplement faire un clone de Mario 1? Eh bien par exemple générer à la volée des "ascenseurs" sur lesquels les personnages vont se déplacer et dont la vitesse verticale donne l'illusion qu'on suit bel et bien la pente.

(C) Ishi
Instead, the world-collision engine would detect sloped ground and generate sort of "lift" objects that are tied to a specific X location, but adjust their Y location to the X position of the character that triggered them. It allowed enough flexibility to make me think twice before dismissing the approach. One of the benefit of the approach is that the code running the is-there-ground check still happily believes the character have solid ground all around them:

This fixes the problem with enemies turning around on slopes. When they do their collision check, they find solid ground in front of them - because as far as they're concerned, they're on a flat surface.

There was one major restriction, though: Ishi decided to go for a single type of slope (22.5°) where X-to-Y offsets function can easily be computed by shifts and additions. But I personally want more. More angles and some curved ground as well. We could still have that in Ishi's engine, but it would require picking a tile_height[offset] array according to the slope type and pass it to the "lift" object.


C'est séduisant, c'est sûr. Mais ce n'est pas allé sans mal pour autant, et à la lecture de tous les "corner cases" qu'il a dû prendre en compte, je ne suis pas certain d'avoir envie de jeter mon implémentation actuelle pour suivre son exemple. Entre autres, une des choses qui permet à Ishi d'éviter de devoir utiliser plusieurs types de pentes, c'est qu'il s'est limité à un seul angle (22.5°) pour lequel on peut facilement déduire la position verticale de l'ascenseur à partir de la position horizontale du personnage à l'intérieur du bloc. Mais moi je veux plus de variété. Il faudrait donc du coup plusieurs comportement d'ascenseurs et un code qui produit le bon à partir du type de pente que l'on a rencontré.

And the approach apparently has its own corner cases that need to be addressed, sometimes with special edge-of-slope types. The approach I'm using so far has its own corner cases, which were mostly resolved by using jump-through tiles (which are also walk-through) next to sloped tiles to avoid introducing artificial walls within the slope. That's precisely what needed more thinking when introducing "physics modifiers" tiles.

Well, anyway, don't miss the full post on Ishi's tigsource thread. It's very detailed and informative.

PS: it feels a bit odd to write a post about someone else's work here, but I've kept re-visiting the browser tab with the forum with the hope that I wouldn't forget it if there's anything important to find there ... for one week or so.


 Edit: apparently, there's one thing I understood terribly wrong: there is no 'lift object' generated. Just a collision rectangle returned by a function. A different rectangle every frame, but its lifetime is restricted to the duration of Character::think().

Sauf que ... en réalité il n'y a pas "d'objet-ascenseur" dans l'implémentation. Chaque fois qu'on demande au 'monde' s'il y a collision avec le sol, il nous renvoie un rectangle adapté à notre position. Ce n'est pas un objet-plate-forme qui aurait une durée de vie dynamique, juste une valeur de retour qui nous sert de contexte pour les prochains tests le temps d'un cycle de calcul des mouvements du personnage.