Sollte sich ein Objekt in einem 2D-Spiel selbst rendern?


16

Ich mache ein 2D Street Fighter-ähnliches Spiel, das nicht auf Kacheln basiert. Normalerweise empfehlen die Leute, Entitäten an einen Renderer zu übergeben, der sie rendert, nicht an sich selbst. Es scheint jedoch, dass das Gegenteil besser ist.

Warum ist einer besser als der andere?

Vielen Dank


Warum denkst du, ist die Umkehrung besser?

1
@Martin, da das Objekt andeutet, welche Bitmap verwendet werden soll. Warum also nicht einfach object-> render () ausführen?
jmasterx

Antworten:


11

Einige Überlegungen:

  • Wie Sie bereits erwähnt haben, müsste jedes Sprite "andeuten", welche Bitmap verwendet werden soll, aber wenn die Entität sich selbst rendern muss. Was wäre das für ein Hinweis? Wenn es sich für jedes Sprite um einen Verweis auf eine andere Bitmap, ein anderes Sprite-Blatt usw. handelt, wird möglicherweise mehr Speicher als erforderlich belegt, oder Sie haben Probleme beim Verwalten dieses Speichers. Ein Vorteil eines separaten Renderers besteht darin, dass Sie nur eine Klasse für das Asset-Management verantwortlich haben. Das heißt, in einem SF2-ähnlichen Kampfspiel hast du möglicherweise nur zwei Sprites;)

  • Wie an anderer Stelle erwähnt, müssen Sie den Code für alle Ihre Sprites ändern, wenn Sie Ihre grafische API ändern möchten.

  • Das Rendern erfolgt selten ohne Verweis auf einen grafischen Kontext. Entweder gibt es eine globale Variable, die dieses Konzept repräsentiert, oder jedes Sprite verfügt über eine Schnittstelle mit render (GraphicalContext ctx). Dies vermischt die grafische API und die Logik Ihres Spiels (was manche Leute als unelegant empfinden) und kann Kompilierungsprobleme verursachen.

  • Ich persönlich finde, dass das Trennen des Renderings von den einzelnen Entitäten ein interessanter erster Schritt ist, um Ihr Spiel als ein System zu betrachten, das überhaupt keine Grafiken benötigt. Ich meine, wenn Sie das Rendern aus dem Weg räumen, stellen Sie fest, dass das Spiel in einer "nicht-grafischen Welt" abläuft, in der die Koordinaten der Entitäten, ihre internen Zustände usw. entscheidend sind. Dies öffnet die Tür für automatisierte Tests, stärker entkoppelte Systeme usw.

Alles in allem bevorzuge ich Systeme, bei denen das Rendern von einer separaten Klasse durchgeführt wird. Das bedeutet nicht, dass Ihre Sprites keine Attribute haben können, die "grafisch zusammenhängen" (Animationsname, Animationsrahmen, Höhe x Breite, Sprite-ID usw.), wenn dies den Renderer einfacher zu schreiben oder effizienter macht.

Und ich weiß nicht, ob dies auf 3D zutrifft (wo der Begriff der Netze und die verwendete Koordinatenvariable möglicherweise mit Ihrer 3D-API verknüpft sind), wohingegen x, y, h, w so ziemlich unabhängig von 2D ist API).

Ich hoffe, das hilft.


11

Sie möchten, dass das Rendering-System kontrolliert, was wann gezeichnet wird. Wenn stattdessen die Sprites die Kontrolle über das Rendering haben, verlieren Sie an Effizienz und Flexibilität. Ich bin der Meinung, dass die Kontrolle über das Rendering-System zu einem saubereren Code führt.

Einige Vorteile des zentralen Renderns:

  • Z-Reihenfolge:
    Wenn die Spielobjekte selbst für das Rendern verantwortlich sind, müssen Sie sicherstellen, dass Sie sie in der richtigen Reihenfolge aufrufen. Andernfalls können Hintergrundobjekte über Vordergrundobjekte gezogen werden.
    Mit dem Rendering-System im Griff kann es alle Rendering-Objekte sortieren, Überlappungen zum Rendering-Zeitpunkt erkennen und diese einfach rendern oder auf die gesamte Reihenfolge verzichten. Der Punkt ist, dass die Entscheidung jetzt einfach getroffen werden kann.
  • Batching:
    Der andere offensichtliche Vorteil der Kontrolle über das Rendering-System ist das Batching. Auch hier muss das Rendersystem die Option haben, Sprites im Batch zu rendern, um die Freigabe mit einer Textur zu versehen. Mit Triangle Slicing kann alles mit einem Aufruf gerendert werden. Möglicherweise können einige Render-Berechnungen zwischengespeichert werden. Oder es könnte einfach jedes Sprite der Reihe nach mit nichts von dem ausgefallenen Zeug rendern. (Hinweis: Es ist möglich, dass jedes Objekt für sich gerendert wird, das Problem ist jedoch weniger effizient und komplex).

So wie ich das in meinen Spielen implementiere, müssen Spielobjekte die Sprites registrieren, die sie mit dem Rendersystem zeichnen möchten. Wenn das Objekt nicht mehr gezeichnet werden soll, hebt es die Registrierung des Sprites auf oder markiert es als inaktiv.

Das alles sagte. Wenn es einfacher ist, Ihre Spielobjekte auf alle Fälle selbst rendern zu lassen, tun Sie dies auf diese Weise. Es ist viel wichtiger, Fortschritte zu erzielen und etwas zu zeichnen, als eine perfekte Architektur zu haben.


Über die Z-Reihenfolge, wenn sich Objekte selbst zeichnen und ein System nicht über die Reihenfolge entscheiden kann, in der die Zeichenmethode aufgerufen wird? Ich meine, zentralisierte vs nicht zentralisierte scheinen keinen Unterschied über die Z-Reihenfolge zu machen.
GorillaApe

3

Haftungsausschluss: Ihre Frage enthält nicht viele Details, daher antworte ich mit einem allgemeinen Prinzip. Bitte entschuldigen Sie, wenn ich Ihre Verwendung oder "Rendern" missverstanden habe.

Im Allgemeinen verwende ich ein externes Objekt zum Rendern verschiedener Akteure in einer Szene, um Eigenschaften und Methoden auf Szenenebene außerhalb der einzelnen "Akteursobjekte" zu kapseln. Objekte in der Szene sollten nur interne Methoden und Eigenschaften enthalten. Sie sollten nur wissen, was sie selbst sind und was sie tun. Vermutlich werden sie von anderen Objekten im Spiel sowie von Benutzereingaben beeinflusst. Dies beeinflusst, wie / ob sie auf dem Bildschirm gerendert werden. Ein 'Director-Objekt' kann zum Beispiel den 'w'-Tastendruck zum Springen übersetzen und dann dem Actor-Objekt .jump () mitteilen. Eine solche Logik auf Regieebene kann den Schauspielern auch anweisen, die Szene vollständig zu betreten oder zu verlassen.

Prost, David


Aber in diesem Sinne konnte der Regisseur nicht einfach acton-> setVisible (false) sagen. ?
jmasterx

Sogar in dem Fall setVisible (false) ist es eine externe Entität, die das Rendern durch Überprüfen der sichtbaren Variablen des Akteurs und Rendern nur dann durchführt, wenn es wahr ist.
Nav

Nur einen Schauspieler unsichtbar zu machen, entfernt ihn nicht von der Szene. Es muss auch aufhören, an Kollisionen usw.
teilzunehmen

3

Was ist, wenn Sie eines Tages Ihr Spiel auf eine andere Auflösung portieren möchten (z. B. iPhone und Freunde)? Wie können Sie als globale Eigenschaft zum Rendern von Änderungen Ihren Code auf einfache Weise aktualisieren?


3

Was ich benutzte, war ein beobachterbasiertes Design. Als ich eine Instanz einer Klasse erstellte, die ich rendern wollte, wurde ein Zeiger darauf in der zentralen Renderer-Klasse gespeichert. Wenn Sie aufrufen RenderFrame(), verfügt der Renderer bereits über alle vorhandenen Objekte, die er zum Rendern benötigt, und greift dazu auf deren Eigenschaften zu. Die Klassen selbst hatten keine Ahnung, dass sie überhaupt gerendert werden würden. Diese API war schön und sauber und einfach zu bedienen.


1
+1 Interessant. Ich habe diesen Ansatz für Sound verwendet, während ich das Besuchermuster für Grafiken verwendet habe. Ich dachte, dass dies für Sound sinnvoller ist, da Grafik und KI auf derselben Uhr laufen, der Audiomixer auf einer anderen, sodass ein Ereignismodell einfacher ist. Es ist auch nicht kritisch, wenn ein Bewegungsereignis (das bewirkt, dass sich Pan / Reverb eines Audiokanals ändert) einige Millisekunden zu spät eintritt. Es ist jedoch kritisch, wenn ein Sprite im falschen Zustand gezeichnet wird.
30.

2

Im Allgemeinen geht es immer darum, wie einfach es ist, Ihren Code zu pflegen und zu erweitern. Morgen stellen Sie fest, dass Sie die aktuell verwendete Grafik-API nicht mögen und wechseln möchten. Müssen Sie jetzt alle Ihre Objektklassen durchgehen und alles ändern, oder müssen Sie Ihren Code nur noch an einem zentralen Punkt des Projekts ändern?

Es hängt davon ab, was Ihre Objekte wirklich tun, wenn Sie render () aufrufen. Solange sie nur Methodenaufrufe um Ihre Grafik-Engine wickeln, ist dies völlig in Ordnung, da die Grafikunterscheidung für Logik <-> weiterhin gegeben ist.

Wenn zum Beispiel Ihre render () -Methoden im Grunde genommen Convenience-Methoden sind und ungefähr so ​​aussehen:

void MyClass::render(const Graphics &g)
{
    g.draw(this);
}

oder

void MyClass::render()
{
   mySprite->render();
}

oder

void MyClass::render()
{
    mySprite->UseShader(thatshader);
    mySprite->render();
}

oder nahe dran, ich glaube nicht, dass es ein Problem ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.