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.
Ebenso vermeiden Sie die Hardcodierung in allgemeinen Funktionen.
Sie übergeben Parameter und speichern Ihre Informationen in Konfigurationsdateien.
In dieser Situation gibt es im Software-Engineering absolut keinen Unterschied zwischen dem Schreiben einer Engine und dem Schreiben einer Klasse.
Dann liest Ihr Client-Code eine "Master" -Konfigurationsdatei ( diese ist entweder fest codiert oder wird als Befehlszeilenargument übergeben), die die Informationen enthält, aus denen hervorgeht, wo sich die Assets-Dateien befinden und welche Zuordnung sie enthalten.
Von dort wird alles von der "Master" -Konfigurationsdatei gesteuert.
quelle
Ich mag die anderen Antworten, deshalb werde ich ein bisschen widersprüchlich sein. ;)
Sie können es nicht vermeiden, Wissen über Ihre Daten in Ihre Engine zu codieren. Woher die Informationen kommen, muss der Motor wissen, ob er danach sucht. Sie können jedoch vermeiden, die eigentlichen Informationen in Ihre Engine zu codieren.
Bei einem "reinen" datengesteuerten Ansatz müssten Sie die ausführbare Datei mit den Befehlszeilenparametern starten, die zum Laden der Erstkonfiguration erforderlich sind. Die Engine muss jedoch codiert sein, um zu wissen, wie diese Informationen zu interpretieren sind. Beispiel : Wenn Sie Ihre Konfigurationsdateien JSON sind, müssen Sie die Variablen hart Code Sie suchen, zum Beispiel der Motor suchen müssen wissen ,
"intro_movies"
und"level_list"
und so weiter.Eine "gut konstruierte" Engine kann jedoch für viele verschiedene Spiele verwendet werden, indem nur die Konfigurationsdaten und die Daten, auf die sie verweist, ausgetauscht werden.
Das Mantra vermeidet also nicht so sehr das harte Codieren, sondern stellt sicher, dass Sie Änderungen mit dem geringstmöglichen Aufwand vornehmen können.
Im Gegensatz zum Ansatz der Datendateien (den ich voll und ganz unterstütze) ist es möglicherweise in Ordnung, dass Sie die Daten in Ihre Engine kompilieren. Wenn die "Kosten" dafür geringer sind, gibt es keinen wirklichen Schaden. Wenn Sie der einzige sind, der daran arbeitet, können Sie das Filehandling auf einen späteren Zeitpunkt verschieben und müssen sich nicht unbedingt selbst verarschen. In meinen ersten Spielprojekten waren große Datentabellen im Spiel selbst fest codiert, z. B. eine Liste der Waffen und ihrer sortierten Daten:
Sie können diese Daten also an einem leicht zu referenzierenden Ort ablegen und bei Bedarf leicht bearbeiten. Das Ideal wäre, dieses Zeug in eine Art Konfigurationsdatei zu packen, aber dann müsst ihr Parsing und Übersetzung machen und all diesen Jazz, und das Verknüpfen von Interstruktur-Referenzen könnte zu einem zusätzlichen Schmerz werden, den ihr wirklich nicht wollt Zurecht kommen.
quelle