Das Konzept
Grundsätzlich ist ein Szenengraph nichts anderes als ein bidirektionaler azyklischer Graph, der dazu dient, eine hierarchisch strukturierte Menge von räumlichen Beziehungen darzustellen.
Engines in freier Wildbahn neigen dazu, wie bereits erwähnt, andere Goodies in das Szenendiagramm aufzunehmen. Ob Sie das als Fleisch oder als Kuh sehen, hängt wahrscheinlich von Ihren Erfahrungen mit Motoren und Bibliotheken ab.
Leichtgewichtig bleiben
Ich bevorzuge den Unity3D-Stil, bei dem Ihr Szenendiagrammknoten (der im Mittelpunkt eher eine topologische als eine räumliche / topografische Struktur darstellt) räumliche Parameter und Funktionen enthält. In meiner Engine sind meine Knoten sogar noch leichter als in Unity3D, wo sie eine Menge unnötiger Junk-Mitglieder von Superklassen / implementierten Schnittstellen erben:
- übergeordnete / untergeordnete Zeigermitglieder.
- Raumparameterelemente vor der Transformation: xyz-Position, Neigung, Gieren und Rollen.
- eine Transformationsmatrix; Die Matrizen in einer hierarchischen Kette können sehr schnell und einfach multipliziert werden, indem Sie rekursiv den Baum hinauf- und hinuntergehen. Auf diese Weise erhalten Sie die hierarchischen räumlichen Transformationen, die das Hauptmerkmal eines Szenendiagramms sind.
- Eine
updateLocal()
Methode, die nur die Transformationsmatrizen dieses Knotens aktualisiert
- Eine
updateAll()
Methode, die diese und die Transformationsmatrizen aller untergeordneten Knoten aktualisiert
... Ich füge meiner Knotenklasse auch Bewegungsgleichungslogik und damit Geschwindigkeits- / Beschleunigungsglieder (linear & winklig) hinzu. Sie können darauf verzichten und es stattdessen in Ihrem Haupt-Controller abwickeln, wenn Sie möchten. Aber das ist es - in der Tat sehr leicht. Denken Sie daran, Sie könnten diese auf Tausenden von Entitäten haben. Also, wie Sie vorgeschlagen haben, lassen Sie es leicht.
Hierarchien aufbauen
Was sagen Sie zu einem Szenendiagramm, das auf andere Szenendiagramme verweist? Ich warte auf die Pointe. Natürlich tun sie das. Das ist ihre Hauptverwendung. Sie können einen beliebigen Knoten zu einem beliebigen anderen Knoten hinzufügen. Transformationen sollten automatisch im lokalen Bereich der neuen Transformation stattfinden. Alles, was Sie tun, ist, einen Zeiger zu ändern. Es ist nicht so, als würden Sie Daten kopieren! Durch Ändern eines Zeigers erhalten Sie ein tieferes Szenendiagramm. Wenn die Verwendung von Proxies die Dinge effizienter macht, dann auf jeden Fall, aber ich habe nie die Notwendigkeit gesehen.
Vermeiden Sie renderbezogene Logik
Vergessen Sie das Rendern, während Sie Ihre Szenendiagramm-Knotenklasse schreiben, oder Sie werden die Dinge für sich selbst verwirren. Alles, was zählt, ist, dass Sie ein Datenmodell haben - ob es sich um das Szenendiagramm handelt oder nicht - und dass ein Renderer dieses Datenmodell inspiziert und Objekte in der Welt entsprechend rendert, ob es sich um 1, 2 handelt , 3 oder 7 Dimensionen. Der Punkt, den ich anspreche, ist: Verunreinigen Sie Ihr Szenendiagramm nicht mit Renderlogik. In einem Szenendiagramm geht es um Topologie und Topographie, dh Konnektivität und räumliche Eigenschaften. Dies ist der wahre Stand der Simulation und existiert auch ohne Rendering (das von der ersten Person über eine statistische Grafik bis hin zu einer textuellen Beschreibung jede Form unter der Sonne annehmen kann). Knoten verweisen nicht auf renderbezogene Objekte - möglicherweise ist dies jedoch umgekehrt. Beachten Sie auch Folgendes: Nicht jeder Szenendiagrammknoten in Ihrem gesamten Baum kann gerendert werden. Viele werden nur Container sein. Warum also überhaupt Speicher für ein Zeiger-auf-Render-Objekt reservieren? Selbst ein Zeigermitglied, das nie verwendet wird, belegt immer noch Speicher. Kehren Sie also die Zeigerrichtung um: Die renderbezogene Instanz verweist auf das Datenmodell (das möglicherweise Ihr Szenendiagrammknoten ist oder diesen enthält), NICHT umgekehrt. Wenn Sie auf einfache Weise die Liste der Controller durchsuchen und dennoch Zugriff auf die zugehörige Ansicht erhalten möchten, verwenden Sie ein Wörterbuch / eine Hashtabelle, die sich der Lesezugriffszeit von O (1) nähert. Auf diese Weise gibt es keine Verunreinigung, und Ihre Simulationslogik kümmert sich nicht darum, welche Renderer vorhanden sind, was Ihre Tage und Nächte der Codierung macht Warum also überhaupt Speicher für ein Zeiger-auf-Render-Objekt reservieren? Selbst ein Zeigermitglied, das nie verwendet wird, belegt immer noch Speicher. Kehren Sie also die Zeigerrichtung um: Die renderbezogene Instanz verweist auf das Datenmodell (das möglicherweise Ihr Szenendiagrammknoten ist oder diesen enthält), NICHT umgekehrt. Wenn Sie auf einfache Weise die Liste der Controller durchsuchen und dennoch Zugriff auf die zugehörige Ansicht erhalten möchten, verwenden Sie ein Wörterbuch / eine Hashtabelle, die sich der Lesezugriffszeit von O (1) nähert. Auf diese Weise gibt es keine Verunreinigung, und Ihre Simulationslogik kümmert sich nicht darum, welche Renderer vorhanden sind, was Ihre Tage und Nächte der Codierung macht Warum also überhaupt Speicher für ein Pointer-to-Render-Objekt reservieren? Selbst ein Zeigermitglied, das nie verwendet wird, belegt immer noch Speicher. Kehren Sie also die Zeigerrichtung um: Die renderbezogene Instanz verweist auf das Datenmodell (das möglicherweise Ihr Szenendiagrammknoten ist oder diesen enthält), NICHT umgekehrt. Wenn Sie auf einfache Weise die Liste der Controller durchsuchen und dennoch Zugriff auf die zugehörige Ansicht erhalten möchten, verwenden Sie ein Wörterbuch / eine Hashtabelle, die sich der Lesezugriffszeit von O (1) nähert. Auf diese Weise gibt es keine Verunreinigung, und Ihre Simulationslogik kümmert sich nicht darum, welche Renderer vorhanden sind, was Ihre Tage und Nächte der Codierung macht Wenn Sie auf einfache Weise die Liste der Controller durchsuchen und dennoch Zugriff auf die zugehörige Ansicht erhalten möchten, verwenden Sie ein Wörterbuch / eine Hashtabelle, die sich der Lesezugriffszeit von O (1) nähert. Auf diese Weise gibt es keine Verunreinigung, und Ihre Simulationslogik kümmert sich nicht darum, welche Renderer vorhanden sind, was Ihre Tage und Nächte der Codierung macht Wenn Sie auf einfache Weise die Liste der Controller durchsuchen und dennoch Zugriff auf die zugehörige Ansicht erhalten möchten, verwenden Sie ein Wörterbuch / eine Hashtabelle, die sich der Lesezugriffszeit von O (1) nähert. Auf diese Weise gibt es keine Verunreinigung, und Ihre Simulationslogik kümmert sich nicht darum, welche Renderer vorhanden sind, was Ihre Tage und Nächte der Codierung machtWelten leichter.
Zum Keulen siehe oben. Das Culling von Interessengebieten ist ein Konzept der Simulationslogik. Das heißt, Sie bearbeiten die Welt nicht außerhalb dieses (normalerweise kastenförmigen, kreisförmigen oder kugelförmigen) Bereichs. Dies erfolgt in der Haupt-Controller / Game-Schleife, bevor das Rendern erfolgt. Kegelstumpf-Keulen ist dagegen rein renderbezogen. Also vergessen Sie jetzt das Keulen. Es hat nichts mit Szenendiagrammen zu tun, und wenn Sie sich darauf konzentrieren, werden Sie den wahren Zweck dessen, was Sie erreichen möchten, verschleiern.
Ein letzter Hinweis ...
Ich habe das starke Gefühl, dass Sie aus einem Flash-Hintergrund (speziell AS3) stammen, wenn man alle Details zum Rendern berücksichtigt, die hier enthalten sind. Ja, das Flash Stage / DisplayObject-Paradigma enthält die gesamte Renderlogik als Teil des Szenegraphen. Aber Flash geht von vielen Annahmen aus, die Sie nicht unbedingt machen möchten. Für eine vollwertige Spiel-Engine ist es aus Gründen der Leistung, des Komforts und der Kontrolle der Codekomplexität besser, die beiden nicht zu kombinieren .
Renderable
s (einer Schnittstelle oder abstrakten Klasse) intern für diese zentralen Model-Controller-Objekte zu speichern. Gute Beispiele hierfür sind Entities oder UI-Elemente. Auf diese Weise können Sie schnell auf nur die Renderer zugreifen, die für dieses bestimmte Kernobjekt relevant sind - ohne Implementierungsspezifikationen, die die Entitätsklasse und damit die Verwendung von Schnittstellen kontaminieren würden.Renderer
.