Mit freundlicher Genehmigung von Inflexion Games

Umstieg bei Nightingale: So migrierte Inflexion Games mitten in der Entwicklung von der UE4 zur UE5

Jacques Lebrun, Technical Director für Nightingale bei Inflexion Games, und Noel Lukasewich, Environment Director bei Inflexion Games
Inflexion Games ist ein Studio aus dem kanadischen Edmonton, das 2018 von einer Gruppe von Branchenveteranen gegründet wurde. Seitdem ist es auf 100 leidenschaftliche Entwickler mit den verschiedensten Hintergründen angewachsen – einige von ihnen haben bereits AAA-Erfahrung, während andere noch ganz neu in der Branche sind. Aber sie alle sind in ihrem Bestreben vereint, Tiefgang, Geschichte und Möglichkeiten in die Spielewelt zu bringen. Zu ihren vorherigen Erfahrungen zählen beliebte Franchises wie Mass Effect, Dragon Age, Wipeout und Far Cry. Mit Unterstützung von Tencent bringt das Studio nun seinen ersten selbst veröffentlichten Titel heraus: ein Survival- und Crafting-Spiel namens Nightingale in einer geteilten Welt.
Wir sind Jacques Lebrun und Noel Lukasewich, zwei von mehreren Entwicklern bei Inflexion, die am Upgrade unseres kommenden Titels Nightingale auf die Unreal Engine 5 gearbeitet haben.
 
Unser Team war seit der Erstveröffentlichung der UE5 im letzten Jahr sehr gespannt auf die neuen Funktionen und Möglichkeiten, aber ein Projekt von der Unreal Engine 4 zu migrieren, wirkte anfangs wie eine beängstigende Aufgabe. In diesem Artikel gehen wir darauf ein, was für das Upgrade eines in Produktion befindlichen Projekts benötigt wird, und machen einige Vorschläge, die Ihnen möglicherweise dabei helfen werden, diesen Umstieg selbst zu vollziehen. 

Ein Hinweis zu Quellkontroll-Werkzeugen: Wir beziehen unsere Engine-Updates über Github und führen unsere eigene Quellkontrolle in Perforce durch, aber dieser Artikel hilft Ihnen vielleicht auch dann weiter, wenn Sie ein anderes Setup benutzen. 

Planung und Vorbereitung

Bevor wir über den Upgrade-Prozess an sich sprechen, wollen wir zunächst unsere Motivation erklären und erläutern, warum wir uns die Mühe gemacht haben, zu den ersten Nutzern der UE5 zu werden. 

Zu den wichtigsten Motivationsfaktoren zählten für uns die visuellen Verbesserungen, die durch die Kombination von Lumen und Nanite ermöglicht werden. Anfangs dachten wir, dass eine Migration unseres Projekts zu Nanite nicht möglich wäre, da keines unserer Assets mit Nanite erstellt worden waren (Spoiler: Wir sind letztendlich dennoch zu Nanite übergegangen, aber mehr dazu später), aber allein die Verbesserungen, die Lumen versprach, waren für uns überzeugend genug. Außerdem erwarteten wir viele Workflow-Verbesserungen in Bezug auf Open-World-Systeme, Asset-Management und die allgemeine Entwickler-Produktivität.
Mit freundlicher Genehmigung von Inflexion Games
Und da sich unser Projekt bereits auf einen Early Access- und Games-as-a-Service-Lebenszyklus zubewegte, wussten wir, dass es nur eine Frage der Zeit war, bis unsere Spieler-Community von unsere bessere Technologien und Visuals verlangen würde.

Nachdem wir festgelegt hatten, was unsere Motivation war, widmeten wir uns den Gesamtkosten des Upgrades. Epic schätzte die Kosten eines Upgrades von der UE4 auf die UE5 als etwa das Vierfache der Kosten eines kleineren Versionsupgrades der UE4 ein. Wir brauchten etwa einen bis zwei Kalendermonate für ein kleineres Upgrade, also rechneten wir mit sechs Kalendermonaten. Und diese Schätzung erwies sich als exakt richtig. 

Ansonsten konnten wir Hürden am leichtesten identifizieren, wenn wir mit der Arbeit begannen und die aufkommenden Probleme auswerteten. Die Hauptherausforderung war in unserem Fall die Migration zur Chaos-Physik, aber da wir nicht allzu viele selbst entwickelte Physikfunktionen in unserem Spiel hatten, fanden wir dieses Risiko überschaubar.
Mit freundlicher Genehmigung von Inflexion Games

Prozesse zur Handhabung eines Engine-Upgrades

Falls Sie an einem Blueprint-Projekt arbeiten, sollte das Upgrade der Engine sehr einfach sein: Da kein Code aktualisiert werden muss, können Sie Ihr Projekt einfach in die neue Engine-Version laden, sich ansehen, welche Asset-Fehler auftreten, und diese beheben.

Das Upgrade eines Code-Projekts ist etwas aufwendiger; deshalb gehen wir hier näher darauf ein. Die Hauptfrage ist, wie sehr Sie Ihre Engine angepasst haben. Wenn Sie die Engine unmodifiziert gelassen haben, sollte das Upgrade auch hier sehr unkompliziert sein; allerdings können erhebliche Anpassungen der Engine den Prozess entsprechend aufwendiger machen. Wir waren irgendwo in der Mitte und konnten den Upgrade-Prozess mithilfe der folgenden Methoden bewältigen.
Handhabung von Engine-Abweichungen
Es ist hilfreich, wenn Sie Ihre Änderungen an der Engine markieren und nachverfolgen, um den Prozess der Zusammenführung bei Code-Konflikten zu vereinfachen. Für Nightingale folgen wir diesem Prozess:
  • Alle Änderungen an der Engine werden von einer Gruppe von Prüfern ausgewertet. In unserem Fall sind das dieselben Programmierer, die auch für die Engine-Upgrades zuständig sind. Das macht es einfacher, Änderungen zu erkennen, die Schwierigkeiten bei der Wartung verursachen könnten.
  • Wir empfehlen, Epic so viele Änderungen wie möglich zuzusenden, siehe Zur Unreal Engine beitragen. Dadurch können wir Code-Unterschiede vor dem Übernehmen einer neuen Version entfernen.
  • Alle Änderungen werden im Code markiert und über ein Jira-Ticket nachverfolgt. Zum Beispiel:

// NTGL_BEGIN - NTGL-1234 - [VERBESSERUNG] Behebung einer nicht initialisierten Variable
// int IntVar;

int IntVar = 0;
// NTGL_END

 
  • Abweichungen werden wie folgt kategorisiert:
    • [VERBESSERUNG] - Änderung eignet sich als Zusendung für Epic
    • [TEMP] - Änderung wird nur temporär benötigt, meist bis zum nächsten großen Engine-Update
    • [PREPORT] - Änderung wurde aus dem Code eines kommenden Update abgerufen
    • [ABWEICHUNG] - Wir müssen diese Änderung für die Veröffentlichung unseres Spiels vornehmen, aber sie eignet sich nicht als Zusendung für Epic (Änderungen dieser Art versuchen wir im Allgemeinen zu vermeiden)
  • Mit dem Jira-Ticket können wir außerdem zusätzlichen Kontext rund um die Änderung überblicken, z. B. Links zum UEN, Links zu anderen Aufgaben, bei denen diese Änderung erforderlich war, und Links zu Aufgaben, bei denen diese Änderung entfernt werden musste.
  • Wenn eine Abweichung nicht problemlos mit einem Engine-Update zusammengeführt werden kann, entfernen wir in der Regel unsere Änderungen und implementieren sie im neuen Code wieder oder lassen sie komplett fallen.  
Integrationsprozess
Um eine unberührte Engine zu aktualisieren, reicht es meist aus, das Update über Epics Github herunterzuladen, alle Inhalte des Engine-Ordners zu ersetzen und einen Abgleich der Offline-Arbeit über P4V durchzuführen; allerdings wird das zunehmend schwerer, je mehr Veränderungen man am Engine-Code vorgenommen hat.

Mithilfe des Stream-Depots von Perforce haben wir folgende Zweige etabliert:
 
 
   Depot / Stream    Beschreibung

//ue4/release-4.27

//ue5/release-5.0

Unmodifizierte Mirrors der UE4-/UE5-Veröffentlichungen
//ntgl/dev
Hauptentwicklungsstream, in dem der Großteil des Teams arbeitet
//ntgl/integrate
Child-Stream des Dev-Streams, in dem Engine-Updates zusammengeführt werden
  • Wir erwarten, dass sich dieser Stream in einem fehlerhaften Zustand befindet, bis alle Probleme behoben wurden

Bevor Sie mit dem Upgrade auf UE5 beginnen, sollten Sie einen Revisionsverlauf erstellen, indem Sie die finale Version der UE4 (//ue4/release-4.27) mit Ihrem integrierten Stream zusammenführen. Falls Sie nicht bereits Version 4.27 nutzen, erwägen Sie möglicherweise, gleich mehrere Versionen zu überspringen. Unserer Erfahrung nach ist es jedoch meist einfacher, Schritt für Schritt zu aktualisieren – eine Version nach der anderen.

Anschließend sollten Sie den Zweig "release-5.0" wie folgt einrichten:
  • p4 copy //ue4/release-4.27 -> //ue5/release-5.0 (ursprünglich als Kopie von 4.27 erstellt)
  • Aktualisieren Sie die Inhalte von //ue5/release-5.0 mit denen der offiziellen Version

Die Aktualisierungs- und Zusammenführungsschritte sehen nun so aus:
  1. Alle Änderungen aus Ihrem Entwicklungsstream in einem Integrationsstream zusammenführen
    • Falls durch die Inhaltsänderungen je Konflikte auftreten, akzeptieren Sie immer die Dev-Version, damit Sie keine Arbeit Ihrer Inhaltsersteller verlieren.
  2. Mirror der gewünschten Unreal-Version in einem separaten Perforce-Depot erstellen (z. B. //ue5/release-5.x)
    • Dieser Mirror sollte unverändert bleiben und dient dazu, die Engine-Änderungen im Revisionsverlauf von Perforce nachzuverfolgen, um die Zusammenführung zu erleichtern.
  3. Inhalte aus //ue5/release-5.x zusammenführen/integrieren -> //ntgl/integrate
  4. Zusammenführungskonflikte lösen und einreichen

All diese Schritte können manuell durchgeführt werden, es ist jedoch sehr nützlich, sie durch Scripting zu automatisieren.

Nach der Zusammenführung der Updates lässt sich das Engine-Upgrade in folgende Schritte unterteilen:
  1. Warnungen und Fehler zur Code-Projekterstellung beheben
  2. Kompilierungs- und Linkfehler beheben
  3. Editor- und Cook-Fehler beheben
  4. Autotestfehler beheben
  5. Spielfehler beheben
Automatisierung
Zeit in die Automatisierung zu stecken kann wirklich hilfreich dabei sein, den Upgrade-Prozess zu optimieren. Im Idealfall sollten Sie einen wiederholbaren Build-Prozess erreichen, der in Ihrem Integrationsstream ausgeführt werden kann, um vor der Zusammenführung mit dem Dev-Stream sicherzustellen, dass alles funktioniert. Diese zusätzlichen Bereiche haben uns geholfen:
  • Die Erstellung und Pflege einer umfassenden Suite aus automatisierten Tests kann eine große Hilfe sein, um noch vor der Durchführung von manuellen Tests viele Fehler zu erkennen. Bei unserer Migration zur UE5 konnten unsere automatisierten Tests zum Beispiel viele Inkonsistenzen zwischen der Physik-Engine der UE4 und der Chaos-Physik der UE5 aufdecken.
  • Scripts können die Erstellung von Mirrors und verschiedene Zusammenführungsschritte stark beschleunigen.
  • Durch das Hinzufügen zusätzlicher Automatisierungsprüfungen, die Asset-Validatoren ausführen und alle Blueprints (-run=CompileAllBlueprints) kompilieren, bevor Sie zum Cooking Ihres Spiels übergehen, können Sie viele zusätzliche Asset-Fehler entdecken.
  • Wenn Sie Ihren Integrationsstream häufig genau den Build-Prozess durchlaufen lassen, den auch Ihr Dev-Stream durchläuft, können Sie auch verborgene Randfälle leichter vor der endgültigen Zusammenführung erkennen.
  • Wenn Sie Systeme aufbauen, mit denen die Test-Builds Ihrer Integrationsstreams verteilt werden können, um breiter angelegte Spieltests zu ermöglichen, können Sie Probleme im Spiel früh bemerken.
Tipps und Tricks
Die folgende Auswahl an Tipps befolgen auch wir bei der Optimierung des Upgrade-Prozesses.
  • Bei den meisten großen Engine-Upgrades werden die meisten Dateien automatisch angepasst, aber am Ende erhalten Sie trotzdem ein Änderungsverzeichnis mit Tausenden von Änderungen. Mit dem folgenden p4-Befehl können Sie die Dateien in ein separates Änderungsverzeichnis eintragen lassen, die manuell angepasst werden müssen:

p4 -F %localPath% resolve -n | p4 -x - reopen -c [new changelist #] 
 
  • Wenn Sie Kompilierungs- und Linkfehler beheben, sollten Sie mit -DisableUnity kompilieren. Dadurch wird die Kompilierungsoptimierung im UnrealBuildTool deaktiviert, die viele .cpp-Dateien in eine kombiniert. Das Kompilieren dauert dadurch etwas länger, aber es wird einfacher, Kompilierungsprobleme nachzuverfolgen und Probleme mit fehlenden Include-Statements zu erkennen.
  • Wenn Sie die Unreal Engine über GitHub herunterladen, müssen Sie sich entscheiden, was Sie mit den Dateien aus GitDependencies.exe machen möchten. Sie können diese Dateien entweder individuell von Entwicklern aktualisieren lassen oder sie – wie wir – in die Mirror-Automatisierungs-Scripts einbauen und in einem eigenen Perforce-Mirror verwenden.
  • Achten Sie auf die Warnungen der Unreal Engine zu veralteten Elementen, da diese Ihnen beim Upgrade Ihres Projekts oft den richtigen Weg weisen. Sorgen Sie dafür, dass bei der Kompilierung 0 Warnungen ausgegeben werden, dann ist es viel einfacher, diese veralteten Elemente im Griff zu behalten.
  • Wenn Sie all Ihre Assets in Ihre Integrationsstreams einschließen, können Sie bei Bedarf auch individuelle Assets upgraden. Das mussten wir zwar häufig nicht, aber es kann recht umständlich sein, diese Assets zu verwalten, wenn sie in Ihrem Hauptentwicklungsstream häufig aktualisiert werden. Die folgenden Vorschläge können dabei helfen:
    • Wenn Sie ein paar minimale Engineänderungen für Ihren Dev-Stream auswählen und die Assets im Dev-Stream aktualisieren, wird die Möglichkeit von Konflikten eliminiert.
    • Falls es möglich ist, die Assets über ein Commandlet zu aktualisieren, können Sie ein Commandlet erstellen, das nach jeder Zusammenführung ausgeführt wird, damit alle relevanten Assets auf dem neuesten Stand bleiben. In vielen Fällen muss ein Asset einfach nur erneut in der neuesten Version des Editors gespeichert werden.
  • Epic ermöglicht es Ihnen, auf die Veröffentlichungszweige zuzugreifen, noch bevor die Version als frühe Vorschau verfügbar ist. So sind Sie für die Zusammenführung einer bevorstehenden Veröffentlichung gut vorbereitet und können einschränken, wie viel Code Sie jeweils zusammenführen möchten. Außerdem können Sie durch tägliche Zusammenführungen aus Ihrem Dev-Stream schwierige Zusammenführungen so früh wie möglich abfangen und mit den zuständigen Entwicklern zusammenarbeiten, um Probleme zu lösen.
  • Falls Sie Plugins von Dritten nutzen müssen (vom Marketplace oder von anderen Anbietern), sollten Sie besser ein wenig warten, bis diese Plugins aktualisiert wurden. Wir möchten ein Engine-Upgrade oft schon vor diesen Updates durchführen, aber das führt zu zusätzlichem Aufwand bei der Aktualisierung dieser Plugins, den wir uns durch längeres Warten sparen können.
  • Jedes neue Engine-Upgrade hat äußerst nützliche Funktionen im Gepäck. So verlockend es auch ist, diese neuen Funktionen sofort zu nutzen, wollen wir die Engine trotzdem erst aktualisieren, wenn wir dazu nur möglichst wenige Änderungen an Spiel- und Entwickler-Workflows vornehmen müssen. Wir probieren neue Funktionen erst dann aus, wenn wir uns sicher sind, dass das Upgrade stabil in unserem Dev-Stream läuft.
 

Vorteile der Unreal Engine 5

Das Upgrade auf Lumen und Nanite war für uns sehr vielversprechend. Wer wünscht sich nicht präziseres reflektiertes Licht und die Möglichkeit, das Poly-Budget für sein Spiel deutlich zu erhöhen?
Mit freundlicher Genehmigung von Inflexion Games
Auch wenn sowohl Lumen als auch Nanite leicht zu aktivieren sind, dauerte es trotzdem eine Weile, Nightingale dahingehend vorzubereiten, da es ursprünglich für die UE4 optimiert wurde. Alle Komponenten unserer Pipeline folgten bei Nightingale folgte bereits streng den PBR-Regeln, daher ließ Lumen unser Spiel nach seiner Integration recht schnell erstrahlen, während wir nach der Konvertierung unserer Meshes zu Nanite noch einige Optimierungen vornehmen mussten.
Mit freundlicher Genehmigung von Inflexion Games
Die Konvertierung unseres Projekts zu Nanite hat länger gedauert, da wir auch von den Virtual Shadow Maps profitieren wollten. Die Unreal Engine 5 unterstützt die Interoperabilität von Nanite- und Nicht-Nanite-Assets, daher konnten wir die Konvertierung Schritt für Schritt durchführen und hatten während unseres gesamten Übergangs stets ein komplett spielbares Erlebnis. Allerdings kann man mit Virtual Shadow Maps eine bessere Performance erreichen, wenn man mehr Assets zu Nanite konvertiert, also besteht ein gewisser Anreiz, diese Konvertierung durchzuführen, um zu verstehen, wie die Performance des Spiels aussehen wird.

Die Konvertierungsprozesse für einzelne Assets fielen meist in eine dieser Kategorien: 
  • Einige Assets hatten bereits eine ausreichend hohe Detailtreue und wir haben einfach Nanite für das Mesh aktiviert. 
  • Für einige Assets war es von Vorteil, mit einer höheren Detailtreue neu erstellt zu werden, also erstellten wir sie mit Nanite im Hinterkopf neu. 
  • Unsere von Spielern gebauten Strukturen hatten einen speziellen Shader, der die Struktur transparent renderte, wenn der Spieler sie platzierte. Da Nanite nur mit undurchsichtigen Objekten unterstützt wird, haben wir ein System implementiert, bei dem wir auf ein Nicht-Nanite-Mesh mit Transparenz zurückgreifen, wenn sich das Mesh in diesem Platzierungsmodus befand.
Mit freundlicher Genehmigung von Inflexion Games
Zudem war es für unsere Workflows auch hilfreich, Level-Instanzen und Packed Level Actors einzuführen. In der UE4 hatten wir Probleme mit verschiedenen Implementierungen von Fertigobjekten, aber die Einführung von Packed Level Actors schuf für uns neue Möglichkeiten für die prozedurale Zusammensetzung von Umgebungen in Nightingale.
Mit freundlicher Genehmigung von Inflexion Games
Mit dem Versionsaufstieg von 5.0 auf 5.2 bemerkten wir Verbesserungen bei vielen Funktionen der Engine: das Rendering volumetrischer Wolken und das Vegetationsrendering mit Lumen war besser. Letzten Endes konnten wir durch die Kombination von Nanite, Lumen und Virtual Shadow Maps ziemlich beeindruckendes Bildmaterial erreichen.
Mit freundlicher Genehmigung von Inflexion Games
Wir sind sehr zufrieden damit, wie Nightingale nach dem evolutionären Prozess aussieht, den es durch die UE5 unterlaufen hat. Die von uns erreichte Wiedergabetreue wäre ohne Lumen und Nanite nicht möglich gewesen, und wir wollen diesen Weg auch bei zukünftigen Updates von Nightingale und der Unreal Engine 5 gehen.

    Laden Sie die Unreal Engine 5 heute herunter!

    Bestehende Unreal-Engine-Nutzer können die Unreal Engine 5 über den Epic Games Launcher herunterladen. Wenn Sie die Unreal Engine zum ersten Mal ausprobieren, klicken Sie auf den unten angegebenen Link. In jedem Fall hoffen wir, dass Ihnen die neuen Funktionen und Verbesserungen gefallen, und freuen uns wie immer über Feedback!