- „Caves of Qud“ hat eine technische Machbarkeitsprüfung durchgeführt, bei der die langjährige Unity-Abhängigkeit entfernt und der Spielkern in Godot gebaut und gebootet wurde; das erste Ziel ist, es ohne moderne VFX oder UI im ASCII- plus Tile-Modus lauffähig zu machen
- Die Arbeit gliederte sich in den Import von Tile-Assets, die Portierung zentraler C#-Assemblies und den Aufbau einer Rendering- und Input-Rig; alte BMP-Tile-Dateien wurden in PNG konvertiert und in Godot geladen
- Die 5.641 Fehler des ersten Builds wurden reduziert, indem GeneratedCode, ConsoleLib, Genkit, Language, HistoryKit, Newtonsoft JSON, CodeDom usw. übernommen wurden, während die Abhängigkeitsoberfläche zu UnityEngine mit Stubs schrittweise verkleinert wurde
- Einsatzstellen von
Color,GameObject,AudioSource,Debug.LogundScreenaus Unity wurden mit UnityEngineReplacer und Ersatzklassen behandelt; chargen- und presentation-Schichten nahe an PlayFab bzw. Unity UI wurden vorübergehend entfernt oder als Kandidaten für ausgelagerte Glue-Module belassen - Im Ergebnis ist der rund 500.000 Zeilen umfassende C#-Spielkern nun in Godot so weit bootfähig, dass er Frames liefert und auf Eingaben wartet, aber Arbeiten an Renderer, Input-Harness sowie VFX-, Sound- und UI-Rigging stehen noch aus
Umfang der Portierung von Unity nach Godot
- Die Portierung teilt sich in drei Bereiche
- Asset-Import: Tile-Assets in das Godot-Projekt übernehmen
- Portierung der Kern-Assemblies: die nicht zu Unity gehörende Haupt-Spiel-Engine
XRL Applicationund den Engine-seitigenGameManagerübertragen - Aufbau des Rendering-Rigs: nach dem Booten des Kerns Bildausgabe und Eingaben anbinden
- Es wurde ein Godot-„mobile“-Projekt erstellt und Quds Textur-Content in den Projekt-Root-Ordner kopiert
- Da Godot alte
.bmp-Dateien nicht verarbeiten konnte, wurden Dateien einschließlich der ASCII-Tiles gesammelt in PNG konvertiert - Nach der Konvertierung wurden die Assets geladen, allerdings dauerte es etwa 30 Sekunden, bis überhaupt ein Import-Fortschrittsbalken erschien
Kern-Assemblies übertragen und Build-Fehler reduzieren
- Nach dem Kopieren von
XRL Applicationwurden in Godot ein C#-Projekt und eine Solution erzeugt; im Ausgangszustand traten 5.641 Fehler auf - Universelle Bibliotheken wie
ConsoleLibundGenkitwurden vollständig übernommen, während bei Kobold, einer älteren Sprite- und Atlasing-Lösung, dateiweise geprüft wurde, was wirklich benötigt wird KoboldJSONwurde als einfache JSON-Serialisierungsbibliothek unverändert übernommen- Durch globales Ersetzen der 85 Einsatzstellen von
Colorsank die Fehlerzahl auf 4.700 - Nachdem der fehlende Ordner
GeneratedCodeergänzt wurde, kamen die generierten Klassen für einzelne Events hinzu und die Fehlerzahl sank auf 1.557- Caves of Qud nutzt eine Struktur, in der eventbezogene Klassen mit viel Boilerplate codegeneriert werden, um Performance und eine stark typisierte Event-Oberfläche zu erhalten
Unity-Abhängigkeiten und die Glue-Schicht aufräumen
Embark Builderund die Charaktererstellung chargen enthalten viel Unity-UI-Code und wurden für den ersten ASCII-Techniknachweis entfernt- Eine Konsolenversion existiert noch nicht, aber der frühe Nachweis lässt sich durch Laden von Spielständen oder einen zufälligen Start testen
- Der Ordner
Gameist größtenteils Glue zwischen Unity und dem Spiel, enthält aber auch nicht zur Glue-Schicht gehörende Ordner wieCodeGeneration; deshalb wurde entschieden, diese in ein separates Root oder einen OrdnerPlatformzu verschieben - Durch die Übernahme der Bibliotheken
LanguageundHistoryKitsank die Fehlerzahl auf 454; hilfreich war, dass Spiel- und Glue-Schicht trotz Unvollkommenheiten bereits teilweise getrennt waren - Die verbleibenden großen Fehlerkategorien waren
CodeDom/Roslyn,PlayFab,Harmony, einige compilerbezogene Probleme und eine in die Spielebene durchgesickerte UnityEngine-Oberfläche - Mit
PlayFabzusammenhängender Code wurde vorübergehend auskommentiert und als Kandidat für ein Glue-Modul belassen
Ersatz-Stubs für Unity-APIs und Unterschiede in Godot C#
Color32ist ein bytebasierter Farbtyp, daher wurde schnell eine Ersatzimplementierung geschrieben- Es wurde ein Ordner
UnityEngineReplacerangelegt, und ohne Unity-Dokumentation oder Code-Referenz wurde anhand der Compilerfehler nur die tatsächlich benötigte Oberfläche implementiert - Sobald ein
GameObject-Stub vorhanden war, wandelten sich Fehler wie „GameObject ist unbekannt“ in Fehler für tatsächlich benötigte Felder und Methoden, wodurch sichtbar wurde, welche Interface-Oberfläche das Spiel wirklich nutzt - Auch bei
AudioSourcewurden anhand der Fehlerliste die angesprochenen Member einzeln ergänzt, bis die vom Spiel tatsächlich verwendete vollständige AudioSource-Port-Schnittstelle vorhanden war - Weitere behandelte Oberflächen waren unter anderem
- Ersatz-Shim für
Debug.Log - Ersatzimplementierung für
Screen - Portierung einer Bibliothek mit der Erweiterungsmethode
IsNullOrEmpty - Behandlung von
Math-Referenzen und Unterschieden zwischenPIundPi - Behandlung der unterschiedlichen Großschreibung von Koordinatennamen
XundYin Godot - Bereinigung falsch von der IDE importierter
System.Drawing.ColorundSystem.Numerics.Vector3
- Ersatz-Shim für
- Newtonsoft JSON wurde durch Hinzufügen des NuGet-Pakets in Visual Studio gelöst, CodeDom über das NuGet-Paket
CodeDom
Bis zum vollständigen Boot in Godot
- Als die Fehlerzahl auf 11 gesunken war, betraf das verbleibende Problem Code zur dynamischen C#-Kompilierung im Mod-Management; nicht zwingend notwendig, aber komplex genug, um bis zuletzt offen zu bleiben
- Danach wurden noch Link-Schritt-Probleme und Stub-Feldfehler behoben, sodass rund 500.000 Zeilen C# gebaut werden konnten
- Der nächste Schritt war, einen kleinen Renderer und ein Input-Harness zu erstellen, um die Kern-Assembly zu booten; Ziel war, das Spiel im ASCII- plus Tile-Modus ausführbar zu machen
- In Godot wurde eine leere Scene erstellt und
GameManager.csan den Basis-Node gehängt;GameManagererbte vonNodeund benötigte partial Code - Godots
_Readywurde entsprechend UnitysAwakeverwendet,_Processentsprechend UnitysUpdate - Im Initialisierungsprozess wurden Load-Pfade und das Mod-Management portiert, und das Problem, dass der Type-Resolver Typen nicht anhand ihrer Namen fand, wurde per printf-artigem Tracing untersucht
- Die Ursache lag in der Behandlung dynamischer Assemblies
- Da Mod-Assemblies dynamisch waren, wurden dynamische Assemblies bei der Prüfung der Main-Assembly ausgeschlossen
- Im Godot-Editor ist aber auch die Main-Game-Assembly dynamisch und fiel deshalb aus der Typensuche heraus
- Nach der Korrektur gelang der vollständige Boot, und der 500-kloc-Spielkern liefert nun Frames und wartet auf Eingaben
- Die verbleibende Arbeit wurde zwar als „just work“ beschrieben, tatsächlich steht aber noch erheblicher Aufwand für VFX, Sound und UI-Rigging aus
1 Kommentare
Hacker-News-Kommentare
Dieses Spiel nutzt offenbar fast vollständig eine eigene Engine, und Unity dient nur als Hardware-Abstraktionsschicht und als Gerüst fürs Portieren; in Sachen Portierungsaufwand wirkt das daher fast wie ein Best-Case-Szenario.
Es gibt überraschend viele Spiele dieser Art, aber natürlich ist das nicht repräsentativ für die meisten Unity-Titel.
Ich weiß nicht, ob das nach der UI-Überarbeitung immer noch so ist, aber falls ja, wirkte es auf mich immer seltsam, dass sie nicht auf MonoGame portiert und damit Kosten gespart haben.
Bei Android ist es ähnlich: Das Betriebssystem wird nur noch dafür benutzt, Libraries zu laden, die Dinge wie Kameraerkennung oder Verschlüsselung ersetzen, bis man eines Tages merkt, dass das Betriebssystem im Grunde nur noch eine dünne Metallschicht und ein Bootloader ist.
https://nitter.net/unormal/status/1703163364229161236
Verdammt, Elon. Twitter zu benutzen ist inzwischen wirklich nervig.
Wow, wirklich großartig. Es ist schön, diesen Portierungsprozess Schritt für Schritt sehen zu können, und ich bin überrascht, dass der Zeitaufwand ziemlich vernünftig war.
Wenn es so viel Custom-Code gibt und die Editor-Funktionen weniger genutzt werden, könnte man statt einer Engine auch eine Rendering-Library verwenden.
Mit einer Engine als Basis bekommt man diese Dinge gratis dazu. Bei Unity könnten allerdings Kosten anfallen.
Wenn man wissen will, was Brian darüber denkt, sollte man sich dieses Video ansehen: https://www.youtube.com/watch?v=U03XXzcThGU
Ich habe früher viel daraus gelernt.
Das erinnert mich an die jüngste TypeScript-Kontroverse um DHH.
Wenn man sich vorstellt, so eine Portierung ohne statische Typen zu machen, müsste man nach jeder Änderung bauen, ausführen und nach Abstürzen suchen. In solchen Momenten ist man wirklich dankbar, wenn etwas in einer refactoring-freundlichen Sprache und mit guter Architektur gebaut wurde.
Ich habe keinen Twitter-Account, was ist los?