- Sorti en 1999, RollerCoaster Tycoon est un jeu de simulation écrit presque entièrement en assembleur, capable de gérer en temps réel des milliers de visiteurs tout en conservant des performances stables
- Le développeur Chris Sawyer a choisi le contrôle bas niveau plutôt qu’un langage de haut niveau, aboutissant à l’une des dernières grandes générations de jeux en assembleur maximisant l’efficacité des calculs CPU
- Grâce au projet de fans OpenRCT2, les schémas d’optimisation précis et les techniques d’économie mémoire de l’original ont pu être analysés par rétro-ingénierie
- Le jeu exploite les décalages de bits et une granularité fine des types de données pour améliorer la vitesse de calcul et l’efficacité du cache, et rend possible la simulation à grande échelle grâce à une limite de profondeur pour la recherche de chemin et à la suppression des calculs de collision
- Cette architecture est un cas d’école d’optimisation tirant parti de contraintes techniques de façon créative, et montre encore aujourd’hui l’importance d’éliminer les calculs inutiles dès la phase de conception
Analyse de l’architecture d’optimisation de RollerCoaster Tycoon
- Sorti en 1999, RollerCoaster Tycoon (RCT) est considéré comme un jeu écrit presque entièrement en assembleur (Assembly), capable de simuler en temps réel des milliers d’agents tout en conservant un framerate stable sur le matériel de l’époque
- En s’appuyant sur un épisode du podcast allemand Stay Forever, l’article analyse en détail comment Chris Sawyer a atteint un niveau d’optimisation extrême
- Le code source original n’a pas été publié, mais le projet de fans OpenRCT2 permet de vérifier par rétro-ingénierie la structure du code et les techniques d’optimisation employées
-
Maximisation des performances grâce à l’assembleur
- RCT a été écrit en assembleur plutôt qu’en C ou en C++, ce qui permettait un contrôle des performances bien plus fin que dans la plupart des autres jeux de l’époque
- Par exemple, Doom (1993) était majoritairement écrit en C, tandis que RCT a été implémenté presque entièrement en assembleur
- Cette approche était déjà rare à la fin des années 1990, et RCT est souvent considéré comme l’un des derniers grands jeux en assembleur
- À l’époque, l’optimisation automatique des compilateurs restait limitée, si bien que l’optimisation manuelle pouvait faire une énorme différence
- RCT a été écrit en assembleur plutôt qu’en C ou en C++, ce qui permettait un contrôle des performances bien plus fin que dans la plupart des autres jeux de l’époque
-
Analyse du code via OpenRCT2
- OpenRCT2 est un projet open source de fans qui a entièrement réimplémenté le jeu original, en réutilisant ses assets tout en conservant une compatibilité à 100 %
- Les premières versions reproduisaient un comportement presque identique à celui du code original, avant d’intégrer diverses améliorations
- Ce projet a permis d’observer de près les motifs d’optimisation très fins du code d’origine
- OpenRCT2 est un projet open source de fans qui a entièrement réimplémenté le jeu original, en réutilisant ses assets tout en conservant une compatibilité à 100 %
-
Granularité des types de données — économie de mémoire
- RCT stocke la taille des données monétaires différemment selon le contexte
- Exemple : la valeur totale du parc utilise une variable de 4 octets, tandis que le prix d’une boutique tient sur 1 octet
- Cette granularité visait à économiser la mémoire et à améliorer l’efficacité du cache ; sur les CPU modernes, l’écart de performance est presque nul, si bien qu’OpenRCT2 a unifié cela en une seule variable de 8 octets
- RCT stocke la taille des données monétaires différemment selon le contexte
-
Optimisation des opérations mathématiques avec les décalages de bits
- Dans le code, des opérations comme
NewValue = OldValue >>remplacent les divisions par des puissances de deux - Ce type d’optimisation n’est possible que lorsque les multiplications ou divisions portent sur des puissances de deux, ce qui laisse penser que les formules du jeu elles-mêmes ont été conçues pour respecter cette contrainte
- Autrement dit, la structure mathématique du jeu a été pensée dès la conception pour l’efficacité des calculs CPU
- Dans le code, des opérations comme
-
Un game design pensé pour la performance
- Comme Chris Sawyer était à la fois programmeur et unique game designer du projet, il a pu bâtir une structure tenant compte des performances dès la phase de conception
- L’exemple le plus représentatif est le système des visiteurs (pathfinding)
- Dans la plupart des jeux de simulation, les visiteurs choisissent une destination puis calculent un trajet ; dans RCT, ils marchent de façon aléatoire et découvrent les attractions par hasard
- Cette approche évite les calculs de pathfinding à grande échelle, ce qui permet de traiter simultanément des milliers de visiteurs
- Lorsque du pathfinding est nécessaire (par exemple, lorsqu’un mécanicien doit rejoindre une attraction en panne), une limite de profondeur de recherche est appliquée pour éviter les chutes de framerate
- Les visiteurs ordinaires ne cherchent qu’à travers 5 intersections, les mécaniciens jusqu’à 8
- Les visiteurs ayant acheté une carte voient cette limite passer à 7
- Ces limites ne sont pas un simple compromis technique, mais une structure d’optimisation intégrée naturellement au gameplay
-
Gestion des foules et suppression de l’évitement des collisions
- RCT supprime entièrement les collisions et calculs d’évitement entre visiteurs
- Des milliers de visiteurs peuvent partager la même tuile de chemin
- À la place, le jeu suit la densité de population locale, et si l’affluence est trop forte, le niveau de bonheur des visiteurs baisse
- Le joueur doit donc toujours gérer la congestion, mais avec une charge de calcul bien plus faible
- Cette méthode est considérée comme un excellent exemple de suppression de calculs physiques complexes tout en préservant l’expérience de jeu
- RCT supprime entièrement les collisions et calculs d’évitement entre visiteurs
-
Ce que cela apporte au développement moderne
- L’optimisation de RCT est souvent citée comme un exemple d’utilisation créative des contraintes techniques
- Une telle approche reste possible aujourd’hui, mais elle exige une collaboration étroite entre programmeurs et designers
- Il peut parfois être plus efficace, plutôt que de résoudre un problème technique, de supprimer le problème lui-même dès la conception
2 commentaires
S'il vous plaît, donnez beaucoup d'amour à RollerCoaster Tycoon.
Commentaires Hacker News
Warcraft 1, 2 et StarCraft utilisaient tous des cartes dont la taille était une puissance de deux
Cela permettait d’obtenir de bonnes performances sur les CPU 386/486 lents en remplaçant divisions et multiplications par des décalages de bits
Le rendu de la carte, les sprites, les polices, le brouillard, etc. étaient gérés par des milliers de lignes d’assembleur, tandis que le reste était du code portable écrit en C
Pour Blackthorne, les versions SNES, Genesis et DOS avaient chacune été portées à la main avec un assembleur différent, et la version PC générait par macros 100 000 lignes de code de rendu pour le VGA Mode X
Cette expérience a conduit Blizzard à retenir la leçon que « l’assembleur consomme beaucoup trop de temps de développement »
Comanche: Maximum Overkill était un simulateur d’hélicoptère à voxels entièrement écrit en assembleur, mais le portage en mode protégé était si difficile que les versions suivantes sont passées au rendu polygonal
EA a publié le code source de la série Command & Conquer, mais Tiberian Sun et Red Alert 2 n’y figuraient pas
Il aurait été bien que StarCraft soit lui aussi publié au nom de la préservation historique
C’est à partir de là que je suis devenu complètement passionné de jeux vidéo
Je me demande aussi s’il a participé à la demoscene
J’y ai moi-même passé un court moment comme consultant vers la sortie de WC3
Comme le mentionne l’article, on a l’impression que les résultats sont bien plus impressionnants quand le designer et le programmeur sont une seule et même personne
Même avec la structure hiérarchique des grandes entreprises, on finit par sortir quelque chose en y mettant assez de monde, mais les résultats vraiment créatifs viennent souvent d’un esprit unique du début à la fin
Je me demande si l’avenir des outils de développement IA ne va pas nous ramener à cette sorte d’« ère du développement en solo »
Les game designers doivent toujours tenir compte des caractéristiques numériques
Les meilleurs designers savent que l’efficacité des calculs ou la précision peuvent avoir un impact sur l’équilibrage du jeu
Beaucoup de développeurs l’ignorent aujourd’hui, mais cela peut devenir une cause cachée de baisse de qualité
Aujourd’hui, on est plutôt à 1 cycle pour une addition, 3 pour une multiplication et 12 pour une division, avec plusieurs opérations traitées en parallèle
À l’époque des anciens Pentium, c’était 46 cycles, avec des fréquences autour de 100 MHz
Aujourd’hui, c’est surtout le layout mémoire qui compte — un seul cache miss peut coûter entre 100 et 1000 cycles
Faire des calculs sur un
int[]est bien plus rapide que sur unMonster[]Il existe des compromis entre vitesse, précision, taille de stockage et complexité
Des articles comme Toward an API for the Real Numbers proposent une approche progressive pour traiter ce genre de problème
Il existe diverses méthodes — erreurs en virgule flottante, arithmétique d’intervalles, calcul symbolique — mais au final, si on ne comprend pas les compromis, on se fait piéger par le problème
Par exemple, Fumito Ueda a pris grand soin de la faisabilité technique dans Shadow of the Colossus, et Doom était lui aussi une fusion de créativité et de technique
Voir cette interview
Les bugs, la cohérence du récit et l’immersion sont plus importants
Bien sûr, de grosses chutes de framerate dégradent la qualité, mais si le jeu tourne de manière fluide sur le matériel visé, les améliorations devraient se concentrer sur d’autres aspects
Je l’ai ajusté pour pouvoir charger des constantes immédiatement avec les instructions Thumb-2, afin d’éviter les memory stalls
Cela a permis d’utiliser des nombres aléatoires rapidement et efficacement, et tous les tests sont passés
Mais plus tard, quand la cible a été changée pour un Cortex-M0, ce code a été abandonné
J’ai trouvé intéressant le fait de transformer les contraintes techniques en éléments de gameplay
Cela m’a fait penser au système de Blood Moon dans The Legend of Zelda
L’explication disant que « remplacer
/8par>>3permet d’économiser une division » est fausse avec les compilateurs modernesSi les types sont corrects, le compilateur optimise automatiquement cela en décalage de bits
signed), des opérations supplémentaires sont ajoutées afin de respecter les règles d’arrondi du standard CJ’ai été surpris de voir l’explication selon laquelle « en binaire, un décalage à gauche revient à multiplier par deux »
C’est curieux de voir à quel point ce concept de base peut paraître presque nouveau ou étrange en 2026
Dire que « le compilateur ne remplace pas une multiplication par une puissance de deux par un décalage » est une vieille blague
Déjà dans les années 2000, les compilateurs optimisaient cela si évidemment qu’ils en bâilleraient presque
Lecture très plaisante. À propos de RCT, je recommande aussi les ressources ci-dessous
Je me demande s’il est possible, dans un grand studio, de sublimer les contraintes techniques pour en faire une caractéristique du jeu
De la même façon qu’en narration, les obstacles rendent une histoire plus intéressante, un développeur solo peut transformer les limites techniques en éléments créatifs
Par exemple, je pense à une approche qui réinterpréterait des bugs ou des glitches comme des mini-jeux
En voyant la partie sur le pathfinding de RCT, cela m’a rappelé la vidéo YouTube de Marcel Vos
Il publie beaucoup de vidéos qui explorent en profondeur le fonctionnement interne de RCT