Objektdesign



  • Hallo allerseits,

    ich versuche mich gerade an C++ und bin dabei ein kleines Spiel zu entwickeln.

    Das größte Problem was ich dabei habe ist allerdings das Design.

    Ich trenne derzeit meine Spielobjekte in die eigentlichen Objekte (also z.B. Gebäude) und deren Grafiken (ein Gebäude kann z.B. je nach Zustand eine andere Grafik haben).
    Das Problem was ich damit habe ist folgendes:

    Wie bringe ich beides am besten zusammen?
    Der Gebäudeklasse als Membervariable einen Zeiger vom Typ der Animationklasse geben, oder alles in einer Managerklasse zuordnen?

    Die Animationen sollen von einer Managerklasse erzeugt und verwaltet werden, da dieser Manager in Intervallen alle Animationen aktualisieren soll.
    Nun muss aber jeder Animation gesagt werden, was sie anzeigen soll.

    Die Hierarchie so einer Animation ist wie folgt aufgebaut:

    Frame (einzelnes Bild)
    Animation (enthält alle Frames für ihre Darstellung, also z.B. das Entstehen eines Gebäudes)
    AnimationsObjekt (enthält beliebige Animationen, die zu Schlüsselkonstanten abgelegt sind, kann z.B. alle Zustände eines Gebäudes beinhalten [Aufbau, Normalansicht, Beschädigung, Zerstört)

    Ein AnimationsObjekt enthält somit alle für ein Gebäude notwendigen Animationen.
    Diese AnimationsObjekte liegen nun im Manager ab und werden in Intervallen abgeprüft, ob sich ihre Darstellung ändern soll (also z.B. neuer Frame der aktiven Animation).
    Nun muss aber ein Bezug zw. einem AnimationsObjekt und z.B. einem Gebäude hergestellt sein, damit sich beim Wechsel des Gebäudezustandes die aktive Animation des AnimationsObjektes ebenfalls umstellt (z.B. von normal auf beschädigft).

    Wie verknüpft man das am besten?

    Bzw. was haltet ihr von diesem Ansatz oder welchen anderen würdet ihr vorschlagen!?

    Vielen Dank schon einmal
    Ciao



  • generell die Ansicht von den Daten zu trennen ist schon ein guter Ansatz.
    Wie wäre es so:

    Es gibt: Gebäude, Animationsdaten und Animationszustand.
    Das Gebäude enthält eben wer drin arbeitet, wie alt es ist, wieviel es aushält und so, und eben auch ob es grad gebaut wird usw.

    Animationsdaten sind die Daten, wie eine Animation darzustellen ist, also wie der jeder frame auszusehen hat. Diese muss ja nur einmal im speicher liegen, es wäre sinnlos, wenn man 10mal das gleiche gebäude baut 10solche gleichen animationen zu laden. Hier kommt der Animationszustand ins spiel:
    Er speichert, welcher frame gerade abgespielt wird, wie lange die animation noch dauert und so.

    Ein Gebäude hat dann unter anderem einen Animationszustand, welcher seinerseits wieder auf die Daten der Animation zugreift. Nun kann das Gebäude seinem Animationszustand sagen, du bist nicht mehr dran, ich bin fertig gebaut oder sonstwas. DAs Gebäude wechselt quasi die animation aus.

    Weiterhin kannst du ja ein singleton-animations-manager machen, wo sich alle animationszustaände registrieren und der manager ruft dann für alle animationen einmal pro Frame Move auf. Ein Animationszustand sollte selber wissen, wann ein anderer Frame gezeigt werden soll, das muss der manager imho nciht machen.

    Vielleicht konnt ihc dir ja ein paar tipps geben, viel erfolg und spaß 🙂
    Maxi



  • Danke für die Antwort!

    Maxi schrieb:

    generell die Ansicht von den Daten zu trennen ist schon ein guter Ansatz.
    Wie wäre es so:

    Es gibt: Gebäude, Animationsdaten und Animationszustand.
    Das Gebäude enthält eben wer drin arbeitet, wie alt es ist, wieviel es aushält und so, und eben auch ob es grad gebaut wird usw.

    Animationsdaten sind die Daten, wie eine Animation darzustellen ist, also wie der jeder frame auszusehen hat. Diese muss ja nur einmal im speicher liegen, es wäre sinnlos, wenn man 10mal das gleiche gebäude baut 10solche gleichen animationen zu laden. Hier kommt der Animationszustand ins spiel:
    Er speichert, welcher frame gerade abgespielt wird, wie lange die animation noch dauert und so.

    Ein Gebäude hat dann unter anderem einen Animationszustand, welcher seinerseits wieder auf die Daten der Animation zugreift. Nun kann das Gebäude seinem Animationszustand sagen, du bist nicht mehr dran, ich bin fertig gebaut oder sonstwas. DAs Gebäude wechselt quasi die animation aus.

    Hm, im Prinzip hab ichs so (habs vllt. nicht so gut erklärt).

    Weiterhin kannst du ja ein singleton-animations-manager machen, wo sich alle animationszustaände registrieren und der manager ruft dann für alle animationen einmal pro Frame Move auf. Ein Animationszustand sollte selber wissen, wann ein anderer Frame gezeigt werden soll, das muss der manager imho nciht machen.

    Und hier wird die Sache interessanter.
    Zur Zeit liegen die Grafikdaten nur einmal im Speicher.
    Jedes FrameObjekt der Frameklasse bekommt einen Zeiger auf seine Grafikdaten.

    Die Frameobjekte einer Animation liegen dann alle in einem Vector. Es kann aber Vektoren mit gleichen Animationen geben, d.h. die Frameobjekte verschiedener Vektoren beinhalten Zeiger auf die gleiche Grafik (da ja gleiche Gebäude an unterschiedlichen Positionen stehen können). Hier könnte ich noch optimieren, da gleiche Frameobjekte ja in unterschiedlichen Animationsobjekten stecken können, da ihre Dimension ja immer die gleiche ist.
    Faktisch ist es sogar so, dass alle Frameobjekte z.B. einer Gebäudebauanimation in allen Gebäudebauanimationen gleicher Gebäude identisch sind. Die veränderlichen Parameter (z.B. Position) ist aber abhängig vom jew. Gebäude und damit bekommt jedes Gebäude einen Zeiger auf ein eigenes Animationsobjekt (die Frames und Grafiken dahinter sind aber immer die gleichen, um Speicher zu sparen).

    Hier ists z.B. so, dass die Positionen beim Gebäude und im Frame jeweils als int-Werte abliegen.

    Die Animationsobjekte kennen natürlich ihren Zustand, der Manager ist nur dafür da, sie in Intervallen aufzurufen, so dass sie ggf. ihren aktuellen Zustand zeichnen können. Der Manager ist ne Factoryklasse, die mit statischen Methoden arbeitet.

    D.h. also ich erzeuge ein AnimationsObjekt mit den notwendigen Animationen für ein Gebäude (im Manager, der es damit automatisch unter Kontrolle für die Intervallabfrage hat), gebe einen Zeiger (oder besser eine Referenz?) in das aktuell erzeugte Gebäude, das damit die Möglichkeit hat, seinen Zustand an das Animationsobjekt weiterzugeben.

    Hm, hoffe, das war verständlich.

    Ein anderes großes Problem das ich aber habe ist, wo nehme ich Zeiger, Referenzen, const etc.
    In STL-Containern kann ich ja keine Referenzen speichern. Da meine Frames einer Animation in nem vector und ich diesen ggf. nach außen reichen muss (hoffe nicht), ist die Frage wie als const * std::vector<FrameC *> * ?
    Oder soll ich z.B. eine konstante Referenz auf das Animationsobjekt in das Gebäude hängen, oder besser einen konstanten Zeiger auf ein konstantes AnimationsObjekt. In beiden Fällen kann ich aber wohl schlecht den Zustand des Animationsobjektes durch das Gebäudeobjekt ändern lassen?!

    Aber das gehört wohl eher ins C++-Forum.

    Ciao



  • Hm... naja, hat nicht mehr so viel mit dem forum zu tun, aber:

    Referenzen eignen sich in dem Fall nicht sehr gut. Denn sie kann man nur einmal initialisieren und dann bleiben sie fest auf das Objekt wo sie initiliaisert wurden. Referenzen als membervariablen muss man außerdem im konstruktor der klasse setzen, was vl auch nciht so gut ist, da das gebäude später vl seine animationsdaten ändern kann o.ä.
    Also zeiger. Für Objekte die sich gegenseitig oder einseitig benötigen aber nicht direkt zusammenhängen benutz ich generell zeiger, erst damit kann man die objekte dann wechseln usw.

    Wenn dein gebäude das animationsobjekt ändern will, kann es natürlich keinen const-zeiger haben das geht ja nicht, is ja klar. Das Gebäude sollte das Animationsobjekt vielleicht auch als private-member haben, weil ja da kein anderer drauf soll, außer der manager und der hat es ja auch in seiner eigenen liste. Frag mich wo du dir da ein const als nötig dachtest 🙂

    ein const std::vector<Frame*>* kann man machen, das bedeutet dass das zurückgegebenen den vector nciht verändern darf, also push_back, clear(), etc. Aber er darf die elemente verändern. Allerdings weiß ich auch nciht wozu du das brauchst. Ein Animationsobjekt sollte doch selber einen zeiger/sonstwas auf seine animationsdaten haben, oder?



  • Maxi schrieb:

    Hm... naja, hat nicht mehr so viel mit dem forum zu tun, aber:

    Referenzen eignen sich in dem Fall nicht sehr gut. Denn sie kann man nur einmal initialisieren und dann bleiben sie fest auf das Objekt wo sie initiliaisert wurden. Referenzen als membervariablen muss man außerdem im konstruktor der klasse setzen, was vl auch nciht so gut ist, da das gebäude später vl seine animationsdaten ändern kann o.ä.

    Wäre in meinem Fall evtl. sogar richtig.
    Ein Gebäude bekommt bei seiner Initialisierung einen Zeiger auf sein AnimationsObjekt mit allen benötigten Animationen. Diese sollen vom Gebäude nicht verändert werden, nur der Zustand wird im AnimationsObjekt umgeschaltet.
    Hier würde also eine Referenz (nicht const) funktionieren.

    Wenn dein gebäude das animationsobjekt ändern will, kann es natürlich keinen const-zeiger haben das geht ja nicht, is ja klar. Das Gebäude sollte das Animationsobjekt vielleicht auch als private-member haben, weil ja da kein anderer drauf soll, außer der manager und der hat es ja auch in seiner eigenen liste. Frag mich wo du dir da ein const als nötig dachtest 🙂

    Private Member ist natürlich selbstverständlich.
    Hab so beim Kennenlernen von C++ von const-Correctnes usw. gelesen (glaub bei Bruce Eckel) und für mich kam das so rüber, als das man const soviel als möglich nehmen sollte!?

    ein const std::vector<Frame*>* kann man machen, das bedeutet dass das zurückgegebenen den vector nciht verändern darf, also push_back, clear(), etc. Aber er darf die elemente verändern. Allerdings weiß ich auch nciht wozu du das brauchst. Ein Animationsobjekt sollte doch selber einen zeiger/sonstwas auf seine animationsdaten haben, oder?

    Das schon, aber eine Animation bekommt einen Vektor von Frames bei ihrer Initialisierung, dieser sollte auch nicht mehr verändert werden können (habe ja die 3-Teilung: Frame->Animation->AnimationsObjekt).

    Hatte in meinem letzten Post noch nen Denkfehler (bin durch Deine Anmerkung der Animationszustände drauf gekommen):
    Die Frameobjekte brauch ich alle wohl wirklich nur einmal, aber gleiche Animationen (z.B. für Gebäudebau) brauch ich mehrfach, da jede von ihnen in einem anderen Zustand sein kann (die Animationen sind ungefähr das, was Du mit den Zuständen meintest), dasselbe gilt für die AnimationsObjekte, die an den Gebäuden hängen, da eines z.B. ja als aktive Animation die Bauphase haben kann, das eines anderen Gebäudes aber gerade die Beschädigt-Animation aktiv haben muss).

    Ciao



  • Und noch eine Frage:

    Wo sollte das Zeichnen (Blitten) denn eher hin? In das Frameobjekt, welches die Grafik (BitMap) enthält, oder in das Objekt, das die Zeichenfläche enthält (z.B. Fenster, in meinem Fall ist es ein RastPort-Objekt)?

    Derzeit habe ich es so, dass der RastPort ein FrameObjekt bekommt und dann zum Blitten dessen BitMap abfragt.

    Da aber die BitMap und deren Getter private sind, müsste ich die RastPortklasse auch noch als friend der Frameklasse deklarieren(ist bereits für die FrameFactoryklasse der Fall)!?

    Ciao



  • Wieso ist denn die Bitmap im Frame private?
    Kann doch ruhig public sein - bzw. einfach ne public GetBitmap() Methode...?

    Und wie kommst du auf "RastPort" - hast du früher mal aufm Amiga programmiert?



  • hustbaer schrieb:

    Wieso ist denn die Bitmap im Frame private?
    Kann doch ruhig public sein - bzw. einfach ne public GetBitmap() Methode...?

    Ich kann den Zeiger, der für die BitMap aus dem Getter kommt nicht const machen, und die dahintersteckende BitMap leider auch nicht, da diese (bzw. der Zeiger darauf) an OS-Routinen übergeben wird und spätestens dann der Compiler meckert.
    Das bedeutet, dass jeder, der Zugriff auf den BitMap-Zeiger hat (via Getter), die BitMap des Frames ändern könnte und das will ich eigentlich verhindern!
    Daher so private und restriktiv wie möglich, derzeit über friend Klassen.

    Und wie kommst du auf "RastPort" - hast du früher mal aufm Amiga programmiert?

    Nicht ganz! Ich programmiere dieses Spiel auf und für den Amiga! 😃

    Ciao



  • Ich finde, ein Gebäude sollte sich selber zeichnen können. Du implementierst also Gebäude::render() und in dieser Funktion, die ja auch weiß an welcher stelle das haus steht usw wird dann für den passenden Frame Frame::Render() aufgerufen.
    Das/Der RastPort(was immer das is) sagt dann nur dem Gebäude dass es sich jetzt malen soll.



  • Maxi schrieb:

    Ich finde, ein Gebäude sollte sich selber zeichnen können. Du implementierst also Gebäude::render() und in dieser Funktion, die ja auch weiß an welcher stelle das haus steht usw wird dann für den passenden Frame Frame::Render() aufgerufen.

    Hm, dann muss das Gebäude die Meldung (also den Render-Aufruf) durchreichen, denn wie gesagt hab ja ne Hierarchie:

    Gebäude->AnimationsObjekt->Animation->Frames (mal von oben nach unten)

    Darum hatte ich auch die Trennung von Gebäude und Grafiken vorgesehen, da alle AnimationsObjekte ja auch noch mal im Manager liegen, ruft der einfach in Intervallen die Rendermethode aller AnimationsObjekte auf, die sich neu zeichnen möchten.
    Deine Idee hat natürlich auch was für sich, so würde die Rendermethode vom Gebäude immer dann gerufen, wenn sich z.B. sein Zustand ändert.
    Allerdings hätt ich da glaub ich mehr Schwierigkeiten mit dem Überdeckungsproblem fertig (wenn z.B. ein Gebäude von einer anderen Animation überdeckt wird und dabei seinen Zustand ändert muss die darüberliegende Animation ja auch neu gezeichnet werden) zu werden.
    Aber heh, jetzt wo ich drüber nachdenke könnte es sorum vllt. sogar besser funktionieren! Muss ich mal weiter verfolgen, v.a. was meine grafischen Cursor angeht!

    Das/Der RastPort(was immer das is) sagt dann nur dem Gebäude dass es sich jetzt malen soll.

    Da hab ich mich wohl falsch ausgedrückt. Der RastPort ist das Teil, in welches reingezeichnet wird (vielleicht einfacher, wenn man hier an ein Fenster denkt, in das gezeichnet werden soll), d.h. laut Deinem Vorschlag könnte ich ihn mir nur als Argument für die Rendermethode denken.
    Wenn ich das genauer erklären wöllte, müsste ich ein bisschen ausholen, was den internen Fensteraufbau angeht.
    Im Prinzip hat jedes Fenster (OS-seitig) nen RastPort und dieser ne BitMap. Will man nun z.B. ne Grafik darstellen, macht man das am besten so, dass man eine Grafikbitmap in die BitMap des RastPorts des gewünschten Zielfensters blittet.

    Die RastPort-Klasse hab ich mir selbst geschrieben, um ein paar RastPort-Funktionen zu kapseln. Vielleicht war das ja schon ein falscher Ansatz!?
    Sie hat z.B. ne Methode, die nen Zeiger auf nen Frame erwartet und dessen BitMap dann in die sichtbare BitMap des RastPorts blittet, so dass die Frame-Grafik sichtbar wird.

    Ciao



  • Hallo nochmals,

    habe mich nun für folgendes Vorgehen entschieden:

    Die Spielobjekte bekommen eine Referenz/einen Zeiger auf die Grafikobjekte.
    Wenn sich der Zustand eines Spielobjektes ändert gibt es das entsprechend an sein Grafikobjekt weiter, dass nun seinerseits das Aussehen ändert.

    Mit diesem Ansatz habe ich nun 2 Probleme:

    1. Werden die Änderungen von Spielobjekten unterschiedlich ausgelöst (via Timer oder Spielaktion), so dass ich noch keinen Ansatz habe, diese Auslöseaktionen einheitlich in das Spielobjekt zu bekommen (wie löse ich z.B. eine Zustandsänderung über den Timer aus? Via Spielaktion gehts ja direkt: "Du bist nun zerstört!" Beim Timer dachte ich an so ne Art Listener, dann hat aber jedes Spielobjekt so einen Listener und braucht ihn aber z.B. nur am Anfang für die Aufbauanimation, danach können nur noch direkte Änderungen erfolgen!)?!

    2. Die Grafikobjekte können prinzipiell in jedem Fenster abgebildet werden, man braucht dazu nur dessen RastPort (s. weiter oben). Wie bekomme ich den im Moment der Zustandsänderung gewünschten RastPort in das Grafikobjekt (das ja im Spielobjekt eingebettet ist)? Ich könnte das Ganze vereinfachen unter der Annahme, das ein einmal in einem RastPort dargestelltes Grafikobjekt seine Zustandsänderungen auch in diesem RastPort betreibt (was ja sinnvoll ist) und den RastPort dann bei der Erstellung der Grafikobjekte als Referenz mitgeben.

    Wie denkt ihr darüber?

    Ciao


Anmelden zum Antworten