Quitter le développement de jeux en Rust après 3 ans
(loglog.games)- À propos de l’affirmation selon laquelle tous les problèmes disparaissent une fois qu’on s’est habitué à Rust
- Même en se familiarisant avec Rust, les problèmes fondamentaux ne disparaissent pas
- Un jeu est une machine à états complexe dont les exigences changent sans cesse, ce qui s’accorde mal avec le caractère statique et le contrôle excessif de Rust
- Le fait de devoir refactorer constamment le code est un problème qu’on se crée soi-même
- Les gros problèmes de refactorisation causés par le borrow checker
- Rust force les refactorisations plus souvent que les autres langages
- Quand les exigences d’un jeu changent souvent, on finit par se battre avec le borrow checker et par devoir restructurer le code
- Il est difficile d’adhérer à l’idée que la refactorisation produit un bon code. Un bon code naît en répétant les idées et en testant des approches
- L’indirection ne résout qu’une partie des problèmes et réduit la productivité de développement
- Utiliser de l’indirection pour contourner les problèmes liés au borrow checker sépare la logique du code et le rend plus complexe
- Dans un jeu, il faut gérer des événements liés entre eux, des timings précis et beaucoup d’état, et l’indirection rend cela plus difficile
- En Rust, même un code simple doit être écrit de façon verbeuse pour introduire de l’indirection
- L’ECS (Entity-Component-System) résout le mauvais problème
- Les avantages de l’ECS sont en réalité ceux des generational arenas
- L’ECS est vu sous divers angles : composition dynamique, structure of arrays, solution au borrow checker de Rust, generational arenas créées dynamiquement, etc.
- Mais il est légitime de se demander si l’ECS est vraiment l’approche la plus adaptée au développement de jeux. Il faut se concentrer davantage sur la logique du jeu que sur la performance ou la composition
- Les systèmes généralisés ne mènent pas à un gameplay intéressant
- Des systèmes trop généralisés produisent un gameplay ennuyeux
- Un bon jeu repose sur des interactions détaillées soigneusement conçues, la synchronisation des VFX, des playtests et expérimentations répétés, ainsi qu’une sortie rapide pour recueillir des retours
- Rust poursuit des valeurs peu adaptées au prototypage rapide et à l’itération
- Dans le développement de jeux, ce qui compte, c’est le prototypage rapide et l’itération, alors que les valeurs de Rust vont à l’opposé
- L’essentiel dans le développement de jeux, c’est le jeu lui-même et l’expérience de jeu, pas un code sans crash
- La maintenabilité du code de jeu n’est pas une valeur si importante dans l’indé ; ce qui compte, c’est la vitesse d’itération
- Rust, obsédé par l’évitement des problèmes, passe à côté de ce qui est vraiment important
- Les procedural macros sont très loin de la réflexion
- En développement de jeux, il faut écrire du code dans des domaines variés : code système, gameplay, UI, VFX, audio, outils, etc.
- En Rust, même un simple « affichage d’objet » doit être codé à la main ou nécessite de créer des procedural macros
- Les procedural macros sont bien plus limitées que les macros déclaratives et allongent aussi le temps de compilation
- La réflexion de C# est très simple à utiliser et permet de développer vite quand les performances ne sont pas critiques
- Le hot reloading joue un rôle important pour améliorer la vitesse d’itération
- Le hot reloading est très utile pour l’UI / rendu en mode immédiat, le débogage et le réglage des constantes de gameplay
- Rust dispose de
hot-lib-reloader, mais ce n’est pas parfait, et cela demande de la planification et de l’anticipation, ce qui limite les usages créatifs - Les développeurs de jeux veulent des outils de plus haut niveau qu’un simple rechargement de struct
- L’abstraction n’est pas un choix, c’est une nécessité
- Exemple concret : lorsqu’il faut des comportements différents selon l’état de l’UI, Rust oblige à refactorer ou à abuser des clones
- Il arrive souvent qu’on modifie le code non pas parce que la logique métier change, mais pour satisfaire le compilateur
- Rust ne dispose pas d’un système de types structurels du genre « type avec ces champs », donc une refactorisation impose de modifier le code à plusieurs endroits
- La situation de l’interface graphique en Rust est catastrophique
- Dans l’UI d’un jeu, l’important n’est pas le data binding ou les mises à jour réactives, mais la personnalisation de l’apparence
- Ce qu’il faut pour l’UI de jeu, c’est une belle GUI, des sprites personnalisés, des animations, des formes vectorielles, des particules, des effets, des flashs, etc.
- Il n’existe actuellement aucune bibliothèque Rust spécialisée pour les GUI de jeux
- L’orphan rule devrait être optionnelle
- L’orphan rule réduit fortement la productivité pour des raisons de sûreté
- Il devrait être possible de désactiver l’orphan rule dans du code applicatif, par opposition au code de bibliothèque
- Les temps de compilation se sont améliorés, mais restent lents dès qu’on utilise des proc macros
- Les proc macros comme
serdeaugmentent fortement les temps de compilation - Avec des builds incrémentaux de 20 à 30 secondes ou plus, il devient très difficile d’affiner les réglages
- Les proc macros comme
- L’écosystème Rust pour le développement de jeux semble survendu
- Il n’y a pas tant de gens que ça qui développent réellement des jeux
- Il arrive souvent que des projets au site web ou au README impressionnants et très connus ne soient pas à la hauteur dans la réalité
- Si l’on veut créer de vrais jeux,
godot-rustest recommandé. Mieux vaut s’appuyer sur un moteur mature que s’obstiner dans une solution 100 % Rust
- Comme les jeux sont en mono-thread, les raisons qui rendent l’état global inconfortable sont mal posées
- Utiliser un état global en Rust est très inconfortable (
static mut,AtomicRefCell,Rc, etc.) - Mais contrairement à un service backend, un jeu tourne entièrement en mono-thread, donc ces contraintes ne sont pas vraiment nécessaires
- L’introduction de systèmes parallèles dans Bevy est vue comme une erreur. Cela a été fait pour la performance, mais oblige les utilisateurs à préciser constamment l’ordre d’exécution, ce qui rend le tout plus pénible
- Utiliser un état global en Rust est très inconfortable (
- La vérification dynamique des emprunts provoque des crashs inattendus après refactorisation
- Après deux ans d’utilisation de
hecs, de nombreux cas de crash ont été observés à cause de requêtes qui se chevauchent - Avec
RefCell, un simple double appel à.borrow_mut()à deux endroits différents peut aussi provoquer un crash inattendu - Le problème n’est pas tant la mauvaise qualité du code que le fait que Rust force des refactorisations inutiles
- Dans les jeux,
RefCellest utile, mais Rust en rend l’usage trop difficile
- Après deux ans d’utilisation de
- Les objets context manquent de souplesse
- Comme il est difficile d’utiliser un état global en Rust, on adopte souvent un pattern consistant à regrouper des références dans un objet context que l’on transmet partout
- Mais si l’on n’emprunte que certains champs, on se retrouve avec des erreurs de compilation à cause du partial borrow
- On conseille alors de découper le context et de modifier la structure, alors qu’on voudrait simplement travailler sur la logique du jeu ; perdre du temps à restructurer le code pour satisfaire le compilateur est du gaspillage
Les points forts de Rust
- Une fois compilé, le programme fonctionne généralement bien. C’est particulièrement utile pour les outils CLI, le traitement de données ou l’écriture d’algorithmes
- Le langage offre de bonnes performances sans effort particulier. L’expérience rapportée évoque une vitesse 1,5 à 2,5 fois supérieure à C#
- Les enums sont bien conçus
- Grâce à Rust analyzer, l’expérience IDE s’est nettement améliorée
- Le système de traits est bien conçu. Si l’orphan rule était un peu assouplie, son utilité serait encore bien plus grande
L’avis de GN⁺
-
L’opinion sur Rust est globalement négative, mais comme il s’agit de la conclusion d’un auteur qui a multiplié les essais, elle mérite d’être prise au sérieux. En particulier, l’idée que dans le développement de jeux, le pragmatisme et la vitesse de développement sont cruciaux, et que Rust reste encore insuffisant sur ces aspects, semble pertinente.
-
Je suis d’accord avec l’idée que la sûreté recherchée par Rust peut parfois faire chuter fortement la productivité. En écrivant du code Rust, on passe souvent plus de temps à satisfaire le compilateur qu’à travailler sur la logique elle-même, ce qui semble découler du fait que Rust est fondamentalement un langage spécialisé dans la programmation système.
-
À l’inverse, le développement de jeux se fait le plus souvent dans un environnement mono-thread où le prototypage rapide et l’itération sont essentiels ; des contrôles de sûreté excessifs peuvent donc devenir contre-productifs. On a beaucoup entendu parler de tentatives avortées de développement de jeux en Rust à ses débuts, et il semble que les problèmes de fond ne se soient pas beaucoup améliorés depuis.
-
Bien sûr, des moteurs de jeu récents comme Bevy ont amélioré le confort du développement de jeux en Rust, mais ils semblent encore très loin du niveau de maturité de moteurs comme Unity ou Godot. Comme le suggère cet article, plutôt que de tout construire en Rust pur, utiliser Rust en complément de moteurs existants peut constituer une alternative réaliste.
-
Les avantages mentionnés par l’auteur se ressentent fortement lorsqu’on utilise Rust dans d’autres domaines que le jeu vidéo. En particulier, dans la programmation système ou le développement de serveurs web, la sûreté et les hautes performances de Rust sont de vrais atouts, mais ces qualités semblent offrir bien moins de bénéfices dans le développement de jeux.
-
En fin de compte, Rust n’est pas un langage universel mais un langage spécialisé pour certains domaines ; il faut donc réfléchir avec soin à son adéquation avec la nature de son projet.
13 commentaires
Je pense que Rust est un langage conçu pour résoudre les problèmes causés par les différentes fautes des développeurs, à mesure que se raréfient les développeurs capables d’écrire un code intègre au niveau du cœur.
Comme il est plus adapté à un développement précis qu’à un développement rapide,
il ne convient pas aux projets où des demandes supplémentaires des utilisateurs surviennent fréquemment et doivent être exécutées.
Malgré cela, si l’on espère voir apparaître une bibliothèque UI,
c’est sans doute parce qu’on s’attend à ce que son utilité augmente même avec une petite UI simple fonctionnant sur un code robuste.
Je n’ai jamais fait de développement de jeux, donc je ne sais pas vraiment,
mais j’ai l’impression soit que le mauvais langage a été choisi dès le départ, (si la rapidité d’itération était importante, il aurait fallu choisir un langage de niveau script...)
soit qu’il manquait une compréhension approfondie du système.
Je me dis qu’il aurait sans doute rencontré des problèmes similaires même en choisissant C++.
C’est vrai. Bon, si j’avais choisi C++, il y aurait quand même eu l’option Unreal...
À notre époque, j’ai l’impression que des langages natifs comme Rust ou C++ sont plus adaptés au développement de middlewares comme les moteurs de jeu qu’au développement de clients de jeu.
Un peu comme il existe très peu de cas de développement web full stack avec des langages natifs.
C’est une langue qui a été conçue dès le départ pour cet usage, comme remplaçante de C++.
On dirait que l’article a fini par devenir une critique du langage.
C'est ça. À l'origine, ça avait commencé pour améliorer le moteur interne de Firefox, non ?
Le contenu de l'article donne l'impression qu'ils ont essayé de développer avec Rust dans un domaine qui ne lui convenait pas vraiment, comme si c'était un remède miracle, et qu'ils en ont bavé haha
Je suis d’accord à 100 %. Pour la logique de jeu, un langage offrant une bonne productivité semble mieux correspondre au cas d’usage.
Comme je débute avec Rust, je m’y reconnais un peu.
C’était vraiment pénible de devoir modifier énormément de code à cause des changements liés au borrowing.
Contrairement à Python ou JavaScript, qui reposent beaucoup sur des expressions implicites, Rust a tendance à être explicite, ce qui peut produire un code verbeux, et comme il oblige aussi le programmeur à faire beaucoup de choix, ça peut être fatigant.
Malgré ça, il y a des concepts qu’on ne trouve pas dans d’autres langages, et je trouve ça rafraîchissant et amusant.
Sans Rust Analyzer ou GitHub Copilot, je pense que j’aurais abandonné plus tôt.
Le développement de jeux, c’est vraiment du hardcore...
Au lieu de tout écrire en Rust, du cœur jusqu’au front-end,
que penseriez-vous d’une approche à la Unity ou avec d’autres moteurs de jeu, où le cœur est écrit en Rust puis combiné avec un langage plus productif ?
Il y a forcément des limites, puisqu’il a tendance à imposer un cadre très strict.
Avis Hacker News
Partage de son expérience de développement d’un client de métavers et présentation des problèmes de Rust
En tant que développeur de jeux avec 20 ans d’expérience, il estime que Rust n’est pas adapté au développement de jeux
L’écosystème du développement de jeux en Rust repose sur le battage médiatique
Comparaison de l’expérience de développement avec Bevy, Unity et Godot
Le langage Nim pourrait être plus adapté que Rust au développement de jeux
Rust semble être un langage dogmatique qui impose une manière très spécifique de programmer
La démo d’outillage d’Allen Blomquist montre que la boucle de feedback du développement est importante
Le problème fondamental de Rust est d’avoir abandonné l’orienté objet au profit de la programmation fonctionnelle
Pour le développement de jeux, Rust est le pire choix
En codant en D, il a réalisé qu’il était bien plus facile de modifier la disposition des données qu’en C