Laurent Le Brun voulait fabriquer un jeu en sept jours. Il a donc commencé par écrire un langage de programmation.

C’est une mauvaise idée dans la plupart des plannings et une excellente idée pour le Langjam Gamejam, dont la règle était précisément de créer un langage puis de l’utiliser pour produire un jeu.1

Le résultat, shmup8, est un shoot’em up Windows dont l’exécutable final tient dans environ 3 kB.1 Le code est public et une version jouable est distribuée sur itch.io.2 3

Ce qui est intéressant n’est pas seulement le chiffre. La contrainte de taille a fini par décider du langage, du moteur, du workflow et même de ce que les ennemis ont le droit de faire.

Une VM peut coûter moins qu’elle ne fait économiser

Le Brun construit un compilateur en F#, un interpréteur en C++, puis écrit la logique du jeu dans son langage maison. Le rendu passe par un unique shader GLSL plein écran.1

On pourrait imaginer que l’interpréteur est un luxe absurde quand chaque octet compte.

À la fin du projet, l’auteur porte la logique en C++ pour vérifier. Dans sa mesure, la version sans VM ressort 90 octets plus grosse que la version bytecode.1

Il demande lui-même de prendre ce résultat avec prudence : ni le port C++ ni l’interpréteur n’ont été poussés dans une compétition d’optimisation exhaustive.1

La conclusion raisonnable n’est donc pas « les VM sont plus petites que C++ ».

C’est qu’un langage intermédiaire minuscule peut compenser le coût de son interpréteur quand il encode très efficacement le domaine précis du jeu.

Un seul type, presque aucune politesse

Le bytecode utilise essentiellement un type : float32.1

Les valeurs vivent dans des tableaux. Une variable locale prend un emplacement. Un index est lui aussi un float, converti en entier lorsque nécessaire. Une condition devient vraie au-dessus de 0,5.

Le bytecode se réduit à deux grandes familles d’opérations : modifier une cellule ou sauter à une autre adresse, éventuellement sous condition.1

Le langage visible par l’auteur reste un peu plus civilisé grâce à du sucre syntaxique : affectations, if, while, boucles for transformées par le compilateur.1

La séparation est classique, mais la contrainte la rend très visible : la syntaxe peut rester agréable tant que le format exécuté demeure brutalement simple.

Le live reload protège la partie créative

Le plus utile dans le billet n’est peut-être pas la compression.

Le Brun voulait modifier la logique et les visuels sans relancer la compilation C++. Son IDE invoque donc le compilateur du langage à chaque sauvegarde, produit un nouveau bytecode, puis l’exécutable déjà lancé recharge ce fichier à chaque frame. Le shader GLSL est rechargé de la même manière.1

Deux boucles de live coding tournent côte à côte.

Cela permet de préserver une chose que les contraintes techniques détruisent facilement : l’itération visuelle rapide.

Le Brun le dit clairement : dans un environnement créatif, on ne peut pas prédire à l’avance ce qui « va bien sentir ». Il faut pouvoir essayer.1

Même les ennemis sont dessinés par la contrainte

Le jeu commence avec trois ennemis et en ajoute un toutes les sept secondes.1

Quand un missile touche un ennemi, celui-ci ne meurt pas. Il est téléporté hors de l’écran puis peut revenir.1

Pourquoi cette immortalité inattendue ? Parce qu’une liste de vagues, des destructions et davantage d’états auraient demandé plus de logique. Le Brun choisit une règle plus simple qui fait tout de même monter la difficulté.

Les parties visent environ 30 à 60 secondes, avec redémarrage rapide.1

La taille n’a donc pas seulement compressé le programme après coup. Elle a produit des décisions de game design.

La contrainte est utile quand elle arrive tôt

shmup8 n’est évidemment pas un modèle raisonnable pour construire le prochain RPG de 80 heures.

Mais le projet montre quelque chose de transférable : une contrainte peut devenir intéressante si elle influence le système dès le début.

Si on décide à la fin qu’un jeu doit être petit, on compresse des fichiers. Si on décide au début qu’il doit être minuscule, on invente un bytecode, on dessine un shader unique et on se demande si les ennemis ont vraiment besoin de mourir.

À 3 kB, même une mécanique de jeu finit par ressembler à une décision de stockage.