Ich habe ein fertiges Spiel, das ich in anderen Versionen ablehnen möchte. Dies wären ähnliche Spiele mit mehr oder weniger dem gleichen Design, aber nicht immer, im Grunde könnten sich die Dinge ändern, manchmal klein, manchmal groß.
Ich möchte, dass der Kerncode separat vom Spiel versioniert wird. Wenn ich also einen Fehler in Spiel A behebe, ist der Fehler in Spiel B vorhanden.
Ich versuche, den besten Weg dafür zu finden. Meine ersten Ideen sind folgende:
- Erstellen Sie ein
engine
Modul / Ordner / was auch immer, das alles enthält, was verallgemeinert werden kann und zu 100% unabhängig vom Rest des Spiels ist. Dies würde einige Codes, aber auch allgemeine Assets enthalten, die von Spielen gemeinsam genutzt werden. - Stellen Sie diese Engine in ein eigenes
git
Repository, das in den Spielen alsgit submodule
Der Teil, mit dem ich zu kämpfen habe, ist, wie der Rest des Codes verwaltet wird. Angenommen, Sie haben Ihre Menüszene, dieser Code ist spielspezifisch, aber der Großteil davon ist in der Regel allgemein gehalten und könnte in anderen Spielen wiederverwendet werden. Ich kann es nicht einfügen engine
, aber eine Neucodierung für jedes Spiel wäre ineffizient.
Vielleicht könnte es effektiv sein, eine Art Git-Verzweigungsvariation zu verwenden, aber ich bin nicht der Meinung, dass dies der beste Weg ist.
Hat jemand Ideen, Erfahrungen oder etwas darüber zu teilen?
Antworten:
Genau das mache ich und es funktioniert sehr gut. Ich habe ein Anwendungsframework und eine Renderingbibliothek, und jede davon wird als Submodul meiner Projekte behandelt. Ich finde, SourceTree ist nützlich, wenn es um Submodule geht, da es sie gut verwaltet und Sie nichts vergessen lässt. Wenn Sie z. B. das Engine-Submodul in Projekt A aktualisiert haben, werden Sie aufgefordert, die Änderungen in Projekt B zu übernehmen.
Mit der Erfahrung geht das Wissen einher, welcher Code in der Engine enthalten sein sollte und welcher pro Projekt. Ich schlage vor, dass Sie es fürs Erste in jedem Projekt behalten, wenn Sie sogar ein bisschen unsicher sind. Im Laufe der Zeit werden Sie bei Ihren verschiedenen Projekten feststellen, was gleich bleibt, und können dies schrittweise in Ihren Motorcode einbeziehen. Mit anderen Worten: Kopieren Sie den Code, bis Sie zu 100% sicher sind, dass er sich nicht diskret pro Projekt ändert, und verallgemeinern Sie ihn dann.
Hinweis zur Quellcodeverwaltung und zu Binärdateien
Denken Sie daran, dass Sie, wenn Sie davon ausgehen, dass sich Ihre binären Assets häufig ändern, diese möglicherweise nicht in eine textbasierte Quellcodeverwaltung wie git einfügen möchten. Sagen Sie einfach ... es gibt bessere Lösungen für Binärdateien. Das Einfachste, was Sie jetzt tun können, um Ihr "Engine-Source" -Repository sauber und performant zu halten, ist, ein separates "Engine-Binaries" -Repository zu haben, das nur Binaries enthält, die Sie auch als Submodul in Ihr Projekt aufnehmen. Auf diese Weise verringern Sie den Performance-Schaden, der Ihrem "Engine-Source" -Repository zugefügt wurde, das sich ständig ändert und für das Sie schnelle Iterationen benötigen: Commit, Push, Pull usw. Quellcodeverwaltungssysteme wie git verarbeiten Text-Deltas und sobald Sie Binärdateien einführen, führen Sie massive Deltas aus Textsicht ein - was Sie letztendlich Entwicklungszeit kostet.GitLab Annex . Google ist dein Freund.
quelle
Irgendwann MUSS sich eine Engine spezialisieren und etwas über das Spiel wissen. Ich werde hier auf einen Tangens gehen.
Nehmen Sie Ressourcen in einem RTS. Ein Spiel kann haben
Credits
und einCrystal
anderesMetal
undPotatoes
Sie sollten die OO-Konzepte richtig anwenden und max. Code-Wiederverwendung. Es ist klar, dass hier ein Konzept von
Resource
existiert.Daher entscheiden wir, dass Ressourcen Folgendes haben:
int
)Beachten Sie, dass diese Vorstellung von a
Resource
Abschüsse oder Punkte in einem Spiel darstellen könnte! Es ist nicht sehr mächtig.Denken wir jetzt über ein Spiel nach. Wir können eine Art Währung haben, indem wir mit Pennies handeln und der Ausgabe einen Dezimalpunkt hinzufügen. Was wir nicht können, sind "augenblickliche" Ressourcen. Wie sagen "Stromnetzerzeugung"
Nehmen wir an, Sie fügen eine
InstantResource
Klasse mit ähnlichen Methoden hinzu. Sie verschmutzen nun Ihren Motor mit Ressourcen.Das Problem
Nehmen wir noch einmal das RTS-Beispiel. Angenommen, der Spieler spendet etwas
Crystal
an einen anderen Spieler. Sie möchten etwas tun wie:Dies ist jedoch wirklich ziemlich chaotisch. Es ist allgemeiner Zweck, aber chaotisch. Schon
resourceDictionary
jetzt, obwohl es ein vorschreibt, was bedeutet, dass Ihre Ressourcen Namen haben müssen! UND es ist pro Spieler, so dass Sie keine Teamressourcen mehr haben können.Dies ist "zu viel" Abstraktion (ich gebe zu, kein brillantes Beispiel). Stattdessen solltest du einen Punkt erreichen, an dem du akzeptierst, dass dein Spiel Spieler und Kristall hat, dann kannst du nur (zum Beispiel)
Mit einer Klasse
Player
und einer Klasse,CurrentPlayer
in derCurrentPlayer
dascrystal
Objekt automatisch das Material auf dem HUD für die Übertragung / das Senden von Spenden anzeigt.Dies verschmutzt den Motor mit Kristall, dem Spenden von Kristall, den Nachrichten auf dem HUD für aktuelle Spieler und so weiter. Es ist sowohl schneller als auch einfacher zu lesen / schreiben / warten (was wichtiger ist, da es nicht wesentlich schneller ist)
Schlussbemerkungen
Der Ressourcenfall ist nicht brillant. Ich hoffe, dass Sie den Punkt dennoch sehen können. Wenn überhaupt, dann habe ich gezeigt, dass "Ressourcen gehören nicht in die Engine" , was ein bestimmtes Spiel benötigt und was für alle Begriffe von Ressourcen gilt, SEHR unterschiedliche Dinge sind. Was Sie normalerweise finden, sind 3 (oder 4) "Schichten"
creature
odership
odersquad
. Mit Vererbung Sie Klassen erhalten , die alle drei Schichten erstrecken (zum BeispielCrystal
eineResource
, die eine istGameLoopEventListener
sagen wir)Aus einer alten Engine ein neues Spiel machen
Dies ist sehr häufig. Phase 1 besteht darin, die Schichten 3 und 4 herauszureißen (und 2, wenn es sich um ein völlig anderes Spiel handelt). Nehmen wir an, wir machen ein RTS aus einem alten RTS. Wir haben immer noch Ressourcen, nur keinen Kristall und so weiter. Die Basisklassen in den Schichten 2 und 1 sind also immer noch sinnvoll. Alles, worauf in 3 und 4 verwiesen wird, kann verworfen werden. So machen wir es. Wir können es jedoch als Referenz für das überprüfen, was wir tun möchten.
Verschmutzung in Schicht 1
Das kann passieren. Abstraktion und Leistung sind Feinde. UE4 bietet zum Beispiel eine Menge optimierter Fälle von Komposition (wenn Sie also möchten, dass X und Y schnell zusammenarbeiten - es weiß, dass es beides tut) und ist daher WIRKLICH ziemlich groß. Das ist nicht schlecht, aber zeitaufwändig. Layer 1 entscheidet, wie "Sie Daten an Shader übergeben" und wie Sie Dinge animieren. Es ist IMMER gut, das Beste für Ihr Projekt zu tun. Versuchen Sie einfach, für die Zukunft zu planen. Die Wiederverwendung von Code ist Ihr Freund. Erben Sie, wo es Sinn macht.
Ebenen klassifizieren
LETZT (ich verspreche es) hab keine Angst vor Schichten. Engine ist ein archaischer Begriff aus den alten Zeiten der Pipelines mit fester Funktion, in denen Engines ähnlich grafisch arbeiteten (und als Ergebnis hatten sie viele Gemeinsamkeiten). Die programmierbare Pipeline stellte dies auf den Kopf und als solche wurde "Layer 1" verschmutzt mit welchen effekten die entwickler erreichen wollten. KI war das Unterscheidungsmerkmal (wegen der Vielzahl von Ansätzen) von Motoren, jetzt ist es KI und Grafik.
Ihr Code sollte nicht in diesen Ebenen abgelegt werden. Sogar die berühmte Unreal-Engine hat VIELE verschiedene Versionen, die jeweils für ein anderes Spiel spezifisch sind. Es gibt nur wenige Dateien (außer vielleicht ähnlichen Datenstrukturen), die unverändert geblieben wären. Das ist okay! Wenn Sie ein neues Spiel aus einem anderen machen möchten, dauert es länger als 30 Minuten. Der Schlüssel ist zu planen, zu wissen, welche Teile zu kopieren und einzufügen sind und was zurückzulassen ist.
quelle
Mein persönlicher Vorschlag für den Umgang mit Inhalten, die eine Mischung aus allgemein und spezifisch sind, ist, sie dynamisch zu gestalten. Ich nehme Ihren Menübildschirm als Beispiel. Wenn ich falsch verstanden habe, wonach Sie gefragt haben, lassen Sie mich wissen, was Sie wissen wollten, und ich werde meine Antwort anpassen.
Es gibt 3 Dinge, die (fast) immer in einer Menüszene vorhanden sind: den Hintergrund, das Spielelogo und das Menü selbst. Diese Dinge unterscheiden sich normalerweise je nach Spiel. Sie können für diesen Inhalt einen MenuScreenGenerator in Ihrer Engine erstellen, der drei Objektparameter enthält: BackGround, Logo und Menu. Die Grundstruktur dieser 3 Teile ist ebenfalls Teil Ihres Motors, aber Ihr Motor sagt nicht wirklich aus, wie diese Teile erzeugt werden, sondern nur, welche Parameter Sie ihnen geben sollten.
Anschließend erstellen Sie in Ihrem eigentlichen Spielcode Objekte für einen Hintergrund, ein Logo und ein Menü und übergeben diese an Ihren MenuScreenGenerator. Auch hier geht Ihr Spiel selbst nicht darum, wie das Menü generiert wird, das ist für die Engine. Ihr Spiel muss der Engine nur mitteilen, wie sie aussehen soll und wo sie sein soll.
Grundsätzlich sollte Ihre Engine eine API sein, die das Spiel angibt, was angezeigt werden soll. Wenn es richtig gemacht wird, sollte Ihre Engine die harte Arbeit verrichten, und Ihr Spiel selbst sollte der Engine nur mitteilen, welche Assets verwendet werden sollen, welche Maßnahmen ergriffen werden müssen und wie die Welt aussieht.
quelle