Showing posts with label CONVEYED. Show all posts
Showing posts with label CONVEYED. Show all posts

Saturday, August 23, 2025

Une pente qui tombe à l'eau

I had tiles that push you along a slope. They've been shown in the sands mostly, but more importantly, they've been tested in a setup where there's plain ground at the bottom of the slope.

Here, I'd like to use water falling down a slope as a way to implement a one-way region somewhere in a cavern of the green zone. But it didn't quite worked as intended. What makes Bilou slide down in such a setup is that a test-point queries the tile number under his feet and use that to look up a physics values array. But once you get down the hill, the hotspot gets in the air, and nothing finishes to push you out. 

Il y a sous l'arbre creux une sorte de pente rendue glissante par une cascade sous-terraine. Mon frère l'avait utilisée pour créer une sorte de sens interdit entre deux morceaux du niveau. Alors j'ai déjà des pentes, donc ça aurait dû être une simple formalité, mais comme vous l'aurez sûrement deviné par le titre, ça n'a pas été sans mal Premier couac: en voulant rendre ce "tapis roulant" plus rapide que les chutes de sable, je suis sorti des limites d'un tableau, ce qui a eu pour effet d'appliquer l'effet geyser à l'ensemble du niveau ^^".

Plus contrariant, une fois arrivé en bas de la pente ... rien. On ne tombe pas dans l'eau, on reste juste là, sur le rebord. Parce que c'est le pixel sous le *milieu* de Bilou (le "hotspot") qui détermine si on se trouve sur un tapis roulant mais qu'il faut qu'il n'y ait plus rien sous lui sur toute sa largeur pour qu'il commence à tomber. Je dois donc rajouter un autre type de tile qui pousse comme un tapis roulant tout en étant pas du sol.

So I added another tile that behaves like air and keeps pushing you as if you were on sliding ground and put them at the bottom of the slope, but even that wasn't enough to get it working. It would take you as far away from the slope as you want, but then, once your hotspot is in plain air, you'll stop, happily idling in the middle of nowhere.

The part I was still missing was the F_START_FALLING property to my new tile type, something introduced so that we could land in slopes properly by having slope tiles letting you keep falling into them (until the hot spot reaches the ground) but not start falling through them when you're walking.

Il restait un dernier soucis. Un couac lié à un changement plus récent et qui fait qu'après avoir bien emmené Bilou au milieu de nulle-part, il ne tombe toujours pas. tout ça parce que j'ai donné au nouveau tile possède bien la propriété F_FALLTHRU, utilisée pendant que Bilou tombe, mais pas F_START_FALLING utilisée pendant que Bilou est au sol pour voir s'il pourrait tomber ^^"

With that new bit set, we finally have it: Bilou keeps sliding until there's no solid ground, and there  it cando(START_FALLING) and ends up in the water :P

There's one odd thing going on with the way the water slide is made as a conveyer, though. Conveyers just move you and don't affect your speed, and that's on purpose. But that means that as you're running full-speed against the water flow (if you manage to) and decide to jump, you suddenly move in the air at top speed while you were almost idle on the ground.

Maybe I should mimmic Sonic's water slide and make a dedicated "woah! I'm sliding down!!" state for Bilou as well.

But even then, when Bilou is walking against a conveyer, it is natural that he's walking faster than he's moving ... against water, we all know there's a force slowing us down, so it'd make sense to see his animation slowed down and pixel-fitting its motion against the water ... which would mean acting on his speed rather than shifting his position ... Meditate on this I will. 

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)


Friday, March 17, 2023

Conveyed ...

It's not a surprise (or it shouldn't) if there are waterfalls and sandfalls in the Three-rooms demo. And especially, if there is ground between two of them. I want my game engine to be able to 'push' you when you're on some ground, like it would if the ground was actually a conveyer belt. And it's been a few years now that using sand/water falls for that integrates better than mere mechanical belts in most levels. 

I have notes in my notebook for quite some times, too, about how the game script should indicate what those blocks do. They're not quite "special blocks" (that interact with collision code), but rather "physical types" that could be polled by frame-per-frame controllers.

I'm not completely satisfied with the proposed syntax, though. I'd rather use type %d {...} and is %s "%x" for level editor identification (like for block %d {} describing interactive special blocks), and I'd use props %x, like we already have for special blocks.

The truly innovative part is %s.%s = ... pattern to setup variables into controller factories. That one will be a bit more complex to get running, because the map of factories is actually managed directly by the GobState class, and I'd rather not re-inforce the Using... anti-pattern here.


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).

Sunday, January 13, 2019

Tile Engine Revision

Bon, j'ai une grosse révision de mon moteur de jeu en cours. Jusqu'ici, les propriétés du niveau étaient conservées dans les bits "palette" du niveau, soit 4 bits par mini-bloc de 8x8 pixels (les tiles, pour les intimes. Prononcez avec de l'aï comme dans light). Pour "School Rush" et ses niveaux de 16 écrans de large pour 1 écran de haut, on parle de 8KiB de données. Un niveau de Commander Keen (mettons la machine infernale des Shikadis) avec ses 1200x1200 pixels nécessite 22500 de ces tiles.

Much of my hobby time has been spent in tileset engine investigation since SchoolRush release. The current one has a few shortcomings which proved annoying during the " finish him " phase. Among other things, I want to stop using the palette bits for metadata, but it is still unclear how many bits per tile I can afford.
Most levels in schoolRush need a mere 16 screens, and consume only 8KiB with the current 4-bit-per-tile implementation. A Commander Keen level in comparison is 1200x1200 pixels, which would require about 22K tiles.


Pourquoi tous ces chiffres ? parce qu'au coeur de la révision, il y a le besoin de faire sortir ça des bits de palette pour pouvoir utiliser librement les changements de couleurs si utiles dans School Rush tout en permettant de nouvelles mécaniques de jeu. Jusqu'ici, j'ai pu m'en sortir avec des blocs spéciaux (bonus, pics) qui utilisaient les "codes couleurs" des 4 tiles qu'ils couvraient pour encoder un numéro suffisamment large. Mais quand on commence à réfléchir à faire de l'eau, des ventilateurs ou des tapis roulant, ce système devient bancal.

Of course, one of the goals for the revised engine is to enable multi-palette edition on both tile layers, but I would also like to increase the number of unique materials in the game. My attempts at having ice blocks, conveyor belts and flowing water highlighted that special blocks are not enough for all purposes.
One reason that makes it misfit is that special blocks only work for 16x16 blocks, not for arbitrary layout of 8x8 tiles or for transparent areas.


En particulier, il était basé sur le fait qu'un groupe de 4 tiles sont liées par les numéros de tile (en mémoire vidéo) utilisé parce que l'éditeur de sprites fonctionne comme ça. Impossible donc de définir un bloc-bumper sur un graphisme qui serait fait d'une moitié de gomme et d'une moitié de lettre. ça, c'est plutôt un avantage. Impossible aussi de rendre une pointe de crayon blessante si elle est sur 'l'arrière-plan'.

So from those figures, and given that the NDS has 4MiB of RAM, upgrading to 8-bit per tile should be possible. Granted, it could be nice to include some decompression techniques or some 16-bit era fancy techniques, but none of this seems to be mandatory right now. That's one of the strengths of the DS, imbo, that you can already make interesting games while keeping the engine simple.
And yet I know that at some point I'll wonder how such function could be achieved on a much simpler system - let's say a GBA, a SNES or a Mega-Drive... And should I have need for e.g. virtual tilesets, splitting away physics (meta) information from remappable graphics information would help.



Faut-il donc que je continue avec ce système (données 4-bit) sachant que je travaillerai maintenant sur un tableau séparé ? Puis-je me permettre une extension à 8-bit par tile ? et si oui, comment organiser au mieux les données ? Est-ce que ça m'impose d'intégrer de la compression ?

A mon avis, avec 22Ko pour une map type "Commander Keen" et sur une bécane qui dispose de 4Mo de mémoire principale, ça reste parfaitement jouable. Oui, c'est loin des techniques de trapéziste de l'époque des consoles 16-bit, mais c'est justement une des raison de choisir la DS à mes yeux: elle permet de se concentrer sur le jeu lui-même, avec des contraintes, certes, mais des contraintes simples à apprivoiser.

Wednesday, March 10, 2010

B_CRUMBLINGFLOOR

Somehow, I stumbled upon this re-implementation of the Manic Miner / Jet Set Willy engine in Visual Basic. I'm unsure on how I should implement crumbling ground in Bilou (or any block animation in response to character's presence). Let's see how a +20 year old game handled it...

Bon, je l'avoue, je suis intrigué par la quantité d'"éléments spéciaux" présents dans un jeu aussi vieux que "Manic Miner" contient, et la difficulté que j'ai à trouver une manière convaincante de réaliser la même chose dans mon moteur de jeu actuel. Du coup, j'ai mis la main sur une ré-implémentation du jeu en Visual Basic. Voyons donc comment les blocs-qui-disparaissent-quand-on-reste-dessus ont été gérés ...

' block type constants
Private Const B_CRUMBLINGFLOOR      As Byte = 2
Block types are encoded in a separate array, with one byte per 8x8 tile. One such type is "crumbling floor". Other block types include B_AIR, B_FLOOR, B_WALL, B_CONVEYOR and B_NASTY (two of them) and B_SPARE (for switches). Jet Set Willy only has one B_NASTY type, and the other one is converted into B_SLOPE. This is consistent with my own engine.

Carles a construit son moteur de Willy de manière à réutiliser les données du jeu telles quelles -- sans être le code original sous Z80, ça donne malgré tout une bonne idée de ce qui aurait pu être fait. En l'occurence, par exemple, la structure de chaque niveau est donnée par un tableau de la taille de l'écran, indiquant quels graphismes utiliser, mais aussi le type de chaque bloc entre 0 et 7.

B_CRUMBLINGFLOOR est celui qui nous intéresse. En cherchant "crumbling" plus loin dans le code, je tombe donc sur la fonction DoWilly (extrait ci-dessous) qui gère toutes les interactions entre Willy et le monde qui l'entoure. DoConveyors ne fait qu'animer des blocs, de même que DoGuardians se contente de déplacer les monstres. Ce sont les sous-fonctions de DoWilly qui détectent les collisions et altèrent le déplacement du personnage. Exactement le genre d'approche que j'utilisais dans Calimero et Bilou en QuickBasic.

What's interesting (for me :-) in Carles' approach of redoing Willy's world is that he operates directly from the tape file of the spectrum, so not only it has the "readability" of modern BASIC (i'm not that much into Z80 assembly ^^") but it still explains the logic of the original game (how switches, witches and stuff are encoded).
If (.mode = [eWalk]) Then
  If Not (WillyCheckFeet()) Then
    ' code for falling stripped out ...
  End If
  Call WillyCheckConveyor(keys)
  Call WillyCheckCrumblingFloor
End If
The DoWilly() sub shown above proceed step by step, handling interaction of Willy with every item "present" in the game. That is, we have DoConveyors or DoGuardians as well, for sure, but they merely perform animation and movement job. WillyCheckConveyor makes sure conveyor pushes Willy. That's the real job.
Let's thus have a look at WillyCheckCrumblingFloor just below ...


Voyons donc WillyCheckCrumblingFloor dont le code est ci-dessous. Après s'être assurée qu'il y a bien un bloc friable sous les pieds de Willy, elle va utiliser FXCrumblingBlock pour procéder à l'animation (décalage du tile d'un pixel vers le bas) et transforme les propriétés du bloc donné en "B_AIR" si FXCrumblingBlock lui dit de le faire à travers la variable f.
o = .x + (.y \ 8 + 2) * 32
For c = 0 To 1
  If (m_RoomBlock(o + c) = B_CRUMBLINGFLOOR) Then
    Call FXCrumblingBlock(m_DIBBack, 8 * (.x + c), .y + 16,
                          CAPaper(m_RoomData(o + c)), f)
    Call FXCrumblingBlock(m_DIBMask, 8 * (.x + c), .y + 16,
                          I_BLACK, False)
    If (f) Then
      m_RoomBlock(o + c) = B_AIR
      Call FXRect(m_DIBBack, 8 * (.x + c), .y + 16, 8, 8,
                  CAPaper(m_BlockCA(0)))
    End If
  End If
Next
FXCrumblingBlock
will not be shown here, because it's a pretty dirty function that mixes up game logic, tile animation and rendering engine commands. What I can tell you is that everytime it is invoked, it scroll downs the data of that specific tile, and insert a "blank" line at the top. Once this is done, it scans the whole tile again and sets the "cleared" flag (variable f) to false if it found any non-blank pixel. That's precisely what triggers transformation of B_CRUMBLINGFLOOR into B_AIR.

As the tile "crumbles" exactly one pixel per frame and Willy walks one pixel per frame, it means if you simply walk on crumbling floor, it will be completely crumbled when you're done walking.


Pour moi, c'est là que le bât blesse: FXCrumbblingBlock mélange allègrement rendu vidéo, manipulation des animations et logique du jeu. Je ne la reprend donc pas ici, sachez juste qu'elle décide si oui ou non l'animation est finie en parcourant les pixels qui viennent d'être édités. Si tous les pixels sont "transparents", c'est qu'il n'y a plus du tout de sol et on peut remplacer B_CRUMBLINGBLOCK par B_AIR.

Là-dessus, je vous laisse bras-dessous: il est temps que je prépare ma pitchounette à aller rechercher ma fée à Belle Ile. Ciao.