J'aurais préféré pouvoir dire "dans le code", mais à défaut, moddingwiki sur shikadi.net a pas mal d'info sur la structure des niveaux d'un de mes jeux fétiches: Fury of the Furries. On y apprend pas mal de choses qui différencient le code du genre de code que moi j'aurais écrit. Comme ce tableau de 5 entrées pour "une zone d'eau" puis 5 autres "téléporteurs". Les concepteurs du niveau devront se débrouiller dans ces limites. Les zones sont forcément rectangulaires, aussi. De mon côté, n'importe quelle zone de 8x8 pixels du niveau peut devenir de l'eau, mais ça aura demandé pas mal d'efforts de développement.
C'est presque encore plus contraignant que les lignes de code BASIC que j'écrivais dans les années '90. Mais avec cette structure statique, on *peut* se faire un éditeur de niveaux et éviter de devoir lancer le jeu après recompilation pour vérifier que les coordonnées correspondent bien à ce qui est prévu. Et le nombre de zones reste suffisamment faible pour ne pas ralentir l'exécution ... même probablement plus efficace qu'avec une liste dynamique puisque les emplacement en mémoire de cette liste sont connus dès la compilation.
There are days where I'm proud of the game engine I've crafted for Bilou on DS and days where I can't help feeling ashamed for how much overhead it carries for things that would have been made so much simpler in the 16-bit age. So from times to times, I go for another hunt on "how 16-bit games stored their levels" in hope that it would teach me how the engine manipulates them.
I knew Fury of the Furries had a level editor. We even have some icons and UI elements in an LBM file shipped with the game. For years, that's been all we had, but now the moddingwiki also features a description of .BIN files for each level in the DAT/ directory. You can see there that there had been some agreement between coders and designers that 5-{anything}-ought-to-be-enough-for-everybody. 5 exits. 5 teleporters. 5 regions where Green's rope wouldn't attach. 5 pools of water ... within each entry, you just find coordinates of a rectangular area.
I used to have IF XX > 150 AND XX < 270 AND YY > 160 THEN die()
in my QBasic edition of Bilou ... well, this is more or less the same. Except you don't get a script line, but you do get a tool that can adjust those coordinates with the mouse and have the engine load them precisely where the game logic will scan them. In comparison, my current engine will instead provide a get_tile_type(x,y)
function that looks up a value in a phys_level_map[WIDTH/8][HEIGHT/8]
and then let the state machine adapt from that ... where Fury.c could have if in_water_area(furry.x, furry.y) swim(furry);
Of course, that puts a strong limit on how many things you may have in every level, and thus on the size of those levels. It works fine with Fury because levels are quite small, although you may stay longer in some of them than in a Super Mario Bros level. This approach wouldn't have worked for Badman II-styled level, nor for a metroïdvania level design ... But for the maps my brother had drawn for Bilou's Adventures, even the 10 ennemies per level limit could have worked.
Un autre élément intéressant: la façon dont les blocs destructibles sont gérés. Vu la palette réduite, il n'y a pas de "portion partagée du tileset" ici.
Certain tiles in the image file have special meaning.
The tile at 0,1 is a coin. If the player touches this tile in the
level, they will gain a coin, and the tile will be swapped out for the
tile at 0,0 which is always empty.
The tile at 0,2 is an extra life. If the player touches this tile
in the level, they will gain an extra life, and the tile will be
swapped out for the tile at 0,0.
The tile at 0,3 is a time extension. If the player touches this
tile in the level, they will gain an extra 30 seconds on the clock, and
the tile will be swapped out for the tile at 0,0.
All the remaining tiles on rows 0, 1 and 2 in the map are destructible tiles.
Par contre, certains objets doivent systématiquement être à certains emplacements, comme la pièce (coordonnées 0,1), l'image à utiliser à sa place (0,0), les animations à afficher entre les deux. On a droit à tout une ligne d'images que Fury Rouge pourra grignoter, et les deux lignes en-dessous serviront pour les blocs un peu abîmés et très abîmés. Tellement plus simple que mes "mapanim" ^^"
They also had an interesting approach for animated tiles: the top rows of the tileset pictures (7 or 8 of them) have special meaning, as well as a few more on the leftmost column. You'll see the details in the quote above. An interesting alternative to the hard-coded crumbling floor logic of 8-bit titles like Manic Miner, if you ask me. It requires a good pre-production brainstorming to decide what sort of things we want/don't want in the level, but at least the level designer and the graphist are free to make anything crumbling / chunked by Red as they see fit without having to bother the coders with One More Change.
%20-%20ModdingWiki.png)
Mais ce qui m'a le plus surpris, c'est le nombre si faible de "sprites" autorisés dans le niveau combiné à la quantité d'informations associée à chaque sprite. Imaginez: seulement 10 ennemis par niveau !? Mais la raison, c'est qu'il n'y a pas ici "d'ennemi partagé" non plus: pour chaque ennemi, on définit les zones qui le déclenchent, les flots qu'il active/désactive (les interrupteurs et les ennemis sont donc gérés de la même manière). ça explique comment on se retrouve avec des comportements si différents pour le même visuel de monstre ... mais rien qui ne se comporte comme un koopa (avec un déplacement identique d'un bout à l'autre du jeu).
One even more interesting thing is the way sprites are handled. There are few of them per level, but they are given over 1600 bytes each. All the switches and doors and falling blocks that you push and then move into place, all this is achieved with those 10 sprites that can have up to 10 state each in the level.
Et ce que vous voyez là ne fait même pas directement partie de la structure "sprite", c'est un des 10 états associés à un des sprites, avec leur propre vitesse, leur propre type de déplacement,
10 states is rather few (pendats in SchoolRush have nearly twice that amount) but what makes it brilliant here is that these are no koopa-like sprites. Their state machine is level-specific and instance-specific. You don't craft a mummy that will turn back at the edge of any platform, you have an editor that let you say *this* mummy will cycle horizontally between *here* and *there*. Same for the falling bricks. or the doors.RSD Game-make state machine could only have one "next state" for each state. Pretty limited. Here there are at least 8 state transition conditions (most of them being triggers), some about "when that position is reached", other "when that water area reached that level", etc. And yup, you can "name" a water area or another sprite, or even a "current" area (flowing air or water) and activate/deactivate that when your sprite reaches a give state.
Even the elaborate level 2 of the Pyramid could be explained with pre-defined path alternating "move horizontally" and "falls & bounce", with triggers so that the rocks start falling... there's not even need for a multi-state orange box as I depicted: there is no "shooting" of rocks, just a stock of 3 rocks rolling left and 3 rocks rolling right, and each could unlock the next one when crossing some predefined trigger. (PS: j'en profite pour introduire le tag "level data", et attendez-vous à ce que j'essaie de trouver l'équivalent pour d'autres jeux)