Wenn Sie Spiele erstellen, erstellen Sie häufig das folgende Spielobjekt, von dem alle Entitäten erben:
public class GameObject{
abstract void Update(...);
abstract void Draw(...);
}
In Ihrer Aktualisierungsschleife iterieren Sie also über alle Spielobjekte und geben ihnen die Möglichkeit, den Status zu ändern. In der nächsten Ziehschleife durchlaufen Sie erneut alle Spielobjekte und geben ihnen die Möglichkeit, sich selbst zu zeichnen.
Obwohl dies in einem einfachen Spiel mit einem einfachen Forward-Renderer ziemlich gut funktioniert, führt es oft zu einigen gigantischen Spielobjekten, die ihre Modelle speichern müssen, mehreren Texturen und am schlimmsten zu einer Fat Draw-Methode, die eine enge Kopplung zwischen dem Spielobjekt schafft. die aktuelle Renderstrategie und alle Rendering-bezogenen Klassen.
Wenn ich die Renderstrategie von vorwärts auf verzögert ändern würde, müsste ich viele Spielobjekte aktualisieren. Und die Spielobjekte, die ich mache, sind nicht so wiederverwendbar, wie sie sein könnten. Natürlich können Vererbung und / oder Komposition mir helfen, die Duplizierung von Code zu bekämpfen und die Änderung der Implementierung ein wenig zu vereinfachen, aber es scheint immer noch mangelhaft zu sein.
Ein besserer Weg wäre vielleicht, die Draw-Methode vollständig aus der GameObject-Klasse zu entfernen und eine Renderer-Klasse zu erstellen. Das GameObject müsste noch einige Daten über seine Grafik enthalten, z. B. mit welchem Modell es dargestellt werden soll und welche Texturen auf das Modell gemalt werden sollen. Wie dies gemacht wird, bleibt jedoch dem Renderer überlassen. Es gibt jedoch oft viele Grenzfälle beim Rendern. Obwohl dies die enge Kopplung vom GameObject zum Renderer aufheben würde, müsste der Renderer immer noch alles über alle Spielobjekte wissen, die es fett machen würden, alles wissen und eng verbunden. Dies würde gegen einige gute Praktiken verstoßen. Vielleicht könnte datenorientiertes Design den Trick machen. Spielobjekte wären sicherlich Daten, aber wie würde der Renderer davon angetrieben? Ich bin mir nicht sicher.
Ich bin also ratlos und kann mir keine gute Lösung vorstellen. Ich habe versucht, die Prinzipien von MVC anzuwenden, und in der Vergangenheit hatte ich einige Ideen, wie man das in Spielen verwendet, aber in letzter Zeit scheint es nicht so anwendbar zu sein, wie ich dachte. Ich würde gerne wissen, wie Sie alle dieses Problem angehen.
Wie auch immer, ich bin daran interessiert, wie die folgenden Designziele erreicht werden können.
- Keine Renderlogik im Spielobjekt
- Lose Kopplung zwischen Spielobjekten und Render-Engine
- Kein allwissender Renderer
- Vorzugsweise Laufzeitumschaltung zwischen Render-Engines
Das ideale Projekt-Setup wäre ein separates 'Spiellogik'- und Renderlogik-Projekt, das nicht aufeinander verweisen muss.
Dieser Gedankengang begann, als ich John Carmack auf Twitter sagen hörte, dass er ein System hat, das so flexibel ist, dass er Render-Engines zur Laufzeit austauschen und sein System sogar anweisen kann, beide Renderer (einen Software-Renderer und einen hardwarebeschleunigten Renderer) zu verwenden. Gleichzeitig kann er Unterschiede untersuchen. Die Systeme, die ich bisher programmiert habe, sind nicht einmal annähernd so flexibel