Thursday, December 02, 2021

The true Mario Jump

J'ai un vieux post, le premier qui raconte comment on peut faire sauter un personnage dans un jeu vidéo. Dedans, il y a une image où j'explique que la physique de SMB3 est un peu 'truquée' du côté des sauts. Mais bon, j'avais fait ça à la grosse loupe sur une vidéo youtube, alors que l'ami Upsilandre, lui, a du lourd sur le sujet: il a analysé en détail les mouvements de Mario et ceux de TinyToons Adventure pour voir s'il y avait eu des fuites de code source entre les deux jeux.

When I was writing one of my first post about having a character jump in a tile-based game, I had the surprise to see that Super Mario bros. 3 doesn't seem to use Newtonian gravity. Instead of having a smooth parabolic curve, there seems to be linear rise, parabolic climax and linear fall in a Mario jump. But I did that just by checking a let-s-play video on youtube. On his French blog, Upsilandre did a detailed comparison of SMB3 and Tiny Toons Adventure where he gathered all the impulse and gravity constants, so why not pay him a visit ?

Il pointe notamment une correspondance exacte pour les impulsions de saut. au centième de pixel par frame près. Troublant ? Eh bin, rappelons-nous: quelques paragraphes plus haut, il relevait aussi que les deux jeux utilisent le même format de nombre pour les vitesses: 4,4 bit

La vitesse dans Tiny Toon est encodée exactement dans le même format à virgule fixe 4b.4b que je n'ai vu que dans SMB3 (même pas dans SMB1), en général c’est plutôt du 8b.8b (voir du 3b.5b). [...] La même force de gravité faible de 0.06 tant qu’on maintient le bouton saut (comme si on était sur la Lune) et une force de gravité forte de 0.31 (retour sur Terre) quand on lâche le bouton ou que la vitesse verticale passe en dessous des 2 pixels par frame (donc quand on approche du sommet du saut). [...] [on va retrouver] strictement les mêmes 5 valeurs d’impulsion à la décimal prêt (respectivement 3.44, 3.56, 3.69, 3.94 et 4.00 ppf). La seule chose qui diffère c’est la valeur qui sert à caper la vitesse max de chute qui, comme pour la glissade en pente, est plus basse dans TTA.

Petite précision: cette histiore de 4,4 (ou 4b.4b selon la formulation d'Upsilandre) signifie que toute vitesse est exprimée en un nombre entier de 16 de pixel/frame. et on peut aller de -16 à +16 pixels par frame (mais personne n'ira jusque là). Du coup, 0.44 c'est 7/16, 0.56=9/16, 0.69=11/16 et 0.94=15/16. Déjà beaucoup moins surprenant. Notons au passage que la gravité lunaire de 0.06 pixels/frame² correspond à une simple décrémentation de la vitesse en 4,4bit. C'est 1/16eme. Tout simplement. Sur un processeur tel que le 6502, si on peut décrémenter plutôt que de soustraire 2, c'est toujours ça de pris.

The most interesting part is that the game uses two different gravity values. the 'ramp up' part of the jump uses the lowest possible gravity the game can deal with, of 1/16th pixel per frame². The switch to another gravity happens when we reach a vertical speed of 2 pixels/frame (or if the button is released), and we then go for 5ppf².

The impulse value may seem surprising, especially when expressed as decimal values like 3.69ppf. In fact, they integrate to nicely integer jump height expressed in Super Mario Blocks. What it means is that any game that wants to use the same gravity as SMB3 and a map made of 16-pixels blocks is likely to need the same impulse values as well.

Mais pourquoi ces valeurs-là ?
C'est le moment de ressortir un autre vieux post où on reverse-designait Super Mario World. Le game design forum y pointait que tout se mesure en bloc, dans le monde de Mario. Y compris la hauteur et la longueur des sauts. je ne serais pas surpris de découvrir que les impulsions correspondent aux vitesses qui permettent de franchir un obstacle de 3, 4, 5 ou 6 blocs le plus justement possible. Mais on a les valeurs, donc vérifions!

Voilà donc: selon l'impulsion initiale (v0), la hauteur atteinte (y2) au moment où la vitesse vaut 2 (et où on applique une gravité "normale" proche de 5/16 ou 6/16 ppf²). Avec en prime le temps mis pour y parvenir. Les valeurs 'magiques' correspondent à un obstacle de 4, 4.5, 5 et 6 blocs respectivement.

During the fall down, the game physics makes the terminal velocity kicks in very soon. As soon as 2 blocks high, actually. It may be an important part of consistent air control, giving you equal chance to hit RIGHT and reach a platform regardless of how long you've been falling down. The quasi-linear ramp up with super-small gravity does its best to mimic that for gameplay symmetry and ensures that we don't need speeds near 1 tile/frame if we want to jump up to 6 blocks high, as 1 tile/frame is almost warp speed for a 8-bit game engine.

ça, c'est pour la montée en gravité lunaire. Avec une gravité de 5/16, il faudra 22 frames pour tomber de 65 pixels de haut, mais 2 frames plus tard, on aurait déjà franchi 80 pixels. Enfin, ce serait en supposant que les programmeurs de SMB3 n'aient pas mis une 'terminal velocity' pour la chute libre en-dessous de 6ppf². En réalité, comme le fait remarquer Upsilandre, la vitesse de chute est limitée à 4+5/16 ppf par la "friction dans l'air". La mini-gravité en début de saut est essentiellement là parce que le gameplay veut une hauteur qui ne devrait pouvoir être atteinte qu'avec une vitesse de chute supérieure à cette vitesse terminale. Sans ça, il faudrait une impulsion de plus de 7+8/16 ppf pour dépasser 6 blocs de hauteur. On s'approche dangereusement de la limite de 1 tile / frame à partir duquel le code de détection de collision avec le niveau doit être radicalement modifié.

Au final, 

  • la descente de Mario est principalement linéaire. Il n'y a accélération que pendant les 2 premiers blocs (1 mario) de hauteur.
  • la montée de Mario n'est pas strictement linéaire, mais la gravité est si faible que la différence est à peine perceptible. Elle va surtout agir comme un compteur intégré de frames de montée forte avant de passer à la phase 'sommet du saut'
  • le changement de gravité au moment de relâcher le bouton offre une alternative sympa à ma solution de "forcer la vitesse de saut à -2 si on relâche le bouton [et que...]" 

edit: j'en profite pour sortir d'un vieux draft un autre schéma 8-bit d'upsilandre: la comparaison des courbes d'accélération de différents personnages.

edit+: en comparaison, Gomez ne sait sauter que par-dessus un obstacle de 2 blocs de haut mais peut s'accrocher à un rebord à 3 blocs de hauteur. Plus approprié pour un jeu de puzzle comme FEZ.

No comments:

Post a Comment

this is the right place for quickstuff