Wednesday, January 31, 2024
Hello 2024
Monday, January 29, 2024
Over-engineered ?
Parce que dans mon cahier bleu, avec les croquis de level design que j'ai l'intention d'utiliser pour Bilou's Dreamland, l'eau est bien présente, mais je n'ai jamais un "niveau aquatique" à proprement parler. Ce qui motive cette recherche sur les mouvements dans l'eau, c'est juste le fait que la berge est trop haute dans la démo. Et donc, garder une mécanique simple "JUMP" à la surface = sauter hors de l'eau et sauter hors de l'eau près de la berge = sortir de l'eau serait impeccable si j'éditais un rien l'écran-démo pour que sa berge soit un tile plus bas ^^" (et éventuellement que j'augmente de 15% la hauteur du saut depuis l'eau, actuellement défini au doigt mouillé)
Pendant que je testais cette nouvelle map avec sa nouvelle "berge" moins haute, je me suis rendu compte que depuis qu'on sait nager à gauche et à droite, les remous de la cascade ne nous entrainent plus vers le fond. La flottaison est trop efficace et le centre de Bilou n'entre plus dans la zone qui le tire vers le bas. J'ai un peu chipoté, mais heureusement, il suffisait de permettre à Bilou de "tomber" aussi dans cette zone pour que tout rentre dans l'ordre (enfin, je pense)
Tuesday, January 23, 2024
Input Buffering.
Tout est parti d'un tweet de Case Portman, auteur du jeu "Flynn, Son of Crismon" dont je suivais le développement dans lequel il nous expliquait en quoi le "jump buffering" est fondamental dans tout jeu de plate-forme. Un nom un peu barbare pour les francophones qui consiste plus ou moins à faire du voyage temporel avec votre manette de jeu.
Quand le joueur est au sol et qu'il fait sauter son personnage, on s'attend à ce que le saut soit pris en compte à la frame exacte où le bouton est enfoncé. Pas trop difficile. Supposons que le joueur veuille rebondir sur le sol en finissant un saut. Il sera assez fréquent qu'il appuie sur le bouton de saut légèrement avant de toucher véritablement le sol. Son intention est de rebondir. Le code peut faire deux choses: soit ignorer la demande de saut vu qu'on est en l'air, soit effectuer un saut dès que l'on atteint le sol. Et clairement, les jeux qui suivent la première approche sont désagréables pour les joueurs.
It's a nifty little feature that will add *a lot* more flow to your game. This can also be applied to almost any action! Shooting, melee attacks, dodge rolls, even menu selection.
Don't let the name frighten you: what it truly means is that you're time-traveling with your DPAD, pretending than your jump button presses did not happen when they did. If time was a photoshop/gimp layer, then jump buffering would be something like "snap to guide". He went on with a detailed and beautiful gif animation showing how to achieve that and make the game more tolerant on the case where player is about to land on ground and press the jump button in one of the last few frames mid-air. Many 8-bit games failed to do so and feel unfair when played.
Dans mon moteur de jeu, tout celà est pris en compte par le contrôleur "dpad
". Pour chacun des boutons, ce contrôleur retient si le bouton était déjà enfoncé lors de la frame précédente, et pendant combien de temps encore il doit être considéré enfoncé. La fenêtre de temps autorisée (6 frames, soit 1/10eme de seconde) sera la même pour tous les boutons. Je me suis refait un petit schéma de la manière dont c'était géré parce que je l'avais complètement perdu de vue. "to[i]
" sur ce schéma, c'est le timeout associé à un bouton donné.
Quand on définit "MOVE + FOOT
" pour l'état "flotter" (dans les airs, façon super-cape de SMW), ça signifie que le DPAD et les boutons de sauts ne passeront jamais par "decrease timeout". l'état "chevauchée d'éponge" évitera quand à lui de faire expirer "MOVE + HANDS
". Et pour tous les états où on a rien précisé, c'est le DPAD et la gâchette droite (qui sert de déclencheur pour la course) qui seront maintenus.
using dpad(MOVE|FOOT)
' for the FLOAT state, where it prevents the press to expire while floating.
Monday, January 22, 2024
How long before we play ?
Je n'ai pas vraiment envie de jouer au gestionnaire de projet pendant mes temps libres. Mais voilà, la question a été posée: "dans combien de temps est-ce qu'on pourra jouer" (à Bilou Dreamland, je présume). Alors j'ai repris mon cahier bleu, celui des secrets, des niveaux et des idées de gamedesign. Et pour chaque niveau déjà esquissé, j'ai fait l'inventaire de ce qui me manquait déjà. Pour les 3 premiers mondes prévus pour le jeu. Je n'ai encore aucun niveau pour le 4eme (ni aucun pixel, d'ailleurs). Chaque case à cocher sur cette map géante, c'est probablement 1 mois de hobby. Probablement 3 pour les cases rehaussées de bleu. Faites le compte ;-)
- [done] faliciter la sortie de l'eau
- [done] animer les chutes et les pentes de sable
- [done] Appleman en CompoundGob
- annoncer la démo aussi sur #retrodev, la communauté francophone de Fei, Kannagi, Banshaku et les autres.
Mais la bonne nouvelle, c'est que certaines de ces cases ne sont pas indispensables pour avoir une version "jouable", c'est à dire où on puisse atteindre la fin des niveaux. Les PNJ, par exemple, sont supposés être optionnels tout comme Yoshi était optionnel. Parfois, une substitution par un élément déjà existant pourrait s'envisager. Certains niveaux de la school zone seraient mieux avec des lattes en guise de tremplin, mais déjà jouables en utilisant les gommes rebondissantes de School Rush, etc.
Sunday, January 21, 2024
De l'eau par ci, de l'eau par là ...
Tentons donc de nager. La première chose serait de pouvoir stabiliser Bilou à la surface sans l'immobiliser pour autant. Ce n'est pas impossible parce que j'ai une bande de 2 tiles à la surface qui est à la fois de l'air et de l'eau. Une ruse pour que Bilou-qui-coule puisse succéder à Bilou-qui-tombait et que la dernière position valide pour l'un soit aussi une position valide pour l'autre. Bilou-qui-flotte à la surface devrait donc éviter d'entrer dans une zone qui n'est que de l'eau ou dans une zone qui n'est que de l'air ? Il lui faudrait pour ça un contrôleur dédié ?
props: 0fc8 | props: 8fc4 | props: 8fcc | props: 8fcc |
using swim | using gravity | using gravity | using float (?) |
Wednesday, January 10, 2024
Nager mieux
Il y a un élément qui m'empêche encore de faire une nouvelle démo avec les améliorations de l'an dernier: c'est difficile de sortir de l'eau. Je m'en suis rendu véritablement compte en laissant un peu la démo actuelle entre les doigts de J.L.N ... Je m'étais donc donné la mission d'essayer de faire fonctionner ça pendant le congé, mais avant même le premier essai, j'ai compris que ça n'irais pas comme je voudrais. Parce que pour qu'on puisse "jaillir de l'eau si on a appuyé sur le bouton de saut près de la surface", j'ai besoin de pouvoir mémoriser que ce bouton a été enfoncé.
ça pourrait être réglé avec une peu de tuning sur l'input buffering, j'imagine. Mais comme j'ai aussi prévu de passer à un autre système de nage dans lequel Bilou reste dans l'équivalent d'un dash sous-marin pendant quelques frames quand on a appuyé sur "pieds", ce serait naturel que l'on jaillisse automatiquement si on est dans cette phase de dash. J'ai donc passé deux petites soirées à faire des animations dans MEDS pour que notre brave Bilou brasse mieux.
Manque de pot, une animation supplémentaire s'est invitée dans le fichier. J'ai prévu 4 "pages" d'animations pour bilou.spr, mais cette nouvelle animation est sur la page 7, décalant toutes les nouvelles animations
- code avant la nage avec les sprites avant la nage:
- démarrer la pyramide: ok
- passer de la pyramide à l'école: ok
- sortie de l'école: gros crash.
Une petite modif' plus tard (là, ce soir) pour éviter que l'animation excédentaire soit à la fois sous le contrôle du jeu et sous le contrôle du reste (sinon, ça fout un chaos digne de Jurassic Park dans le gestionnaire de mémoire) et j'ai de quoi commencer à utiliser toutes ces jolies nouvelles animations. Sauf que ça fait bizarre de voir Bilou essayer de rejoindre la surface la bouche grande ouverte (animation "super jump" recyclée) puis fermer la bouche une fois qu'il arrive à l'air libre :-P Edit et il va falloir que j'en fasse une ou deux de plus pour permettre au joueur de quand-même avancer. Sur base des animations de Fury ? Pourquoi pas ...
Edit: one more thing ... Dash-swimming might be fun, but I also need something to use when player just navigates with the DPAD. Maybe Fury's swim sheet will be the template I need for that ...
Saturday, January 06, 2024
Raycasting ?
The awesome part is that depth effect added with the horizontal planes, of course. That is about adjusting both X and Y scrolling registers and that will need maths to explain properly, using some pre-slanted texture as shown in the ripped contents of DKC2.
First immediate thought after I realised that it could be more than welcome in the pyramid level of Bilou's Dreamland was that it is actually an instance of the raycasting algorithm. Yeah, that infamous thing that turned ID software away from my beloved Commander Keen and sent them trashing the whole era of 2D platformers with first-person shooters. No wonder why, despite my affinty for maths and software optimization, I never ever felt tempted to code one myself. But hey, do it vertically instead of horizontally, and this is precisely what we need to decide whether to show floor, ceiling, wall or cut-through floor-to-ceiling structure.- the background scene must be structured along a 2D grid. I'd say 64x64 pixels would be fine by me
- For every scanline , we will have to step through the grid, one tile at a time, essentially checking whether one more "depth" step takes us farther than one "height" step in our case.
- We need a direction vector associated with every scanline. That implies 192 square roots per frame, but hopefully, those computations are the same for every frame: they depend on the camera-to-screen distance and define angle used to trace through each pixel. They could be pre-computed even at compile time and stored in a look-up table.
So for every scanline Ys of the screen, (DDA) raycasting will give us a pair of (Zw, Yw) coordinates in the world that is shown on that scanline. Most likely, we don't want to compute the distance to Zw, Yw nor use that distance to adjust scrolling speed. Instead, Zw should directly be used to decide the scrolling speed. Well, unless we're on a horizontal surface, that is. Well, I can't help thinking this is overgeneral and overkill, despite übercool.
The trick in the SNES DKC2 implementation is that we have pre-rendered floors and ceilings. They already feature some depth-of-field effect. if you use them as-is. And being 440px wide, they're significantly larger than the screen (256 pixels iirc). Where does that 440 value come from ? Well, I guess this is screen_width + pattern_width, as the flat stripe shows a repeating pattern of 184 pixels. So whereever you are in the pattern, you can always have at least one full scanline ahead. The most distant line of the ceiling has only 96 pixels between two patterns, matching exactly the size of the tiled background wall. That means if the 'front' part is moving exactly at 1 pixel / frame, the tiled part should be moving at 0.52 pixel/frame so that Thales theorem is satisfied.
That size difference also tell us how far away the tiled parallax layer should be from the 184x32 parallax layer (and thus how deep the floor/ceiling objects are): they're as far from each other as the 184x32 layer is from the "camera"
s16 xref = REG_BG1HOFS;
s16 yref = (offset >> 2) % 192;
s16 xamp = xref + xref / 2;
s16 yamp = yref + yref / 2;
int ytrigger = 224 - yamp;
int btrigger = 192 - yref;
int i, j;
for (i = 0, j = 0; j < N; j++, i += 2) {
if (j > ytrigger && j < ytrigger + 64
|| j > ytrigger + 256) {
data[i] = xamp;
data[i+1] = yamp + 30;
} else {
data[i] = xref;
data[i+1] = yref + (j >= btrigger ? 128 : 64);
}
}
That doesn't make the 3D-effect of DKC yet, but at least it gets me synchronous parallax with a single hardware layer.
Next step: find the zones where ceiling and floor should be shown. And there, trials and errors became too complicated to figure out. Hopefully, I found a way to analyze the problem with maths. Most of what's computed is derived from that "yref" value, which is normally the input from camera position. What I need to do is use that yref as horizontal axis and study how "triggers" that define top or bottom of areas evolve, cross and areas overlap appear or disappear. And once the (simple) maths were written, it took only half an hour to write the code to do it right.With this graphics, that's the best I can do... let's see how it follows up once I have dedicated background.