(Vielleicht möchten Sie etwas über den Begriff "Affenflicken" oder "Entenschlagen" wissen, wenn auch nur wegen des humorvollen mentalen Bildes.)
Abgesehen davon: Wenn Ihr Ziel darin besteht, die Iterationszeit für "Verhaltensänderungen" zu verkürzen, probieren Sie einige Ansätze aus, mit denen Sie den größten Teil des Weges dorthin finden, und kombinieren Sie sie gut, um in Zukunft mehr davon zu ermöglichen.
(Dies wird ein bisschen tangential ausgehen, aber ich verspreche, es wird wiederkommen!)
- Beginnen Sie mit Daten und fangen Sie klein an: Laden Sie an Grenzen neu ("Ebenen" oder ähnliches), und arbeiten Sie sich dann daran, die Betriebssystemfunktionalität zu nutzen, um Benachrichtigungen über Dateiänderungen zu erhalten oder einfach regelmäßig abzufragen .
- (Für Bonuspunkte und kürzere Ladezeiten (wiederum kürzere Iterationszeit) schauen Sie sich das Datenbacken an .)
- Skripte sind Daten und ermöglichen es Ihnen, das Verhalten zu wiederholen. Wenn Sie eine Skriptsprache verwenden, haben Sie jetzt die Möglichkeit, Benachrichtigungen zu erhalten und diese Skripts neu zu laden, zu interpretieren oder zu kompilieren. Sie können Ihren Interpreter auch an eine In-Game-Konsole, einen Netzwerk-Socket oder ähnliches anschließen, um die Laufzeitflexibilität zu erhöhen.
- Code kann auch Daten sein : Ihr Compiler unterstützt möglicherweise Overlays , gemeinsam genutzte Bibliotheken, DLLs oder ähnliches. So können Sie jetzt eine "sichere" Zeit zum Entladen und erneuten Laden einer Überlagerung oder DLL wählen, ob manuell oder automatisch. Die anderen Antworten gehen hier ins Detail. Beachten Sie, dass einige Varianten davon mit der Verschlüsselung der Signatur, dem NX-Bit (No-Execute) oder ähnlichen Sicherheitsmechanismen zu tun haben können .
- Stellen Sie sich ein tiefgreifendes, versioniertes Speichern / Laden- System vor. Wenn Sie Ihren Status trotz Codeänderungen stabil speichern und wiederherstellen können, können Sie Ihr Spiel beenden und es mit neuer Logik genau zum gleichen Zeitpunkt neu starten. Leichter gesagt als getan, aber es ist machbar, und es ist ausgesprochen einfacher und tragbarer, als den Speicher zu durchsuchen, um Anweisungen zu ändern.
- Abhängig von der Struktur und dem Determinismus Ihres Spiels können Sie möglicherweise Aufnahmen und Wiedergaben machen . Befindet sich diese Aufzeichnung nur über "Spielbefehle" (z. B. bei einem Kartenspiel), können Sie den gewünschten Rendering-Code ändern und die Aufzeichnung erneut abspielen, um Ihre Änderungen anzuzeigen. Für einige Spiele ist dies so "einfach" wie das Aufzeichnen einiger Startparameter (z. B. eines zufälligen Startwerts) und anschließender Benutzeraktionen. Für manche ist es viel komplizierter.
- Bemühen Sie sich, die Kompilierzeit zu verkürzen . In Kombination mit den oben genannten Speicher- / Lade- oder Aufzeichnungs- / Wiedergabesystemen oder sogar mit Überlagerungen oder DLLs kann dies Ihren Turnaround mehr als jede andere Sache verringern.
Viele dieser Punkte sind auch dann von Vorteil, wenn Sie weder Daten noch Code vollständig neu laden müssen.
Unterstützende Anekdoten:
Auf einem großen PC-RTS (~ 120-köpfiges Team, meistens C ++) gab es ein unglaublich tiefgreifendes State-Save-System, das für mindestens drei Zwecke verwendet wurde:
- Ein "flacher" Speichervorgang wurde nicht auf die Festplatte, sondern auf eine CRC-Engine übertragen, um sicherzustellen, dass die Multiplayer-Spiele in der Lock-Step-Simulation alle 10 bis 30 Frames einen CRC beibehalten. Dies stellte sicher, dass niemand betrog und einige Frames später Desync-Fehler entdeckte
- Wenn ein Desync-Fehler im Multiplayer-Modus auftrat, wurde jedes Bild extra tief gespeichert und erneut an die CRC-Engine übertragen. Diesmal erzeugte die CRC-Engine jedoch viele CRCs, jeweils für kleinere Bytestapel. Auf diese Weise können Sie genau feststellen, welcher Teil des Zustands innerhalb des letzten Frames auseinander zu laufen begonnen hat. Wir haben einen unangenehmen Unterschied zwischen AMD- und Intel-Prozessoren im "Standard-Fließkomma-Modus" festgestellt.
- Ein normaler Tiefenspeicher speichert möglicherweise nicht das genaue Bild der Animation, die Ihr Gerät abgespielt hat, aber er zeigt die Position, den Gesundheitszustand usw. aller Ihrer Geräte an, sodass Sie diese während des Spiels jederzeit speichern und fortsetzen können.
Ich habe seitdem deterministische Aufnahme / Wiedergabe für ein C ++ - und Lua-Kartenspiel für den DS verwendet. Wir haben uns in die API eingebunden, die wir für die KI entwickelt haben (auf der C ++ - Seite) und haben alle Benutzer- und KI-Aktionen aufgezeichnet. Wir haben diese Funktion im Spiel verwendet (um dem Spieler eine Wiederholung zu ermöglichen), aber auch um Probleme zu diagnostizieren: Wenn es zu einem Absturz oder einem merkwürdigen Verhalten kam, mussten wir nur die Sicherungsdatei abrufen und sie in einem Debugbuild wiedergeben.
Ich habe auch seitdem mehr als ein paar Mal Overlays verwendet und wir haben es mit unserem System "Dieses Verzeichnis automatisch spinnen und neue Inhalte auf den Handheld hochladen" kombiniert. Alles, was wir tun müssten, ist die Zwischensequenz / Ebene / was auch immer zu verlassen und wieder hereinzukommen, und nicht nur die neuen Daten (Sprites, Ebenenlayout usw.) würden geladen, sondern auch jeglicher neue Code in die Überlagerung. Leider wird dies bei neueren Handhelds aufgrund des Kopierschutzes und der Anti-Hacking-Mechanismen, die Code speziell behandeln, immer schwieriger. Wir machen das immer noch für Lua-Skripte.
Last but not least: Sie können (und ich muss, unter verschiedenen sehr kleinen spezifischen Umständen) ein bisschen Entenpunsch machen, indem Sie Anweisungs-Opcodes direkt patchen. Dies funktioniert jedoch am besten, wenn Sie sich auf einer festen Plattform und einem Compiler befinden. Da diese Plattform kaum zu warten ist, sehr fehleranfällig ist und nur eine begrenzte Anzahl von Funktionen zur Verfügung steht, verwende ich sie meistens nur, um den Code beim Debuggen neu zu routen. Es bringt Ihnen jedoch verdammt viel über Ihre Befehlssatzarchitektur bei.