Model Design, Komponenten & OOP in dynamischen Basisklassen?
-
Ich erstelle gerade ein Spiel und da ich momentan eh bei der Portierung und Fakturierung des Codes bin habe ich meine Gedanken mal etwas abschweifen lassen.
Ich komme eigentlich aus der Ecke OOP, von daher wären Richtigstellungen etwaiger Fehlinterpretationen und Hinweise auf Abstruse, Unsinnige Ideen erwünscht.
Ich habe mich die letzten Tage mit Komponenten basierten Modellen auseinander gesetzt , MVC & MVVM um mal Stichworte zu nennen aber nicht ausschließlich diese.
Meine Gedanken dazu gehen aktuell in die Richtung Komponenten Klassen zu definieren welche hauptsächlich Ihre Eigenschaften definieren oder reine Funktionssammlungen darstellen, welche dann dynamisch via Manager als Komponenten, Attribute und Funktionen in eine Basisklasse geladen werden.
Um hier ein Beispiel zu nennen:
Normalerweise würde gibt es in einer Renderengine eine Klasse Node bzw. SceneNode. welche Funktionen für Transformation, Translation, Parent und Children, etc. zur Verfügung stellt.
Nun möchte man ein "gameObject" erstellen, meistens bleibt es aufgrund der Vielfalt moderner Spiele nicht bei einem "gameObject" da oft unterschiedlichste Attribute und Funktionen benötigt werden.
Oftmals wird dann ein großes gameObject ddefiniertund deren Funktionen in Einzellteile(einzelne Klassen) zerlegt um die Übersicht nicht zu verlieren.So kann Beispielsweise ein gameObject durch Attribute des "Fahrzeuges", des "Spielers(Player)" welche wiederum Auswirkungen auf Leistungsfähigkeit von Waffen, Verteidigungssystemen, Lebenspunkten, Schilden, Panzerung, Reparaturleistung, Geschwindigkeit, Laderaumtc. verändert werden oder ganz einfach nur ein Stein sein der Dumm in der Gegend rum liegt, ein Mesh und eine Textur sowie ein Material besitzt und eine gewisse Masse aufweist evtl. ein kollidierbares Objekt darstellt und somit ggf. Auswirkungen auf Physikalische Reaktionen anderer gameObjects haben kann, aber im Grunde genommen würde sich die Klasse Meinetwegen eines Panzers und des Steins ganz erheblich im Funktionsumfang, den Attributen unterscheiden.
Zurück zu meinen vorherigen Äußerungen :
Um das ganze nun in Komponenten aufzuteilen könnte man folgende Komponenten erstellen:Laderaum
Spieler
Vehikel
Waffen
Schilde
Bonus
etc.Daraus ließeich dann meinetwegen ein Spieler oder NPC bezogenes gameObject via Manager erstellen indem man einer Basisklasse, welches man meinetwegen playerGameObject nennt, aus VeVehikelSpieler, Waffen, Schilde usw. einen Panzer zuzusammenstelltder die vom VeVehikelitgebrachten Attribute besitzt, dazu nimmt man dann Attribute des der Spieler Klasse welche sich dann zusammen mit der Bonus Klasse auf die Attribute des VeVehikelsder Schilde, Waffen, Laderaum, usw. Auswirken und zum Ende hin dann Meinetwegen einen Panzer mit doppelt so großen Laderaum, 50% mehr Schilden, 30% mehr Feuerkraft etc. besitzt darstellt um in konkreten für das Spiel relevanten Werten zu Sprechen.
Wobei die Bonus Klasse dann rein zur Berechnung verwendet wird, Spieler, Vehikel, Waffen(Waffenmodule), Schilde(Schildmodule) hingegen stellen hauptsächliche Klassen für Grundsätzliche Attribute dar welche aber auch renderbare Anteile (SceneNodes, Billboards, etc. besitzen).Nun könnte man noch weiter gehen und mal das ganz einfache Beispiel des Steins nehmen.
Der besteht sozusagen auch aus diversen Komponenten:
* Kann dem Inventar Hinzugefügt werden
* Zerstörbarkeit
* Masse(Physikalisch Relevante Masse)
* Volumen(Verdrängung in m³)
* Größe(Abmessungen in cm oder m)* Substanz(die Materialien aus denen der Stein zusammen gesetzt ist, ggf. Interessant für Story, finanzieller Wertigkeit oder physikalischen Auswirkungen wie Härtegrad)
* Mesh(also das Renderbare 3D Model)
* Material(das graphisch Renderbare Oberflächen Material)
* Textur(wieder die Renderbare Oberflächen Beschaffenheit)wenn man nun wieder zurück geht zum Panzer von oben so hat dieser eine Komponente genannt Laderaum, wie würde sich logischerweise diese Komponente Zusammensetzen:
* Kann dem Inventar Hinzugefügt werden
* Zerstörbarkeit
* Masse(Physikalisch Relevante Masse)
* Volumen(Verdrängung in m³)
* Größe(Abmessungen in cm oder m)
* Aufnahmevolumen(Also das was in den Laderaum hinein passt)
* Einbaubarkeit(Meinetwegen die Modulgröße)
* Energiebedarf(Für Licht, Kühlung, etc.)* Substanz(die Materialien aus denen der Stein zusammen gesetzt ist, ggf. Interessant für Story, finanzieller Wertigkeit oder physikalischen Auswirkungen wie Härtegrad)* Mesh(also das Renderbare 3D Model)
* Material(das graphisch Renderbare Oberflächen Material)
* Textur(wieder die Renderbare Oberflächen Beschaffenheit)Und wir sehen das die Laderaum Komponente eigentlich ähnlich der eines Steins ist.
Nun könnte man dazu übergehen die oben genannten Komponenten wie Laderaum, Vehikel, Spieler etc. wie gerade beim Stein gesehen in Ihre Basisattribute zu zerlegen und auch diese Komponenten zu "Komponentisieren" (gibt es das Wort überhaupt?).
Im Endeffekt landet man dann bei den beiden extremsten Beispielen von einer Klasse Panzer ausgehend bei 1000den Unterklassen.
Nun war die Grundidee aber alles Dynamisch zu gestalten und sich nicht mehr Arbeit als notwendig zu machen zumal so viele Klassen auch einen riesigen "Verwaltungsaufwand" darstellen.
Ein Dilemma, was ist Notwendig, was nicht und was ist sogar grober Unfug.
Gehen wir mal von der Grundidee der Dynamisierung und der Vereinfachung aus und akzeptieren beim Aufbau unseres Models einen erhöhten Arbeitsaufwand.
Wir unterteilen erstmal unsere Komponenten in
* Attribute (Attribut Ebene)
* Renderbar (Modul Ebene)
* Code basiert (Util Ebene)Natürlich ließen diese sich noch weiter in Kategorien unterteilen aber bleiben wir erstmal bei dieser Aufteilung.
Nun geben jeder Basis-Komponente erstmal folgende Eigenschaften / Funktionen:
für Attribute
* get/set ID (eindeutig)
* get/set Value
* get/set Name
* get/set/removeFrom Parent (hier bezogen auf die Parent ID)
* needUpdate
* initrenderbare erhalten zusätzlich
* get/set Mesh
* get/set Material
* get/set Texture
* get/set IconCode basiertee lassen wir mal außen vor und sehen sie als Util (also Hilfsmittelklassen welche dazu dienen Komplexer ggf. wiederverwendbarere Berechnungen Durchzuführen) und vom Manager direkt angesprochen werden.
Weiterführende Komponenten (reden wir hier mal von Subsystemen) erhalten folgende Funktionen.
* get/set ID (eindeutig)
* get/set Value
* get/set Name
* get/set/removeFrom Parent (hier bezogen auf die Parent ID)
* needUpdate
* init
* get/set Icon* get/add/remove Children ("by ID")
* get/add/remove Attribute ("by ID")
* get/add/remove Renderbar ("by ID")
* get/add/remove Subsystem ("by ID")dann haben wir doch eigentlich schon ein Komplettes Model aus dem sich ein Panzer, ein Spieler, ein Stein oder was auch immer dynamisch zusammen setzen lässt.
Um das Beispiel Stein zu nehmen was wir mal oben Anschauen können so ist dort alles Vorhanden.
Naja der Manager bräuchte natürlich noch Zugriff auf die eben außen vorgelassenen Code-Klasse(n) auch Util´s genannt um meinetwegen aus Masse,Größe, Volumen die Trägheit zu berechnen, oder aus aus Substanz und Volumen die Masse an sich, wenn wir kleinkariert wären fehlt da oben sogar noch die Dichte aber wir wollen es nicht zu weit Treiben.
Dazu müsste er die Daten der Konfiguration noch irgendwo temporär Speichern aber dazu später mehr.Was fehlt (vielleicht noch keinem aufgefallen) sind Events(Listener) aber Normalerweise besitzen Renderbare auch event-Listener, Eingabe Events werden üblicherweise in den Entsprechenden Eingabe library´s und deren Funktionen verarbeitet und können hier an den Manager weitergeleitet werden.
Manager, das Wort ist hier schon oft gefallen, was macht der eigentlich?
Nun in dem Beispiel oben haben wir:Attributs Klasse (Attributs Ebene)
Renderbare Klasse (Modul Ebene)
Subsystem Klasse ((Sub) System Ebene)Util (Code Ebene)
Bisher nicht angesprochen, für das Model nicht unbedingt Wichtig aber Notwendig sind.
Konnektivität
und
PersistenzWieder zurück zum Manager um die Frage zu beantworten:
Der Manager bezieht über Konektivität und Persistenz einen Datensatz bezüglich des Aufbau des "gameObjects" (der Begriff ist eigentlich nicht ganz richtig, evtl sollten wir es besser Konfiguration´s ID nennen) und setzt diese Dynamisch zusammen, eigentlich reicht hier eine (eben so bezeichnete) Subsystem-Klasse.
Die sich aus Attributen, Renderbaren und andere Subsystemen zusammensetzt und diese in den dafür vorgesehenen "Listen" - "Maps" (wie auch immer man das programmatisch realisieren möchte) der Attribute, Renderbare, Subsysteme mittels den Funktionen:
* get/add/remove Children ("by ID")
* get/add/remove Attribute ("by ID")
* get/add/remove Renderbar ("by ID")
* get/add/remove Subsystem ("by ID")temporär zwischenspeichert.
Dazu braucht der Manager selbst noch einen temporärern Speicher welcher eigentlich auch schon den gerammten Manager darstellt dieser ist recht simpel und klein:
* get/add/remove List
* get/add/remove IDDieser temporäre Speicher des Managers hat folgende Gründe:
* Verwaltung der einzelnen ID´s und Konfigurationen
* Für Events und andere Einflüsse liefert er ID´s und Konfigurationen (Listen) für den eigentlichen Spiele spezifischen Code.So können Events direkt über die ID auf die Attribute, Module und Subsysteme zugreifen und Werte direkt Setzen sowie durch needUpdate die Notwendigkeit der Weiterleitung, ggf. auch beim Parent setzen.
Updates durch beispielsweise Positions-Änderung werden durch needUpdate gesetzt.
So kann in der RenderLoop jede ID via Manager und Iteration der ID´s nachgefragt werden ob ein Update benötigt wird und dieses ggf. durchführen.
Zugriffe auf die Code basierten util Klassen werden vom eigentlichen Spiele Code aus getätigt und via Manager an die entsprechenden ID´s verteilt.
(Im Falle der Bonus Klasse beispielsweise beim Initieren zum Anpassen der Attributswerte oder bei Veränderung der Äußern Einflüsse oder Spieler-Skills)Und was ist nun der Unterschied zwischen dem Manager und den Subsystem Klassen welche eine Konfiguration von Attributen, Modulen, Subsystemen darstellt? so gesehen gibt es nur einen einzigen Unterschied.
Die Subsystemklasse stellt die Methoden und Funktionen bereit dynamisch unterschiedliche Datentypen und Objekte in Listen zu Speichern und die Parent Child Relation für die Engine herzustellen während der Manager lediglich die ID´s Speichert und verwaltet.
_________________________________________________
So und nun nach Äonen von Zeilen und dem Dank fürs durchhalten beim Lesen bitte ich um Berichtigung meiner Fehler, falsch verstandenen Umsetzungen und dem Virtuellen Nacken schlag für soviel Unsinn, der mir gerade allerdings selbst sehr logisch vorkommt.
Des weiteren sind Aufzählungen von Vor- sowie Nach- teilen, Verbesserungsvorschläge Gegenüberstellungen zur reinen OOP und klassischem Polymorphismus etc. gewünscht.
Na dann bin ich mal gespannt wie sehr ich zerrissen werde und ob sich überhaupt einer das lesen Antut.