Schnelle Iterationszeiten sind der Schlüssel zur Entwicklung von Spielen, meiner Meinung nach weit mehr als ausgefallene Grafiken und Engines mit einer Fülle von Funktionen. Kein Wunder, dass viele kleine Entwickler Skriptsprachen auswählen.
Die Unity 3D-Methode, mit der Sie ein Spiel pausieren und Assets UND Code ändern können, um dann fortzufahren und die Änderungen sofort wirksam werden zu lassen, ist hierfür absolut geeignet. Meine Frage ist, hat jemand ein ähnliches System auf C ++ - Game-Engines implementiert?
Ich habe gehört, dass es einige wirklich Super-High-End-Engines gibt, aber ich bin mehr daran interessiert, herauszufinden, ob es eine Möglichkeit gibt, dies in einer selbst gebauten Engine oder in einem Spiel zu tun.
Offensichtlich würde es Kompromisse geben, und ich kann mir nicht vorstellen, dass Sie Ihren Code einfach neu kompilieren können, während das Spiel pausiert, neu geladen wird und unter allen Umständen oder auf jeder Plattform funktioniert.
Aber vielleicht ist es für AI-Programmierung und einfache Logikmodule möglich. Es ist nicht so, dass ich das kurzfristig (oder langfristig) in einem Projekt machen möchte, aber ich war neugierig.
quelle
Hot-Swapping ist ein sehr, sehr schwieriges Problem, das mit Binärcode zu lösen ist. Selbst wenn Ihr Code in separaten Dynamic Link-Bibliotheken abgelegt wird, muss Ihr Code sicherstellen, dass keine direkten Verweise auf Funktionen im Speicher vorhanden sind (da sich die Adresse von Funktionen bei einer Neukompilierung ändern kann). Dies bedeutet im Wesentlichen, keine virtuellen Funktionen zu verwenden, und alles, was Funktionszeiger verwendet, tut dies über eine Art Dispatch-Tabelle.
Behaarte, einschränkende Sachen. Es ist besser, eine Skriptsprache für Code zu verwenden, der eine schnelle Iteration erfordert.
Bei der Arbeit ist unsere aktuelle Codebasis eine Mischung aus C ++ und Lua. Lua ist auch kein kleiner Teil unseres Projekts - es ist fast eine 50/50-Trennung. Wir haben das direkte Neuladen von Lua implementiert, sodass Sie eine Codezeile ändern, neu laden und weitermachen können. In der Tat können Sie dies tun, um Absturzfehler zu beheben, die im Lua-Code auftreten, ohne das Spiel neu zu starten!
quelle
(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!)
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:
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.
quelle
Sie können zur Laufzeit so etwas wie Hot-Swap-Module ausführen, indem Sie die Module als Dynamic Link Libraries (oder Shared Libraries unter UNIX) implementieren und mit dlopen () und dlsym () Funktionen dynamisch aus der Bibliothek laden.
Für Windows lauten die Entsprechungen LoadLibrary und GetProcAddress.
Dies ist eine C - Methode, und hat einige Tücken , indem sie es in C ++ verwenden, können Sie darüber lesen Sie hier .
quelle
Wenn Sie Visual Studio C ++ verwenden, können Sie Ihren Code tatsächlich unter bestimmten Umständen anhalten und neu kompilieren. Visual Studio unterstützt Bearbeiten und Fortfahren . Fügen Sie im Debugger eine Verknüpfung zu Ihrem Spiel hinzu, halten Sie es an einem Haltepunkt an und ändern Sie den Code nach dem Haltepunkt. Wenn Sie speichern und dann fortfahren möchten, versucht Visual Studio, den Code erneut zu kompilieren, ihn erneut in die aktuelle ausführbare Datei einzufügen und fortzufahren. Wenn alles gut geht, wird die Änderung, die Sie vorgenommen haben, live auf das laufende Spiel angewendet, ohne dass ein vollständiger Kompilierungs-Build-Testzyklus durchgeführt werden muss. Das Folgende verhindert jedoch, dass dies funktioniert:
Ich habe Edit and Continue verwendet, um die Geschwindigkeit von Dingen wie der UI-Iteration dramatisch zu verbessern. Angenommen, Sie haben eine funktionierende Benutzeroberfläche, die vollständig im Code integriert ist, mit der Ausnahme, dass Sie versehentlich die Zeichenreihenfolge von zwei Feldern vertauscht haben, sodass Sie nichts sehen können. Indem Sie 2 Codezeilen live ändern, können Sie sich einen 20-minütigen Kompilierungs- / Build- / Testzyklus ersparen, um einen trivialen UI-Fix zu überprüfen.
Dies ist KEINE vollständige Lösung für eine Produktionsumgebung, und ich habe die beste Lösung gefunden, um so viel wie möglich von Ihrer Logik in Datendateien zu verschieben und diese Datendateien dann neu zu laden.
quelle
Wie andere gesagt haben, ist es ein schweres Problem, C ++ dynamisch zu verknüpfen. Aber es ist ein gelöstes Problem - Sie haben vielleicht von COM oder einem der Marketingnamen gehört, die im Laufe der Jahre darauf angewendet wurden: ActiveX.
COM hat aus Entwicklersicht einen schlechten Ruf, da es sehr aufwändig sein kann, C ++ - Komponenten zu implementieren, die ihre Funktionalität offenlegen (obwohl dies mit ATL - der ActiveX-Vorlagenbibliothek - einfacher gemacht wird). Aus Verbrauchersicht hat es einen schlechten Ruf, weil Anwendungen, die es verwenden, um beispielsweise eine Excel-Tabelle in ein Word-Dokument oder ein Visio-Diagramm in eine Excel-Tabelle einzubetten, früher häufig abstürzten. Und das führt zu den gleichen Problemen - trotz aller Anleitungen, die Microsoft anbietet, war / ist es schwierig, COM / ActiveX / OLE richtig zu machen.
Ich werde betonen, dass die Technologie von COM an sich nicht schlecht ist. Erstens verwendet DirectX COM-Schnittstellen, um seine Funktionalität zu offenbaren, und das funktioniert gut genug, ebenso wie eine Vielzahl von Anwendungen, die Internet Explorer mithilfe seines ActiveX-Steuerelements einbetten. Zweitens ist es eine der einfachsten Möglichkeiten, C ++ - Code dynamisch zu verknüpfen - eine COM-Schnittstelle ist im Wesentlichen nur eine reine virtuelle Klasse. Obwohl es eine IDL wie CORBA hat, müssen Sie diese nicht verwenden, insbesondere wenn die von Ihnen definierten Schnittstellen nur in Ihrem Projekt verwendet werden.
Wenn Sie nicht für Windows schreiben, denken Sie nicht, dass COM keine Überlegung wert ist. Mozilla hat es in seiner Codebasis (die im Firefox-Browser verwendet wird) erneut implementiert, weil sie eine Möglichkeit brauchten, ihren C ++ - Code zu komponieren.
quelle
Es gibt eine Implementierung von Runtime-kompilierten C ++ für Gameplay Code hier . Ich weiß auch, dass es mindestens eine proprietäre Spiel-Engine gibt, die dasselbe tut. Es ist schwierig, aber machbar.
quelle