Saturday, August 31, 2024

Meilleure branche

Voilà déjà un moment que j'y cogite: faire en sorte que la branche-qui-rebondit soit plus facile à utiliser. Dans sa version actuelle, on doit appuyer sur le bouton de saut au moment où elle nous propulse, pour bénéficier de la hauteur maximale. ça ferait bizarre de se voir propulser plus tôt, l'ennui c'est qu'entre les deux Bilou nous fait un petit rebond casse-pied.

A few notes on how the bouncy branch should be adjusted. But I wanted to try at least one thing before starting to discuss it: couple branch animation and motion so that the actual position of the "solid" hit box of the branch would change over time.

With that done, I could switch Bilou to a "land on soft ground" state where some button press are frozen in time, and when the branch will eventually reach the animation frame where a "throw up" collision can happen, player's JUMP input wouldn't be ignored.

Une des stratégies que je veux mettre en place, c'est donc de lui rajouter un état "atterri sur quelque-chose de mou" pendant lequel il attendrait que l'animation de la branche se termine et atteinge les images où on propulse Bilou.

J'aimerais évidemment que Bilou suive le mouvement de la branche pendant ce temps-là, et c'est en tentant de corriger ça que je me suis retrouvé bloqué par un gros bug pendant les vacances des enfants. Une de mes stratégies pour ce suivi, c'est de faire en sorte que la hitbox "solide" de la branche se déplace au fil de l'animation, un peu comme le faisait le petit ver jaune, premier personnage animé sur DS.

There is "animation/motion" coupling feature in my AnimEDS. It has been introduced so that feet stick to the ground ... But unfortunately, the animation of the branch wasn't drawn with that in mind. AnimEDS expect you to make your walking cycle with the "main body" of your character at fixed position, with hand and feet moving around. Then you'd show one feet and say "this one should be pinned to its position, and all the rest will move around. The bouncy branch was drawn in the other way round: the leaves part (that the hitbox should follow) is moving around and the wooden part (which should be pinned) is already sticking in its position.

Mais la branche, elle est réalisée dans AnimEDS dont les mouvements sont un peu différents. Rééditer chaque position de chaque étape d'animation, ce serait franchement pénible. Mon plan B, ce serait donc de m'inspirer de la plate-forme invisible qui empêche Sonic de tomber trop vite dans le pétrole de Oil Ocean, et générer un objet invisible qui fasse attendre Bilou pendant que la branche, qui ne serait plus solide, termine son animation.

I pushed it to the state where I actually have the animation triggering motion when playing, but it makes the whole branch detach from the tree and move down. Hopefully, I have a plan B : do something like the invisible platform hidden in the Oil Ocean of Sonic. There, it catches you and moves down slowly, allowing you to jump back. Here, it would do the move Bilou is supposed to do. It would be spawn when Bilou hits the branch, too.

edit: je ne voulais pas rester sur un "oah, ça va être trop dur", donc je l'ai fait. 20 minutes de bidouille dans AnimEDS à utiliser les petites croix pour réaligner le bas du feuillage à chaque image, retenir "3 vers le haut, un vers la droite" et appliquer le même décalage à la main à tous les éléments. C'est pénible, mais avec le DPAD, c'est possible de le faire de manière précise.

Ensuite, j'ai récupéré le .spr sur PC et un petit coup de débuggeur m'a permis de comprendre pourquoi using momentum(y to 1024 by 64) ne modifiait jamais la vitesse: j'avais écrit les valeurs pour le pseudo-pad dans le mauvais "registre" du gobscript ^^". Je corrige donc ça aussi et je me retrouve enfin avec une branche qui bouge quand on tombe dessus, synchronisant sa position (pour les collisions) et son animation. Sauf que c'est pathétique.

La dynamique de l'animation est complètement cassée: tantôt la vitesse que Bilou lui imprime est trop élevée et on passe des frames, tantôt les mouvements accumulés sont inutiles parce que l'animation fait demi-tour et on reste "gelé" sur la même étape jusqu'à ce que la vitesse négative soit devenue telle que la suite de l'animation va elle aussi être jouée à trop grande vitesse. Et ça, c'était avant que je ne vienne modifier certaines étapes pour forcer l'utilisation d'un délai cassant au passage la propriété 'position finale = position initiale" ce qui explique que la branche remonte petit à petit dans l'arbre au fil des cycles.

Saturday, August 24, 2024

game design with Super Sunny World

There is a NES game in the making. Yes, a new one. A homebrew, of course. There are quite a few ongoing, actually, but this one features artwork from Kenneth Fejer ! and aims at cloning the gameplay of Super Mario Bros in a different setting. And I must admit I'm loving the simple charm it has, and I've been using Matt's posts on Super Sunny World as a sandbox to toy with game design ideas since March '23 the same way I did with Cyber Shadow a few years ago.

Matt proposes "grow up" power that sits still and "fire" power-up that slides away (like a SMB mushroom). Personally, I feel like both sliding and sitting idle do not fit the power-up personalities. I'd like the "grow-up" flower to have a little motion. To make it easier to grab than Mario's mushroom, it could be floating down towards the player. (post-thought: that might be luring a bit too much into Leilani's Island gameplay ^^")

Also, it feels odd that the pearl slides since it is in a shell but if it was a bare pearl, it would perfectly work to see it rolling away. I'm afraid it moves a bit too fast now and I'm unsure you could catch it unless you realized that you can run, which most young player won't. And even if it is only barely faster than yourself, 

Another follower, XPascalou, points out that In Super Mario Bros, players have to work to get their power-ups. It is not "free". Players have to master "run & jump" to catch the mushroom or the star. The star is even harder to catch because it gives much more power. That's true, but level designers put some surrounding elements so that early mushrooms are redirected towards you. (that would be interesting to study SMB1 maps to see how often that happens and when the games start making you take risks for a mushroom, then compare that with SMB3 and Giana Sisters)

Sometimes, it's just a tiny interaction, like when Matt announce he has adjusted the "hit range" for going down tree trunks, and now allows the player to trigger the transition when "duck sliding" across the top. But by all the hours spent on Super Princess Peach ... Thank you, Matt.

Sometimes, there's more meat, like when Matt wants to introduce a cousin to the snail (his default koopa-like creature). But as much as I love its look, with spikes-like on the shell, it looks more like it's deadly to jump on its head and seeing it shielded against shots was a surprise. (I guess I must unlearn to think of them as fireballs ^^"). It would be nice that the shots would slightly bounce them backwards (unless they entered the "stick to the ground" state after they bounced once, possibly).

Of course, for a programmer, it's always nice to get crusty details about what did not really went right, like how initially, shots would be "swallowed" by blocks 1 tile about the ground. Especially because that feels like something the Giana Sisters would have featured, and thus gives a new light on why there were such weird behaviours in that game.

It's nice to see other platformer authors come up with creative alternatives like waterfalls we can swim in ... rather than artificial ladders or elevator that would have felt out of place in the Sunny's world setting. +1 for using white clouds at the bottom of waterfall only when it is swimmable (it wasn't the case initially, and it will definitely help distinguish interactive and decorative waterfalls). I think mixing swimmable and non-swimmable waterfalls will be very demanding for non-expert players and should only be done if there's plain ground to catch us if we're confused. The way that waterfall is placed in the level perfectly introduce the mechanics in a no-tuto way, too ^_^

And then there are the cases where Matt's post make me think "uh. No. I wouldn't do that in my own game", like with the fishes:

fun twist on the "flying cheep cheeps" 🐡 for my NES game, Super Sunny World 🌞! Instead of just jumping and falling, they land and slide across solid surfaces. Maybe I should replace the fish sprite with a penguin sprite

Maybe they could either slow down and become immobile hazard on the ground or fall back through the ground once they are done bouncing ? I like them being fishes. Sliding penguins only work with slippery ground (and well, so could fishes, if ground is slippery) oh. Or they could slide to a stop, then miserably try to avoid you with ridiculously small jumps. Possibly jumping faster if you approach them.

I could try it, but my plan it to have 3 of those active at the same time, at all times, so my instinct is the keep the movement very linear and consistent, to allow the player to more easily track 3 moving enemies flying around the screen.

Okay, a last one, because that was on my blog-me list for such a long time:

Did you ever notice: In Mario 3, if you hit a block that bounces up, it pushes mario downward faster than if he hits a solid block?

I noticed that while playing "Croc World" demo which was lacking it. It felt cheap. I think Mario's Y speed is reverted while bouncing on an active block. On a solid block, the speed is simply set to 0 so it takes a little time to start falling down. Note on Matt's video how the same question block is active when flashing and just solid once empty. That surely contributes to how the game communicates what is interactive and what isn't.


Sunday, August 11, 2024

Houba!

Le Marsupilami en jeu vidéo!

Okay, since you don't speak French, I guess I need to introduce the Marsupilami first. It's a fictional animal starring in some of the most popular Belgian comics of the 70s-90s. It is mostly yellow with a very long tail (about 8m for an adult) and the comics show him doing all sort of tricks with it, like lasso foes, punch nasty guys, use it as a spring to bounce higher ... I guess you've got the idea. There are 2 main marsupilamis character, one who has been brought back with heroes and act as a super-smart pet, and a wild one, who staid in the jungle in a fictional South-American country. Both like to play jokes (but aren't complete clowns), both are strong and smart and protect their loved ones. 

Distribué par Microïds, ceux qui avaient publié Nicky Boom (et plein d'autres titres auxquels je n'ai pas joué). Moi, je suis grand fan (exigeant, mais fan quand-même) du Marsu de Franquin et j'ai donc craqué pour ce titre qui pourrait presqu'être catégorisé "Donkey Kong Country Honoris Causa" tant il en reprend les codes: jusqu'aux "tonneaux-propulseurs-volants" (ici incarnés par des toucans), aux fruits piquants géants, les cavernes-défi-cachées et le dash-roulade-qui-permet-donne-un-double-saut. 

Since the 90s, there have been cartoons and video games about the Marsupilami, but usually they made a fool out of him and couldn't capture the essence of the comic character. And since I'm a huge fan of the original design and character, it was a showstopper. But hey! some French publisher added a Marsupilami title from some French studio to the Nintendo Switch store and from the teaser videos, it seems to play like a Donkey Kong Tropical Freeze DLC level. So I've lets-go-purchased it to see what it looked like.

Bref, au niveau gameplay, c'est validé. Pas aussi original que le travail de Apache Software sur Mega-Drive, mais ce n'est pas forcément une mauvaise chose: manette en main, le jeu de 1995 était grandement confus, comme si on avait voulu transposer un Sokoban en jeu de plate-forme en empruntant les morceaux manquants dans Lemmings. Pas top.

Côté esthétique, c'est du grand art! Le must de ce qu'on peut attendre d'un jeu de plate-formes 2D à l'heure actuelle. On a le choix entre trois personnages mais ils se jouent tous de la même façon. Aucun ne ressemble vraiment au Marsu phare de Franquin ni même aux bébés-marsu du tome "Le pollen du Monte Urticando". Il m'a fallu un moment pour m'y faire, mais je crois que le truc, c'est que qu'on joue avec les bébés devenus marsu-ado. Comme Franquin n'en a jamais dessinés (afaik), ça donnait la liberté aux artistes d'Ocellus Studio pour avoir un graphisme moderne, un niveau de détail adapté à la technologie utilisée et une personnalité qui convient à la narration. Bingo.

On a visual standpoint, it is a success. The environments are well-defined, characters are well-animated and their are quite matching the original creation although not looking exactly like the beloved comics. And the developers used a clever trick to get some distance with the comic: you're not playing any of the iconic grown-up marsus, nor any of the cute marsu babies starred in the 4th book. Well, actually, you're playing any of them three, but they're no babies anymore ... yet they aren't grown up yet.  They're at the marsupilami equivalent of "Son, you're getting close to be a man now" age where farmboys usually see their origin and destiny revealed and set up on a quest to save the world.

On a gameplay standpoint, this is close to DKC:TF the same way Shovel Knight is close to Megaman. The core mechanics are identical, there are a few twists here and there to make it feel fresh, although you wouldn't call it "a novel experience". And considering how the previous game from '95 failed at being fun or even entertaining by being so obscure, it is rather a good thing.

Premier élément sur lequel j'aimerais m'attarder: les "champignons" rebondissants, puisque je suis moi-même occupé à faire rebondir une branche. Restez par-dessus ces "champis", et vous rebondissez automatiquement, encore et encore, à la hauteur d'un saut ordinaire (qui n'est pas bien grande pour un marsu, celà dit). Si votre bouton de saut est enfoncé au moment du contact, vous partez pour un maxi-rebond. Le timing n'a pas d'importance. Maintenez le bouton enfoncé pendant 2 minutes et vous ferez des maxi-rebonds pendant 2 minutes.

C'est simple, c'est direct, c'est prévisible. C'est exactement ce qu'il me faut pour un niveau 1. Et c'est un des choix qui font que ce Marsu est plutôt bien adapté aux plus novices qui n'auraient pas réussi à aller plus loin que le monde 2 de Tropical Freeze (je ne parle même pas de Returns). Pas que le jeu soit enfantin (toutes les recettes de Rayman Origins pour adapter la difficulté au joueur sont reprises) mais il ne cherche pas à faire dans la Grande Quête. 3 mondes + 1 monde caché, et voilà.

From a game design perspective, there are two items I want to keep track of. The first is how easy and intuitive the bouncing elements are in the game (compared to that tricky-to-use bouncing branch in my own demo). Just stay on the thing and you'll make small bounces (about the height of a regular jump). Have the jump button held down and you'll make huge bounces. That simple. No need to press it again everytime you touch the bouncy thing (mushrooms ?). No need to time your button presses against the position of your character on the screen. Just. Hold. It. Down.

Autre élément de gameplay qui mérite qu'on s'y arrête, ce sont les anneaux-pour-s'accrocher. Aucune tentative pour habiller l'objet ici: ils sont à peu près aussi discrets que des blocs-question dans le royaume champignon. Ils s'éclairent en vert quand votre marsu est à la bonne distance et une simple pression sur le bouton d'attaque nous y accroche alors quasi-instantanément. On est ensuite tiré jusqu'à l'anneau ou on restera accroché, statiquement jusqu'à l'appui sur le bouton de saut.

Une séparation des rôles (Y=accrocher, B=quitter-et-sauter) qui est vraiment la bienvenue et qui évite les fausses manoeuvres même quand on s'embrouille dans ses boutons: appuyer sur Y à nouveau quand on est déjà accroché n'a ici aucun effet.

The game also provides an interesting answer to "how to HOOK to things". Here, hooks are plainly identifiable as such ... almost as artificial as Super Mario Bros question blocks. It gets a green glow if you're in range to trigger the HOOK mechanics. If you press the attack button, it will almost immediately (less than 50ms) reach for the hook with its tail and will then start zooming towards it along the straight line drawn by the tail. You won't leave the hook by pressing the attack button again: you'll have to use the JUMP button for that. A nice protection against accidentally leaving the hook when there's no ground to catch you.

Should there be multiple hooks in the range, the direction you indicate with the left stick will select which hook is targeted. Only that hook gets the green glow. That works most of the time, but may trap you in some of the latest levels where the hook you're leaving is falling down or about to move into spikes. Because yeah, those flying hooks will come into a good number of variations that I'll have to study later and the same mechanics are also used for the shooting birds that replace DKC barrels.

Dernière petite touche sympa: appuyer sur le bouton d'attaque pendant qu'on marche va déclencher une attaque-roulade (comme dans Donkey) alors que l'utiliser quand on est à l'arrêt déclenche un "coup de poing" avec la queue du marsu terminée par un gros noeud (sa signature dans la BD). pas de risque donc de se mettre à partir en avant avec un dash involontaire, résultat trop fréquent dans la série DKC. Ultime subtilité, mitrailler le bouton d'attaque pendant qu'on marche permet de rester dans un dash infini, qui pourrait se finir mal quand on arrive au bout d'une longue plate-forme avant un passage où il faudra plutôt s'accrocher. Et la subtilité la voilà: dans cette situation, les concepteurs ont presque systématiquement placé une ou deux caisses en bout de course. Le marsu ne peut pas rouler à travers les caisses: il se retrouve stoppé net. Et si le joueur continue son mitraillage malgré tout, marsu -- maintenant à l'arrêt -- donne un bon coup de queue dans les caisses, récupérant leur contenu, mais reste immobile malgré tout.

Final gameplay sweetness: in many places, you're invited to make a long roll by mashing the attack button. since the roll is actually a dash, you might end up doing one press too much and end up past the edge of the current platform. Hopefully, the level designers thought it wouldn't feel fair and typically end such platforms with a crate. The crate might just contain one fruit but most of all the crate cannot be rolled through. And if you happen to hit the attack button again while stopped by the crate, you'll trigger the Marsu's signature move: punch with a tail-knot, crashing the crate and opening the way.

Friday, August 09, 2024

Fixing the Inspector

I've been through another guru-meditation session last night. It's a bit weird to claim "I like it" while it implies checking hex codes between disassembled C++ and numbers shown on the Nintendo DS screen, looking up for virtual table addresses in gdb to confirm some arbitrary location found in the DS registers actually leads to a GobArea rather than a GobState and so. But I actually do like it :P

Eh oui: c'est encore un post avec des captures de débuggeur, du code assembleur, des chiffres sur la DS et des tas de gribouilles sur un bloc de feuilles: c'est la saison des guru meditations. Le fautif cette fois, c'est Inspector Widget, qui se met à regarder dans des zones mémoires invalides dans certaines situations. Dont la situation que je voudrais bien essayer pour vérifier une stratégie pour améliorer la branche-qui-rebondit. Parce que le tuning ne convainc définitivement pas.

Hopefully my "blue screen of death" isn't that dead and I can navigate almost freely in the DS's core memory to follow pointers and such. Hopefully, too, the bug I encountered last week-end wasn't too hard to reproduce. Well, you have to pick a specific sprite (the bouncing tree branch), give it the debugging focus, activate its passive collision box, then activate Bilou's collision boxes as well and frame-step the game until they collide again ... but at least I was doing that intently last time the bug happened.

Mais cette fois, j'ai été attentif à profiter du plantage sur la DS pour partir explorer la mémoire et prendre des petites notes sur un bloc-notes. Repérer les addresses du code sur la pile, prendre le début des différentes structures pointées par les registres, etc. 

ça n'a pas suffit, malheureusement, et je n'ai pas pu reproduire le bug dans l'émulateur, mais au moins cette fois-ci, je savais comment il était apparu et je pouvais le reproduire sur DS. Donc, recompilation, synchronisation des montres exécutables, petit passage par le stick WiFi et je me retrouve avec un setup de méditation digne de ce nom: la RAM sur l'écran bleu de la DS, le code désassemblé dans mon terminal, et un émulateur qui tente de reproduire le problème grâce auquel je peux vérifier des hypothèses comme "le 116eme byte à partir du début du GameObject, c'est le pointeur vers le GobState. Si c'est bon, le devrais y voir 0x0206c16c qui est l'adresse de la table virtuelle de la classe GobState".

But at least, I could upload a recompiled runme.nds to my DS over my wifi stick and have hex numbers that can be directly used rather than playing match-three between .elf debugging in ddd and hex numbers on the NDS. And yeah, that implied graph paper, writing down values, annotating assembly code with registers values and the like.

All of this to figure that I was missing a test against NULL somewhere in the Inspector Widget logic. and then I feel like a hunter coming back with some evaded cows, proud to bring them back only to be welcomed by his relatives frowning, with their arms crossed and a look saying, "Well, maybe if you were paying a bit more attention when crafting fences, we wouldn't have to hunt after our own cows every 3 weeks ! ..."

Anyway ... that was part of an attempt to see whether I could make Bilou better follow the branch position in a new "soft-land" state and it turns out that it will take more effort before it starts working.