HINWEIS: Ich habe dies vor einigen Tagen bei Stack Overflow gefragt, hatte aber nur sehr wenige Aufrufe und keine Antwort. Ich dachte, ich sollte stattdessen auf gamdev.stackexchange fragen.
Dies ist eine allgemeine Frage / Bitte um Beratung zur Aufrechterhaltung eines prozeduralen Generierungssystems durch mehrere Aktualisierungen nach der Veröffentlichung, ohne zuvor generierte Inhalte zu beschädigen.
Ich versuche, Informationen und Techniken zu finden, um Probleme mit dem "Schmetterlingseffekt" beim Erstellen von prozeduralen Inhalten für Spiele zu vermeiden. Bei Verwendung eines gesetzten Zufallszahlengenerators kann eine sich wiederholende Folge von Zufallszahlen verwendet werden, um eine reproduzierbare Welt zu erstellen. Während einige Spiele die generierte Welt nach der Generierung einfach auf der Festplatte speichern, ist eines der leistungsstarken Merkmale der prozeduralen Generierung die Tatsache, dass Sie sich auf die Reproduzierbarkeit der Zahlenfolge verlassen können, um eine Region mehrmals auf dieselbe Weise neu zu erstellen, ohne dass dies erforderlich ist Beharrlichkeit. Aufgrund der Einschränkungen meiner besonderen Situation muss ich die Persistenz minimieren und mich so weit wie möglich auf rein gesäte Konzentration verlassen.
Die Hauptgefahr bei diesem Ansatz besteht darin, dass selbst die geringste Änderung des prozeduralen Generierungssystems einen Schmetterlingseffekt verursachen kann, der die ganze Welt verändert. Dies macht es sehr schwierig, das Spiel zu aktualisieren, ohne die Welten zu zerstören, die die Spieler erkunden.
Die Haupttechnik, mit der ich dieses Problem vermieden habe, besteht darin, die prozedurale Generierung in mehreren Phasen zu entwerfen, von denen jede ihren eigenen Zufallszahlengenerator hat. Dies bedeutet, dass jedes Subsystem in sich geschlossen ist und wenn etwas kaputt geht, wirkt es sich nicht auf alles auf der Welt aus. Dies scheint jedoch immer noch ein großes Potenzial für "Bruch" zu haben, selbst wenn es sich um einen isolierten Teil des Spiels handelt.
Eine andere Möglichkeit, dieses Problem zu lösen, besteht darin, vollständige Versionen Ihrer Generatoren im Code zu verwalten und weiterhin den richtigen Generator für eine bestimmte Weltinstanz zu verwenden. Dies scheint mir jedoch ein Alptraum für die Wartung zu sein, und ich bin gespannt, ob dies tatsächlich jemand tut.
Meine Frage ist also wirklich eine Bitte um allgemeine Ratschläge, Techniken und Entwurfsmuster, um dieses Problem des Schmetterlingseffekts zu lösen, insbesondere im Zusammenhang mit Spielaktualisierungen nach der Veröffentlichung. (Hoffentlich ist das keine allzu breite Frage.)
Ich arbeite derzeit in Unity3D / C #, obwohl dies eine sprachunabhängige Frage ist.
AKTUALISIEREN:
Vielen Dank für die Antworten.
Es sieht immer mehr so aus, als ob statische Daten der beste und sicherste Ansatz sind. Wenn das Speichern vieler statischer Daten keine Option ist, würde eine lange Kampagne in einer generierten Welt eine strikte Versionierung der verwendeten Generatoren erfordern. Der Grund für die Einschränkung in meinem Fall ist die Notwendigkeit eines mobilen Cloud-Speicherns / Synchronisierens. Meine Lösung könnte darin bestehen, Wege zu finden, um kleine Mengen kompakter Daten über wesentliche Dinge zu speichern.
Ich finde Stormwinds Konzept von "Käfigen" eine besonders nützliche Art, über Dinge nachzudenken. Ein Käfig ist im Grunde ein Nachsaatpunkt, der die Auswirkungen kleiner Änderungen, dh des Käfigs des Schmetterlings, verhindert.
Antworten:
Ich denke, Sie haben die Grundlagen hier behandelt:
Verwenden mehrerer Generatoren oder erneutes Aussäen in Intervallen (z. B. mithilfe von räumlichen Hashes), um das Überlaufen von Änderungen zu begrenzen. Dies funktioniert wahrscheinlich für kosmetische Inhalte, aber wie Sie hervorheben, kann es dennoch zu Brüchen in einem Abschnitt kommen.
Verfolgen Sie die in der Sicherungsdatei verwendete Generatorversion und reagieren Sie entsprechend. Was "angemessen" bedeutet, könnte sein ...
n
Generatorversionen in Ihrer ausführbaren Datei. Wenn die Sicherungsdatei eine dieser neuesten Versionen verwendet, aktualisieren Sie die Sicherungsdatei auf die neueste Version. Dies verwendet den entsprechenden Generator, um veraltete Zustände in Literale (oder in Deltas aus der Ausgabe des neuen Generators auf demselben Seed, wenn sie sehr ähnlich sind) zu entpacken. Jeder neue Zustand kommt von nun an von den neuesten Generatoren. Spieler, die lange nicht spielen, könnten jedoch zurückgelassen werden. Und im schlimmsten Fall speichern Sie den gesamten Spielstatus in wörtlicher Form. In diesem Fall können Sie genauso gut ...Wenn Sie erwarten, dass Ihre Generierungslogik häufig geändert wird und die Kompatibilität mit früheren Versionen nicht beeinträchtigt werden soll, verlassen Sie sich nicht auf den Generatordeterminismus: Speichern Sie Ihren gesamten Status in Ihrer Sicherungsdatei. (dh "Nuke es aus dem Orbit. Es ist der einzige Weg, um sicher zu sein")
quelle
Die Hauptquelle für einen solchen Schmetterlingseffekt ist wohl nicht die Generierung von Zahlen - was einfach genug sein sollte, um von einem einzelnen Zahlengenerator deterministisch zu bleiben -, sondern die Verwendung dieser Zahlen durch den Clientcode. Codeänderungen sind die eigentliche Herausforderung, um die Dinge stabil zu halten.
Code: Komponententests Der beste Weg, um sicherzustellen, dass sich geringfügige Änderungen nicht unbeabsichtigt an anderer Stelle manifestieren, besteht darin, gründliche Komponententests für jeden generativen Aspekt in Ihren Build aufzunehmen. Dies gilt für jeden kompakten Code, bei dem sich das Ändern einer Sache auf viele andere auswirken kann. Sie benötigen Tests für alle, damit Sie bei einem einzelnen Build sehen können, was betroffen ist.
Zahlen: Periodische Sequenzen / Slots Nehmen wir an, Sie haben einen Zahlengenerator, der für alles dient. Es weist keine Bedeutung zu, es spuckt nur Zahlen nacheinander aus - wie jedes PRNG. Bei gleichem Startwert über zwei Läufe erhalten wir dieselben Sequenzen, ja? Jetzt überlegen Sie sich etwas und entscheiden, dass es vielleicht 30 Aspekte Ihres Spiels geben wird, die regelmäßig mit einem zufälligen Wert versehen werden müssen. Hier weisen wir eine Zyklussequenz von 30 Slots zu, z. B. ist jede erste Nummer in der Sequenz ein unebenes Geländelayout, jede zweite Nummer ist Geländestörungen ... usw. ... jede zehnte Nummer fügt dem AI-Zustand einen Fehler für den Realismus hinzu. Ihre Periode ist also 30.
Nach 10 haben Sie 20 freie Plätze, die Sie im Verlauf des Spieldesigns für andere Aspekte nutzen können. Die Kosten hier sind natürlich, dass Sie Zahlen für die Slots 11-30 generieren müssen, obwohl sie derzeit nicht verwendet werden , dh den Zeitraum abschließen, um zur nächsten Sequenz von 1-10 zurückzukehren. Das hat CPU-Kosten, sollte aber gering sein (abhängig von der Anzahl der freien Steckplätze). Der andere Nachteil ist , dass Sie sicherstellen müssen , dass Ihr endgültiges Design in der Anzahl der Slots untergebracht werden kann, die Sie zu Beginn Ihres Entwicklungsprozesses zur Verfügung gestellt haben. Je mehr Sie zu Beginn zuweisen, desto mehr "leere" Slots Sie müssen möglicherweise jeden einzelnen durchlaufen, damit die Dinge funktionieren.
Die Auswirkungen davon sind:
Natürlich wird es einen langen Zeitraum geben, in dem Ihr Spiel der Öffentlichkeit nicht zur Verfügung steht - sozusagen in Alpha -, sodass Sie von 30 auf 20 Aspekte reduzieren können, ohne einen Spieler zu beeinflussen, nur Sie selbst, wenn Sie dies bemerken Sie hatten zu Beginn viel zu viele Slots zugewiesen . Dies würde natürlich einige CPU-Zyklen einsparen. Aber denken Sie daran, dass eine gute Hash-Funktion (die Sie selbst schreiben können) sowieso blitzschnell sein sollte. Es sollte also nicht teuer sein, zusätzliche Slots zu betreiben.
quelle
Wenn Sie eine Persistenz mit PCG wünschen, empfehle ich Ihnen , den PCG-Code selbst als Daten zu behandeln . So wie Sie Daten über Revisionen hinweg mit regulären Inhalten und generierten Inhalten beibehalten würden, müssen Sie den Generator beibehalten, wenn Sie sie über Revisionen hinweg beibehalten möchten.
Der beliebteste Ansatz ist natürlich die Generierung generierter Daten in statische Daten, wie Sie bereits erwähnt haben.
Ich kenne keine Beispiele für Spiele, bei denen es viele Generatorversionen gibt, da die Persistenz in PCG-Spielen ungewöhnlich ist - deshalb geht Permadeath oft Hand in Hand mit PCG. Es gibt jedoch viele Beispiele für mehrere PCGs, auch vom selben Typ, innerhalb desselben Spiels. Zum Beispiel hat Unangband viele separate Generatoren für Dungeon-Räume, und wenn neue hinzugefügt werden, funktionieren die alten immer noch gleich. Ob dies wartbar ist, hängt von Ihrer Implementierung ab. Eine Möglichkeit, die Wartung aufrechtzuerhalten, besteht darin, Ihre Generatoren mithilfe von Skripten zu implementieren und sie mit dem Rest des Spielcodes isoliert zu halten.
quelle
Ich unterhalte eine Fläche von ca. 30000 Quadratkilometern mit ca. 1 Million Gebäuden und anderen Objekten sowie zufälligen Platzierungen verschiedener Dinge. Eine Outdoor-Simulation von c. Die gespeicherten Daten betragen ca. 4 GB. Ich habe das Glück, Speicherplatz zu haben, aber es ist nicht unbegrenzt.
Zufällig ist zufällig, unkontrolliert. Aber man kann es ein wenig einsperren:
Das ist alles. Käfige verbrauchen leider auch Daten.
Es gibt ein finnisches Sprichwort: Hajota ja hallitse. Übersetzt in Teilen und Erobern .
Ich gab die Idee der präzisen Definition kleinster Details schnell auf. Zufällig will Freiheit, also hat es die Freiheit. Lass den Schmetterling fliegen - in seinem Käfig. Stattdessen konzentrierte ich mich darauf, die Käfige auf eine reichhaltige Art und Weise zu definieren (und zu pflegen !!). Es spielt keine Rolle, welche Autos sie sind, solange sie blau oder dunkelblau sind (ein langweiliger Arbeitgeber sagte einmal :-)). "Blau oder Dunkelblau" ist hier der (sehr kleine) Käfig entlang der Farbdimension.
Was ist handhabbar, um numerische Räume zu steuern und zu verwalten?
In Bezug auf Wartung und Versionskompatibilität der Version ... wir haben
: wenn version = n dann
: elseif version = m dann ...
Ja, die Codebasis wird größer :-).
Vertraute Dinge. Ihr korrekter Weg wäre imho, eine reichhaltige Methode zum Teilen und Erobern zu definieren und einige Daten dazu zu opfern. Geben Sie dann, wo möglich, die (lokale) Randomisierungsfreiheit, wo es nicht entscheidend ist, sie zu kontrollieren.
Nicht völlig unvereinbar mit dem von DMGregory vorgeschlagenen lustigen "Nuke it fom Orbit", aber vielleicht kleine und genaue Atomwaffen verwenden? :-)
quelle