Brian Bucklew est en train de porter « Caves of Qud » de Unity vers Godot
(twitter.com/unormal)- « Caves of Qud » a mené une validation technique pour se débarrasser de sa longue dépendance à Unity et construire/démarrer le cœur du jeu dans Godot ; l’objectif prioritaire est d’abord de le faire tourner en mode ASCII+tuiles sans VFX ni UI modernes
- Le travail a été divisé entre l’import des assets de tuiles, le portage des assemblages C# principaux et la mise en place du rig de rendu et d’entrée ; les anciens fichiers de tuiles BMP ont été convertis en PNG puis chargés dans Godot
- Les 5 641 erreurs de la build initiale ont été réduites en déplaçant GeneratedCode, ConsoleLib, Genkit, Language, HistoryKit, Newtonsoft JSON, CodeDom, etc., tandis que la surface de dépendance à UnityEngine a été progressivement réduite à des stubs
- Les usages de Color, GameObject, AudioSource, Debug.Log et Screen de Unity ont été traités via UnityEngineReplacer et des classes de remplacement, tandis que les couches chargen et presentation, plus proches de PlayFab et de l’UI Unity, ont été temporairement retirées ou laissées comme candidates à une séparation en modules glue
- Au final, le cœur du jeu en C# d’environ 500 000 lignes démarre désormais dans Godot, fournit des frames et attend des entrées, mais le renderer, le harnais d’entrée ainsi que le rigging des VFX, du son et de l’UI restent à faire
Étendue du travail de portage de Unity vers Godot
- Le portage se divise en trois branches
- Import des assets : intégrer les assets de tuiles dans le projet Godot
- Portage des assemblages principaux : déplacer
XRL Application, qui constitue le moteur principal du jeu hors Unity, ainsi que leGameManagercôté moteur - Mise en place du rig de rendu : relier l’affichage et les entrées une fois le cœur démarré
- Un projet Godot « mobile » a été créé et le contenu des textures de Qud a été copié dans le dossier racine du projet
- Comme Godot ne traitait pas les anciens fichiers
.bmp, les fichiers incluant les tuiles ASCII ont été convertis en masse en PNG - Après conversion, les assets se chargeaient, mais il fallait environ 30 secondes avant de voir apparaître l’indicateur de progression de l’import
Réduire les erreurs de build tout en déplaçant les assemblages du cœur
- Après avoir copié
XRL Application, un projet et une solution C# ont été générés dans Godot, et l’état initial produisait 5 641 erreurs - Des bibliothèques génériques comme
ConsoleLibetGenkitont été déplacées en bloc, tandis que l’ancienne solution de sprites/atlasing Kobold devait être importée fichier par fichier selon les besoins KoboldJSON, simple bibliothèque de sérialisation JSON, a été déplacée telle quelle- Une substitution globale des 85 points d’usage de
Colora ramené les erreurs à 4 700 - L’ajout du dossier manquant
GeneratedCodea inclus les résultats de génération de code des classes par événement, réduisant les erreurs à 1 557- Caves of Qud s’appuie sur une architecture qui génère le code de classes d’événements très verbeuses afin d’obtenir de meilleures performances et une surface d’événements fortement typée
Nettoyage des dépendances à Unity et de la couche glue
Embark Builderet la création de personnage chargen contenaient beaucoup de code d’UI Unity ; ils ont donc été retirés de la validation technique ASCII initiale- Il n’existe pas encore de version console, mais la validation initiale peut être testée en chargeant une sauvegarde ou via un démarrage aléatoire
- Le dossier
Gamesert majoritairement de glue entre Unity et le jeu, mais comme certains sous-dossiers tels queCodeGenerationne relèvent pas de cette glue, la direction choisie a été de les déplacer vers une racine distincte ou un dossierPlatform - Le portage des bibliothèques
LanguageetHistoryKita fait tomber les erreurs à 454, aidé par le fait que la séparation jeu/glue existait déjà, même imparfaitement - Les grandes catégories d’erreurs restantes concernaient
CodeDom/Roslyn,PlayFab,Harmony, certains problèmes liés au compilateur, ainsi que la surface UnityEngine qui fuyait vers la couche jeu - Le code lié à
PlayFaba été temporairement commenté et reste candidat à un déplacement dans un module glue
Stubs de remplacement de l’API Unity et différences du C# de Godot
Color32, type de couleur basé sur des bytes, a reçu rapidement une implémentation de remplacement- Un dossier
UnityEngineReplacera été créé et seule la surface nécessaire a été implémentée au fil des erreurs de compilation, sans s’appuyer sur la documentation ou le code de référence de Unity - La création d’un stub
GameObjecta transformé l’erreur « GameObject inconnu » en erreurs plus concrètes sur les champs et méthodes requis, ce qui a permis de cartographier la surface d’interface réellement utilisée par le jeu - Pour
AudioSource, les membres accédés ont été ajoutés un à un en suivant la liste d’erreurs, jusqu’à compléter l’interface de portageAudioSourceeffectivement utilisée par le jeu - Les autres surfaces traitées comprenaient notamment
- un shim de remplacement pour
Debug.Log - une implémentation de remplacement pour
Screen - le portage d’une bibliothèque de méthodes d’extension
IsNullOrEmpty - la gestion des références à
Mathet de la différence entrePIetPi - la gestion de la différence de casse entre
X,Yde Godot - le nettoyage des imports incorrects de
System.Drawing.ColoretSystem.Numerics.Vector3ajoutés par l’IDE
- un shim de remplacement pour
- Newtonsoft JSON a été résolu en ajoutant le package NuGet dans Visual Studio, et CodeDom via le package NuGet
CodeDom
Jusqu’au démarrage complet dans Godot
- Quand les erreurs ne sont plus tombées qu’à 11, les problèmes restants concernaient le code de compilation dynamique C# de la gestion des mods ; ce n’était pas indispensable, mais suffisamment complexe pour rester jusqu’à la fin
- Après traitement de l’étape de linkage et des erreurs de champs dans les stubs, environ 500 000 lignes de C# ont pu être compilées
- L’étape suivante consistait à créer un petit renderer et un harnais d’entrée pour démarrer les assemblages du cœur, avec pour objectif de rendre le jeu exécutable en mode ASCII+tuiles
- Une scène vide a été créée dans Godot,
GameManager.csa été attaché au nœud principal, etGameManagerdevait hériter deNodeavec du code partial _Readydans Godot a été utilisé comme équivalent deAwakedans Unity, et_Processcomme équivalent deUpdate- Pendant l’initialisation, le load path et la gestion des mods ont été portés, puis un problème où le resolver de types ne retrouvait pas les types par leur nom a été traqué à coups de
printf - La cause était la gestion des dynamic assembly
- comme les mod assembly étaient dynamiques, l’inspection de l’assembly principal excluait les dynamic assembly
- dans l’éditeur Godot, l’assembly principal du jeu était lui aussi dynamique et se retrouvait donc exclu de la recherche de types
- Après correction, le démarrage complet a réussi, et le cœur du jeu de 500 kloc fournit désormais des frames en attendant les entrées
- Le travail restant a été résumé par « just work », mais en réalité il reste un volume important de travail sur les VFX, le son et le rigging de l’UI
1 commentaires
Avis Hacker News
Ce jeu semble utiliser presque entièrement un moteur maison, Unity ne servant que de couche d’abstraction matérielle et de cadre pour le portage ; en termes de difficulté de portage, on dirait donc presque le cas idéal
Il existe étonnamment beaucoup de jeux de ce type, mais bien sûr cela ne représente pas la plupart des titres Unity
Je ne sais pas si c’est encore le cas après la refonte de l’UI, mais si oui, j’ai toujours trouvé étrange qu’ils ne l’aient pas porté vers MonoGame pour économiser sur les coûts
Android est similaire : le système d’exploitation finit par ne servir qu’à charger des bibliothèques remplaçant des choses comme la détection de caméra ou le chiffrement, jusqu’au jour où l’on réalise que l’OS n’est en fait guère plus qu’une fine couche de métal et un bootloader
https://nitter.net/unormal/status/1703163364229161236
Bon sang, Elon. Twitter est vraiment devenu pénible à utiliser
Waouh, c’est vraiment chouette. C’est aussi intéressant de pouvoir voir ce processus de portage étape par étape, et je suis surpris que le temps nécessaire soit plutôt raisonnable
S’il y a autant de code custom et que la structure utilise moins les fonctionnalités de l’éditeur, on pourrait aussi utiliser une bibliothèque de rendu plutôt qu’un moteur
Avec un moteur en dessous, on obtient tout ça gratuitement. Avec Unity, il peut aussi y avoir un coût
Si vous voulez savoir ce qu’en pense Brian, cette vidéo est intéressante : https://www.youtube.com/watch?v=U03XXzcThGU
J’y ai beaucoup appris à l’époque
Ça me rappelle la récente controverse autour de TypeScript de DHH
Imaginer faire un tel portage sans typage statique : à chaque changement, il faudrait compiler, lancer et chercher les plantages. Dans ces moments-là, on est vraiment reconnaissant d’avoir construit le tout avec un langage facile à refactorer et une bonne architecture
Je n’ai pas de compte Twitter, qu’est-ce qui se passe ?