Showing posts with label game. Show all posts
Showing posts with label game. Show all posts

Friday, September 4, 2026

Waking up a vomiting lion

Back in April 2025 I spent a weekend with Codex making a small Godot game called LeLion. The pitch fits in a sentence: a lion head flies over a grey city, picks up colour dots, and vomits a rainbow to paint the buildings, whilst a flying saucer and a ladybug try to kill it. Nine commits in ten days, most of them titled « On continue » (we keep going), then I put it down. The thing more or less ran, the painting worked through a shader and a pixel mask, and that was that.

Yesterday (yep, 17 months as the crow flies) I picked it up again with Claude Code, and by the end of the day there was a finished web game, and a GBA ROM (for #retrogramming).



Waking up the prototype

The first thing Claude did was read everything and tell me, plainly, what state it was in. Two signal connections pointed at methods that didn't exist (one of them because of a capital letter). Three dated copies of the main script lived next to the real one, because in 2025 I knew git but apparently didn't bother. The victory condition counted the transparent sky as something to paint, so it was unreachable. Seven emitters of 7,500 GPU particles each. One saucer, one ladybug, to debug, and then nothing ever again.

We agreed on phases: clean up, structure, gameplay, polish. Claude installed Godot through Homebrew and drove everything headless. It wrote a smoke test as a SceneTree script that loads the main scene without a display, unlocks a colour, makes the lion vomit on the city, provokes a defeat, then a victory, and asserts on the way. That script grew all day, from 12 assertions in the morning to 136 yesterday evening, and it caught a good half of the regressions before I even saw them.

A few Godot gotchas for anyone trying the same (but I'm sur Claude will reengineer it anyway, for a few tokens). godot --check-only doesn't load autoloads, so every reference to a singleton is flagged as an error; ignore it and trust a real run. A --script harness compiles before the autoloads exist, so you fetch them with root.get_node("GameState") rather than by name. And when you run the game windowed to take screenshots, wait in seconds with create_timer, not in frames, because without vsync the OpenGL window runs at several hundred frames per second and your "60 frames" is a quarter of a second.

Those screenshots helped Claude see the mess the project was in. The colour pickup's collision shape was offset by (195, 54) from its own origin, so it was never where the code put it. The lion's mouth marker sat at (186, 191) on a 128 pixel sprite, well outside the lion, and the vomit was a handful of one pixel particles (I was beginning with Godot and Codex, OK?). Claude located the actual mouth by scanning the sprite for pink pixels, moved the emitter there, and placed the paint zone at the landing point of a projectile fired at 45 degrees with the same velocity and gravity as the particles.

Making it a game

From there it was a long list, and I'll go fast. Three difficulty modes (easy with three hearts and heart pickups, normal with three hearts, hardcore with one). Three levels, two of them procedurally generated skylines, plus a title screen with best times. A rainbow star that doubles the jet for eight seconds. A pause menu. Screen shake, a red flash and a knockback when you're hit, confetti when you win, paint drips running down the buildings, a bit of inertia and lean on the lion. A procedural chiptune loop written by a Python script that outputs WAV files, later split into three synchronised tracks so the arpeggios and the melody come in as the town gets painted, with a minor key theme for the boss level. Touch controls that only appear on touch screens. Settings with volumes, fullscreen, a CRT filter and a language toggle, the whole game translated to English through Godot's CSV translations. A READY? VOMIT! intro, a CONTINUE? countdown when you die, an animated end-of-level summary, an arcade mode chaining the nine level and difficulty combinations, and an attract mode that plays a demo if you leave the title screen alone for fifteen seconds. CI on GitHub Actions builds the web export and deploys it to GitHub Pages on every push.

The boss deserves a paragraph. The Village level was too easy, so we added a giant painter's head, 65 percent of the screen tall, who comes in from one side, walks to the centre, pauses, walks back, and returns from the other side; you paint the half he leaves free. There was no sprite, so Claude drew an SVG silhouette (beret, nose, eye, brush with a red tip) and we iterated on the moustache three times: first it looked like two pieces, then it curled up, then I asked for it to curl down and hold the brush. The collision polygon is generated from the SVG's alpha at runtime, so replacing the file replaces the hitbox. Then a bug I noticed: the sprite flipped when the boss came from the right, but the collision didn't. Fixed together. Progress is now a real coverage measurement (the paint mask downscaled to a grid every 200 ms), which made 85 percent honest and, as a consequence, hard. Normal now asks for 90, hardcore for 95.

And then, a GBA ROM

Late in the day I asked whether the game could exist as a Game Boy Advance ROM, with the constraints of the era. Godot doesn't export to GBA, so the honest answer was a rewrite in C. Claude proposed a scale: 2000 by 648 pixels becomes a 480 by 72 town scrolling on a 240 by 160 screen, the 128 pixel lion becomes 32 by 32, the jet lands 35 pixels ahead and 45 down instead of 136 and 190, the boss becomes a composite of four hardware sprites. We created a second repository and went.

No toolchain got installed on my Mac. The build runs inside the official devkitPro Docker image (make docker), and the same image builds the ROM in CI. For tests, since the Homebrew build of mGBA was linked against an ffmpeg that no longer exists, Claude rebuilt it from source and then wrote a 150 line C harness on top of libmgba: it loads the ROM, runs frames, presses keys, reads memory, pokes memory, and dumps the screen as PPM. The game writes a small debug block at a fixed address in EWRAM (lion position, camera, progress, lives, state), and the test scripts assert on it. Screenshots come out as PPM, a tiny Python script turns them into PNG, and I got to look at them.

The town is a mode 4 bitmap (8 bit paletted), painted directly in an EWRAM buffer and copied into the visible page with one DMA per row. The sky gradient is drawn the same way, with fixed-source DMA fills. Sprites do the rest: the lion, ten drops along the arc, pickups, the saucer, the ladybug, the painter in four parts. Music runs on the actual Game Boy channels (two squares, the wave channel with a triangle for the bass, noise for kick and hat), sequenced from the same note data as the Godot version, and the sound effects are 8 bit samples on the two DirectSound FIFOs. Records and settings go to cartridge SRAM. Seven phases, thirteen commits, four tagged releases, all built and tested by CI, the ROM attached to each release.

The GBA taught us things the Godot version couldn't. libtonc's clamp() excludes its upper bound, so the lion stopped one pixel short of the edge and the camera two. RGB15() is a function, not a constant, so it can't initialise a static table. Non-square hardware sprites use the size code 64 for both 64 by 32 and 32 by 64; I'd used 32 and the painter lost his collar. And two bugs I found by actually playing in mGBA: pressing left on the difficulty row showed an empty label, because key_hit() returns the key's bitmask and not a boolean (right is 16, left is 32, and 16 modulo 3 happens to be 1, which is why right worked). Then the game lagged with four colours or more whilst vomiting: the paint stamp did three software divisions per pixel on a processor with no divide instruction, up to 441 pixels a frame. A lookup table and a multiply fixed it, the hot loops moved to IWRAM as ARM code, and a test now holds A with seven colours for 240 frames and checks that the frame counter advanced by exactly 240.

I asked whether it was worth optimising more, in case we add things. Measured, not guessed: a probe records the scanline reached when the frame's work is done. Worst case in play, boss on screen, seven colours, vomiting and scrolling, is 41 percent of the frame. So no. A test will go red if a future feature pushes that past three quarters.

Last thing, since I thought of it: the released ROM read two test hooks from the debug block, so anyone with an emulator's memory editor could poke one word and win. Those hooks now compile only into a separate lelion-debug.gba; CI tests that one and publishes the clean one, with a final check that the release ROM ignores the pokes. A memory editor can still write your lives directly, of course. That's true of every game ever made.

The web version is at w3cdotorg.github.io/LeLion, the GBA ROM in the releases of the second repo; mGBA runs it, and it should run on a flash cart, though I haven't tried one yet (will try today on a RG35XXSP — not an affiliated link)). If you do, or if the chiptune sounds wrong on real hardware (I haven't heard it on anything but an emulator), tell me in the comments.

Session ledger

Since I'd like to be transparent with costs in tokens, here is the actual count, pulled from the local transcript Claude Code keeps for the session (an 18 MB JSON file, which tells its own story). One session, one model (Claude Fable 5.1), from 07:37 on the 4th to a last exchange the next morning, so roughly fifteen hours of actual work with a night in between.

My messages39
Model calls280
Tool calls319 (224 shell commands, 78 file or image reads, the rest browser automation)
Output tokens561,242
Input tokens, fresh7,700
Input tokens written to the cache2,450,708
Input tokens read from the cache125,947,570

The big number is the one to read carefully. A coding agent re-reads its whole context at every call, so 280 calls over a context that ended up around half a million tokens gives a hundred and twenty-six million tokens of cache reads. Those are billed at a fraction of fresh input, which is the whole point of the cache, and they don't mean the model "read" that much new material; it kept re-reading the same day. The genuinely new material is the two and a half million tokens written to the cache (my messages, every tool result, every screenshot description) and the half a million tokens it produced: the code, the commands, the commit messages, the screenshots' analyses, and the explanations you've just read a summary of. For scale, that output is about the length of four novels, most of it GDScript, C, Python and shell, and a fair amount of it thrown away along the way.

Monday, June 17, 2013

Pourquoi j'appréhende GTA V

Le nouveau Grand Theft Auto, GTA V, est attendu pour le 17 septembre 2013. Les précédents jeux de la série, depuis le premier (1997, sorti sur PlayStation / PC) jusqu'au dernier (GTA IV, en 2008, sur XBox360 / PS3 / PC) ont tous présenté un univers ouvert, dans lequel le joueur peut se déplacer à volonté dans des espaces de liberté entre une variété de missions (principales ou secondaires) à effectuer dans la ville.
Malgré le plaisir que j'ai pu prendre à jouer aux divers GTA de la série (principalement GTA 2 [1999] et GTA Vice City [2002]), j'appréhende fortement le prochain. Les trois bandes annonces sorties, présentant chacune un des trois personnages principaux (masculins) n'ont pas aidé à m'en faire une meilleure idée.
Les GTA récents font à mon avis preuve d'une volonté de plaire à un public déjà conquis (tout pour l'argent, pas de prise de risque), et à travers ceci d'un manque de maturité, mais aussi d'un manque de prise de conscience des nouvelles formes de narration ainsi que de l'évolution des personnages vidéoludiques.


De la surcommunication comme générateur de buzz (et de dégoût)

Le nouveau Grand Theft Auto (V, ou 5), est attendu. Très attendu. À titre d'exemple, chaque post concernant le jeu à venir sur la page Facebook de Rockstar Games reçoit des milliers de likes : plus de 19.000 pour la simple annonce de la venue de trois bandes annonce concernant les trois personnages principaux, Michael, Franklin et Trevor, ou près de 70.000 pour l'annonce du retard dans la date de sortie (17 septembre 2013 au lieu de « printemps 2013 », retard annoncée le 31 janvier dernier).
Je n'analyserai pas plus longuement la manière outrancière (à mon avis) de faire la publicité de ce jeu, mais c'est à ce niveau presque de la surcommunication : tenter de créer du buzz sur l'annonce d'une bande annonce, en postant presque chaque jour pour prévenir de la venue de la bande annonce... Il n'y avait que Rockstar pour le faire. C'est un modèle qu'ils appliquent — et ont appliqué — à d'autres jeux, comme le troisième volume des Max Payne, et qui est un moyen simple de faire du bruit sur rien.
Bien sûr, on sait que GTA V ne sera pas l'arlésienne des jeux vidéos (hello Duke Nukem Forever) et sortira un jour, jamais assez tôt pour les mordus, mais c'est néanmoins une méthode de communication que je réprouve. Qui plus est lorsqu'elle est alliée à une volonté d'exploiter le moindre produit déjà réalisé jusqu'à plus soif, en vendant par exemple presque dix euros (cinq euros sur Steam, mais régulièrement moins dans les compilations de jeux Rockstar) un jeu vidéo (GTA Vice City) sorti, je le rappelle, en 2002. Ou encore en en vendant la bande originale, non complète (par rapport au coffret CD et aux CD sortis à l'époque, cf. photo 1, photo 2), en moyenne sept euros l'album sur iTunes.
Pour ces raisons, je ne porte pas (plus) forcément Rockstar dans mon cœur. Surtout quand on prend en compte le fait que leurs derniers jeux n'ont toujours pas prouvés que l'équipe savait narrer correctement une histoire, ni proposer un environnement dans lequel on pourrait souhaiter vivre.


Narration schmarration

Nombre de jeux, récents ou moins récents, ouverts ou non-ouverts, ont ces dernières années présenté des environnements assez immersifs et complexes, au point de donner envie au joueur d'y habiter. Ou en tout cas d'y passer du temps à explorer, visiter, converser (avec des personnages non-joueurs [PNJ] ou d'autres joueurs pour les jeux en ligne). Je pense à toi, série des Elder Scrolls, à vous, jeux de thatgamecompany, à toi aussi, dernier Tomb Raider, ou même à des jeux proposant un graphisme plus « artistique » (tel Okami) pour n'en citer que quelques uns.
Mais pas les Grand Theft Auto, non. Pour faire court, si quelques détails peuvent parfois suffire à donner l'illusion d'une ville complète [1], ce n'est pas le cas des Grand Theft Auto, et notamment de celui dont on pourrait croire le contraire, le quatrième (numéroté) de la série, dont l'action se déroule dans une New York fantasmée, Liberty City. Si quelques détails sont amusants (un policier complètement fou dont de multiples vidéos sont disponibles sur YouTube, des spectacles de comiques existant dans la réalité, des chaînes de télévision multiples), la ville en elle-même ne vit pas. Vous ne pouvez pas suivre un passant toute la journée en espérant le voir agir comme tel (aller au travail, manger à heures fixes, rentrer chez lui le soir) ; vous ne pouvez pas suivre une voiture dans la rue plus d'une dizaine ou vingtaine de minutes (selon les cas), sous peine de la voir se comporter de manière erratique, subissant les algorithmes de route aléatoire.
Essayez dans Oblivion, qui est sorti — je le rappelle — en 2006, il y a sept ans. Vous pouvez suivre le moindre PNJ, un marchand par exemple, il va sortir de chez lui le matin à heure fixe, rejoindre son magasin qui deviendra accessible légalement à votre personnage, sortir manger entre midi et deux, travailler l'après-midi puis rejoindre son chez-lui le soir venu.
Je parlerai moins de Flow, Flower et Journey, qui font plutôt partie des jeux expérimentaux et poétiques, mais dont l'ambiance est telle que l'on peut s'y balader des heures durant à faire et refaire les niveaux, seul ou, dans le cas de Journey, — léger spoiler — avec d'autres joueurs.
Si je relance régulièrement GTA IV, c'est surtout pour l'ambiance « bac à sable », c'est-à-dire globalement se déchaîner avec les voitures et les armes les plus puissantes pour mettre la ville à feu et à sang. Mais on ne croit pas en la cohérence globale de la ville.
Je n'ai jamais fini la série de missions principales de GTA San Andreas (sorti en 2004), et je n'ai jamais repris les missions principales d'aucun GTA passé Vice City après les avoir terminées une première fois. C'est là le second point de mon argumentaire environnemento-narratif : les développeurs ne savent pas raconter une histoire, et ne donnent pas envie d'y rejouer. Si j'ai repris Vice City environ 92330219 fois, c'est principalement parce que le scénario, s'il était bon, était entièrement pompé sur les films de gangsters (Scarface [celui de De Palma], Goodfellas...). Les personnages caricaturaux fonctionnent lorsqu'ils font référence à des entités médiatiques connues (Tony Montana), mais dès que l'on s'éloigne de ces personnages, principalement masculins, c'est le désert scénaristique.


Bechdel what now?

Dernière raison qui me rend dubitatif quant au plaisir que je prendrai à jouer au nouveau GTA : la place des femmes dans cette série de jeux. Aucun d'entre eux (quatorze au total et le quinzième pour septembre prochain) ne passe le Bechdel test, ce simple test — au départ appliqué aux films mais qui s'étend aux jeux vidéo — qui permet d'identifier les préjugés liés au genre dans les œuvres de fiction.
Ce test est simple, et comporte trois règles :
• L'œuvre de fiction doit comporter au moins deux personnages féminins,
• qui discutent entre elles,
• d'autres choses que d'un homme.
Aucun des GTA ne passe ce test. En fait je crois qu'aucun n'arrive à la seconde règle. Je ne vais pas décrire en détail la vision totalement réductrice des femmes présentée dans les divers jeux, il vous suffira de visionner quelques vidéos pour vous en rendre compte.
Ce n'est pourtant pas parce que les jeux vidéo ne savent pas faire de bon personnage féminin ; cette liste de six personnages féminins marquants du jeu vidéo, faite par le site 1up, est éloquente.
Ceci-dit, comme le souligne G. Christopher William sur Kotaku, construire un personnage féminin qui soit une gangster crédible paraît difficile, notamment en raison du manque de représentation de ce type de personnage dans les médias traditionnels. Dans la plupart des cas, ce personnage se construit par rapport à un scénario de viol / vengeance, ce qui pour William ferait prendre à GTA un tournant trop sombre par rapport à ce dont le jeu a l'habitude.
« GTA est à l'aise dans la comédie un peu sombre, c'est vrai, mais une telle “construction du personnage” pourrait très vite lui retirer toute nuance d'humour. »
S'il y a bien un studio qui aurait pu se permettre cette tentative, quitte à y laisser quelques milliers de dollars, c'est bien Rockstar, il me semble.


Grand Theft Auto V n'est pas encore sorti, certes, et mon propos, s'il est basé sur les précédents jeux, ne reste que spéculatif concernant ce dernier opus. Les développeurs ont su prouver qu'ils savaient et pouvaient pourtant poser la questiondu genre, à travers notamment la Ballad of Gay Tony, contenu additionnel (DLC) de Grand Theft Auto IV. À eux de nous prouver qu'ils n'ont pas peur de s'adresser maintenant à un public adulte, qui aurait grandi avec leurs jeux.


Plus de lecture :
À titre de référence, je ne saurai que conseiller l'excellent livre du journaliste David Kushner Jacked: The Outlaw Story of Grand Theft Auto (retitré en français, assez bizarrement, Jacked: The Rockstar Story of Guns, Games and Grand Theft Auto [coucou Pardon My Titres !]).

[1] William Welden, comparant la construction mentale de Minas Tirith à celle du sindarin dans le roman de J. R. R. Tolkien Le Seigneur des Anneaux, disait : « On peut facilement imaginer qu'il y a des douzaines de rues comme Rath Celerdain [la rue des Lanterniers] à Minas Tirith, mais seulement parce que Tolkien l'a décrite en détail, et d'une manière qui la rendait complète, intègre, partie d'un tout ».