BEVOR DU BEGINNST

Wie die Unreal Engine denkt

Dies ist kein weiteres Thema des Kurses, sondern ein Kompass über allen anderen. Zwölf "Warums", die die Engine immer wieder mit derselben Denkweise löst — sobald du die Argumentationslinie selbst verstehst, hört jedes weitere Engine-System auf, eine neue Faktenmenge zum Auswendiglernen zu sein, und wird zur vorhersehbaren Konsequenz derselben Überlegung.

Projektregel: weniger Magie nach jedem Thema

Das Ziel des Kurses ist nicht, "die API zu zeigen". Das Ziel ist, dass nach jedem Kapitel das, was zuvor wie Engine-Magie aussah, zur logischen Konsequenz der Architektur wird. Nach dem Reflection-Kapitel ist klar, warum UCLASS, UPROPERTY und UFUNCTION gebraucht werden. Nach dem Lifecycle-Kapitel ist klar, warum bestimmte Dinge nicht im Konstruktor erledigt werden können. Nach dem Gameplay-Framework ist offensichtlich, warum die Logik zwischen GameMode, PlayerController, PlayerState und Character aufgeteilt wird. Diese Seite ersetzt diese Themen nicht, sondern ist ihr gemeinsamer Nenner: Die elf verschiedenen "Warums" unten sind eigentlich Varianten derselben Frage, gestellt an unterschiedliche Teile der Engine.

Das Hauptziel des Lernens

Nach Abschluss des Kurses sollte der Leser lernen, eigenständig ingenieurtechnische Entscheidungen in der Unreal Engine zu treffen — nicht Fakten über bestimmte Klassen auswendig zu lernen, sondern in der Lage zu sein: die Architektur eines neuen Spielsystems selbstständig zu entwerfen; Unreal-Engine-Quellcode ohne Angst vor einer unbekannten Datei zu lesen; die Gründe für die Existenz jedes Engine-Systems zu verstehen, auch eines, das nicht im Kurs behandelt wird; eine Lösung in einem unbekannten Teil der Engine durch Analogie zu einer bereits verstandenen Denkweise zu finden, statt durch Ausprobieren; die Kompromisse hinter einer bestimmten architektonischen Entscheidung von Epic zu verstehen — und sie in Worten formulieren zu können, nicht nur das Ergebnis zu zitieren.

Das Hauptziel des Kurses ist, die Unreal-Engine-Dokumentation für den Leser irgendwann überflüssig zu machen. Nicht weil er alles auswendig gelernt hat, sondern weil er gelernt hat, die Argumentation zu reproduzieren, mit der die Dokumentation selbst geschrieben wurde.

Warum ist im Gameplay-Code fast alles ein UObject?
Weil "ein UObject zu sein" keine Vererbung um der Vererbung willen ist, sondern der einmalige Erwerb von drei Dingen gleichzeitig: Sichtbarkeit für den Garbage Collector, Sichtbarkeit für Reflection (und damit für Blueprint/den Editor) und ein einheitlicher Serialisierungsmechanismus. Eines dieser drei Systeme von Grund auf für einen "leichtgewichtigen" Typ ohne UObject zu schreiben, bedeutet, ein bereits gelöstes Problem ein zweites Mal zu lösen.
Vertiefung → UObject: Objektarchitektur
Warum ist fast alles ein Zeiger, kein Wert?
Ein UObject ist eine Einheit von Identität und Eigentümerschaft (über Outer), die der Garbage Collector anhand der Adresse des konkreten Objekts im Speicher verfolgt, nicht anhand seines Werts. Ein UObject "nach Wert" zu kopieren würde entweder diese Identität brechen (zwei verschiedene Objekte mit denselben Daten sind für GC und Reflection nicht dasselbe), oder heimlich doch per Referenz kopieren — also macht die Engine diese Referenznatur über einen Zeiger explizit, statt sie zu verbergen.
Vertiefung → Smart Pointers und GC
Warum gibt es einen Garbage Collector?
Der Graph der Spielobjekte in Echtzeit ist kein Baum mit einem einzigen Eigentümer, sondern ein Netzwerk aus Querverweisen (ein Actor referenziert eine Component, eine Component referenziert einen anderen Actor, der wiederum zurückreferenziert), bei dem manuelles Nachverfolgen von "wer hat zuletzt aufgehört zu referenzieren" ohne Lecks oder vorzeitige Freigabe praktisch unmöglich ist. Der GC löst dies, indem er die Erreichbarkeit von der Wurzel aus verfolgt, statt manuell Referenzen zu zählen.
Vertiefung → Garbage Collector
Warum gibt es Reflection?
Der Editor, Blueprint und das Serialisierungssystem müssen zur Laufzeit über die Felder und Funktionen deiner C++-Klasse Bescheid wissen — etwas, das C++ grundsätzlich nicht bietet (die Sprache hat keine Laufzeitinformationen über beliebige Klassenfelder). Das Unreal Header Tool löst dies, indem es diese Beschreibung im Voraus aus Makros wie UPROPERTY generiert, statt die Engine zu zwingen, aus dem Binärcode zu raten.
Vertiefung → Reflection
Warum gibt es Component?
Weil die Fähigkeiten von Gameplay-Entitäten ("hat Health", "hat Inventory") eine "hat-ein"-Beziehung sind, keine "ist-ein"-Beziehung, und Vererbung, die "ist-ein" korrekt modelliert, bei der Anwendung auf "hat-ein" zu einer kombinatorischen Explosion von Klassen für jede neue unabhängige Fähigkeit führt.
Vertiefung → UActorComponent
Warum Composition statt Inheritance?
Weil Composition als einziger der drei versuchten Ansätze (Vererbung, Interfaces, Templates) gleichzeitig unabhängiges Ein-/Ausschalten einer Fähigkeit, physische Speicherung von Zustand und volle Reflection-/Blueprint-Sichtbarkeit bietet — Interfaces bieten nur einen Vertrag ohne Zustand, Templates sind für UHT unsichtbar.
Vertiefung → UActorComponent
Warum gibt es Modules?
Eine Codebasis in der Größenordnung der Engine lässt sich als einzelnes monolithisches Binary nicht in angemessener Zeit kompilieren und hat keine handhabbaren Abhängigkeitsgrenzen. Ein Module ist eine eigenständige Kompilierungseinheit mit eigener Build-Datei und einer expliziten Liste von Abhängigkeiten zu anderen Modules, was es erlaubt, nur das tatsächlich Geänderte neu zu bauen, und physisch verhindert, dass ein Teil der Engine etwas mitzieht, das er nicht braucht (z.B. Editor-Code in ein gepacktes Spiel).
Artikel folgt
Warum gibt es Plugins?
Ein Module ist eine Kompilierungseinheit, aber keine Distributionseinheit. Ein Plugin verpackt eines oder mehrere Modules zusammen mit Content und Config zu einem eigenständigen, ein-/ausschaltbaren Feature für ein bestimmtes Projekt — sodass eine Engine-Erweiterung kein Forken oder Patchen des Engine-Quellcodes erfordert, was sonst mit jedem weiteren Engine-Update auseinanderdriften würde.
Artikel folgt
Warum gibt es Subsystem?
Eine selbstgebaute Singleton-/globale Variable passt nicht gut dazu, dass in Unreal mehrere "Welten" gleichzeitig existieren können (Editor + Play-In-Editor gleichzeitig, mehrere Clients in einem Prozess beim Testen) — globaler Zustand hat keinen eindeutigen Ort, zu dem er "gehört". Ein Subsystem ist ein UObject mit einer von der Engine automatisch verwalteten Lebensdauer, gebunden an eine konkrete, explizite Grenze (Engine/GameInstance/World/LocalPlayer), sodass es diese Grenze nicht überlebt und nicht zwischen parallelen Welten vermischt wird.
Artikel folgt
Warum gibt es Gameplay Tags?
Eine wachsende, verzweigte Menge von Gameplay-Zuständen und -Kategorien passt schlecht in ein C++-enum — ein neuer Wert erfordert Neukompilierung, und hierarchische Beziehungen ("Status.Debuff.Stun" ist ein Subtyp von "Status.Debuff") lassen sich mit einem enum überhaupt nicht ausdrücken. FGameplayTag ist ein hierarchischer String mit schnellem Vergleich, deklariert als Daten statt als neu zu kompilierender Code.
Vertiefung → FGameplayTag
Warum gibt es Data Assets?
In C++ oder Blueprint fest codierte Content-Eigenschaften (Waffenschaden, Item-Gewicht) skalieren nicht mit einer wachsenden Content-Liste und erfordern die Beteiligung eines Programmierers bei jeder Balance-Änderung. UDataAsset ist ein gewöhnliches UObject, aber speziell für Daten konzipiert, die ein Designer ohne Neukompilierung und ohne Rückgriff auf einen Programmierer bearbeiten kann.
Vertiefung → UDataAsset
Warum gibt es Primary Assets?
TSoftObjectPtr löst "nicht alles auf einmal laden", aber nicht "welche 50 von 3000 DataAssets werden gerade wirklich gebraucht" — das über implizite Ketten von Soft References zu verwalten, skaliert nicht. Ein Primary Asset ist eine explizite Registrierung eines konkreten UObject beim Asset Manager unter einer stabilen Kennung (Typ + Name), unabhängig vom Dateipfad, was einen einzigen, zentral verwalteten Content-Ladekatalog bereitstellt.
Vertiefung → UAssetManager
💡 Senior Thoughts

Wenn ich zum ersten Mal auf ein unbekanntes Engine-System stoße, lese ich nicht dessen gesamte API. Ich frage zuerst: welches dieser zwölf Probleme löst es — und für wen spielt es in dieser konkreten Aufgabe die Rolle von UObject, Reflection, GC oder Component? In neun von zehn Fällen findet sich die Antwort schneller als die Dokumentation zum System selbst.