Datengesteuerte Codierung
Alles, was Sie erwähnen, kann in Daten angegeben werden. Warum lädst du aspecificmap
? Weil die Spielkonfiguration besagt, dass dies die erste Ebene ist, wenn ein Spieler ein neues Spiel startet, oder weil dies der Name des aktuellen Speicherpunkts in der gerade geladenen Speicherdatei des Spielers ist, usw.
Wie findest aspecificmap
du Weil es sich um eine Datendatei handelt, in der Zuordnungs-IDs und ihre Ressourcen auf der Festplatte aufgelistet sind.
Es muss nur einen besonders kleinen Satz von "Kern" -Ressourcen geben, die aus legitimen Gründen schwer oder unmöglich zu vermeiden sind. Mit einem wenig Arbeit, kann dies zu einem einzigen hartcodierte begrenzt Standard Asset Namen wie main.wad
oder dergleichen. Diese Datei kann möglicherweise zur Laufzeit geändert werden, indem ein Befehlszeilenargument an das Spiel übergeben wird, auch bekannt als game.exe -wad mymain.wad
.
Das Schreiben von datengesteuertem Code beruht auf einigen anderen Prinzipien. Beispielsweise kann man vermeiden, dass Systeme oder Module nach einer bestimmten Ressource fragen, und stattdessen diese Abhängigkeiten umkehren. Das heißt, DebugDrawer
laden Sie debug.font
den Initialisierungscode nicht nach. DebugDrawer
Nehmen Sie stattdessen ein Ressourcenhandle in den Initialisierungscode. Dieses Handle wird möglicherweise aus der Hauptspielkonfigurationsdatei geladen.
Als konkretes Beispiel aus unserer Codebasis haben wir ein "globales Daten" -Objekt, das aus der Ressourcendatenbank geladen wird (die selbst standardmäßig der ./resources
Ordner ist, aber mit einem Befehlszeilenargument überladen werden kann). Die Ressourcendatenbank-ID dieser globalen Daten ist der einzig notwendige fest codierte Ressourcenname in der Codebasis (wir haben andere, weil Programmierer manchmal faul werden, aber im Allgemeinen werden diese letztendlich repariert / entfernt). Dieses globale Datenobjekt ist voll von Komponenten, deren einziger Zweck darin besteht, Konfigurationsdaten bereitzustellen. Eine der Komponenten ist die UI Global Data-Komponente, die neben einer Reihe anderer Konfigurationselemente Ressourcenhandles für alle wichtigen UI-Ressourcen (Schriftarten, Flash-Dateien, Symbole, Lokalisierungsdaten usw.) enthält. Wenn ein UI-Entwickler beschließt, das Haupt-UI-Asset von /ui/mainmenu.swf
in umzubenennen/ui/lobby.swf
Sie aktualisieren nur diese globale Datenreferenz. es muss sich überhaupt kein Motorcode ändern.
Wir nutzen diese globalen Daten für alles. Alle spielbaren Charaktere, alle Ebenen, Benutzeroberfläche, Audio, Kernelemente, Netzwerkkonfiguration, alles. (Nun, nicht alles , aber diese anderen Dinge sind zu behebende Fehler.)
Dieser Ansatz hat viele andere Vorteile. Zum einen wird das Packen und Bündeln von Ressourcen zum integralen Bestandteil des gesamten Prozesses. Festcodierte Pfade in der Engine bedeuten in der Regel auch, dass dieselben Pfade in den Skripten oder Tools, die die Spielelemente zusammenfassen, festcodiert werden müssen. Diese Pfade können dann nicht mehr synchron sein. Wenn wir uns stattdessen auf ein einzelnes Kern-Asset und Referenzketten von dort stützen, können wir ein Asset-Bundle mit einem einzigen Befehl erstellen bundle.exe -root config.data -out main.wad
und wissen, dass es alle erforderlichen Assets enthalten wird. Da der Bündler lediglich Ressourcenreferenzen folgt, wissen wir, dass nur die von uns benötigten Ressourcen enthalten sind, und überspringen alle verbleibenden Flusen, die sich unvermeidlich während der Laufzeit eines Projekts ansammeln (und wir können automatisch Listen davon erstellen) Flusen zum Beschneiden).
Ein kniffliger Eckfall dieser ganzen Sache ist in Skripten. Es ist konzeptionell einfach, die Engine datengetrieben zu machen, aber ich habe so viele Projekte gesehen (Hobby bis AAA), bei denen Skripte als Daten betrachtet werden und daher "nur wahllos" Ressourcenpfade verwenden dürfen. Mach das nicht. Wenn eine Lua-Datei eine Ressource benötigt und nur eine Funktion wie diese aufruft, wird textures.lua("/path/to/texture.png")
die Asset-Pipeline große Probleme damit haben, zu wissen, dass das Skript /path/to/texture.png
ordnungsgemäß ausgeführt werden muss, und diese Textur möglicherweise als nicht verwendet und unnötig erachtet. Skripts sollten wie jeder andere Code behandelt werden: Alle erforderlichen Daten, einschließlich Ressourcen oder Tabellen, sollten in einem Konfigurationseintrag angegeben werden, den die Engine und die Ressourcenpipeline auf Abhängigkeiten untersuchen können. Die Daten, in denen "Skript laden foo.lua
" steht, sollten stattdessen "foo.lua
und geben Sie diese Parameter ein, "wo die Parameter alle erforderlichen Ressourcen enthalten. Wenn ein Skript beispielsweise zufällig Feinde erzeugt, geben Sie die Liste der möglichen Feinde aus dieser Konfigurationsdatei in das Skript ein. Die Engine kann die Feinde dann mit dem Level vorladen ( da kennt er die vollständige Liste der möglichen Spawns) und die Ressource Pipeline kennt alle Feinde mit dem Spiel zu bündeln (da sie von Konfigurationsdaten endgültig referenziert sind.) Wenn die Skripts Strings von Pfadnamen erzeugt und ruft nur eine load
Funktion dann weder Die Engine oder die Ressourcen-Pipeline wissen auf irgendeine Weise, welche Assets das Skript möglicherweise zu laden versucht.