Organisation von Spielobjekten
-
Vielleicht wirfst Du einmal einen Blick in den Source von Doom3. JC ist auch nicht so der Anhänger modernster C++-Techniken und das vielleicht auch mit gutem Grund. Das trifft bestimmt Deinen Geschmack.
-
Ethon schrieb:
Wird irgendwo ein konkreter Ansatz erklärt?
Ansätze? Ja. Konkretere Implementierungen? Selten. Und ich selbst kenne leider auch noch keine bei der ich denken würde "ja, das ist es". Besser als Vererbung sind aber wohl die meisten. Zudem gibt es sehr, sehr viele Interpretationen der Grundidee.
Ethon schrieb:
Die Grundidee dass ein Entity erstmal komplett leer ist und echt jedes Feature (selbst Position etc) erstmal hineingestopft werden muss finde ich doof.
Das ist aber eben gerade das Tolle an der Idee. Wir sehen doch sogar bei dir schon, dass du anfängst ein Gottobjekt zu bauen. Ist jedes Objekt sichtbar? Nein. Hat jedes sichtbare Objekt eine Textur? Nein. Ein Sprite? Nein. Ist jedes Objekt mit Textur animiert? Nein. Und das wird, wie gesagt, immer schlimmer je mehr Features deine Objekte bekommen. Abgesehen davon: Für jedes Objekt die Textur im Speicher halten? Keine gute Idee würde ich sagen.
-
cooky451 schrieb:
Ist jedes Objekt sichtbar? Nein. Hat jedes sichtbare Objekt eine Textur? Nein. Ein Sprite? Nein. Ist jedes Objekt mit Textur animiert? Nein. Und das wird, wie gesagt, immer schlimmer je mehr Features deine Objekte bekommen. Abgesehen davon: Für jedes Objekt die Textur im Speicher halten? Keine gute Idee würde ich sagen.
Zusätzlich: Wieso sollte PhysicalObject von GameObject erben und wieso genau std::shared_ptr?
-
Das einzig schlimme an der hier gerade auftretenden Situation ist, dass man sich an solcherlei Fragen so lange aufreiben kann (auf verschiedensten Erfahrungsleveln), dass man in dieser Zeit auch mit Gottesobjekten etwas schönes hinbekommt, Erfahrung sammelt, die Anforderungen besser kennen lernt, usw. Man kann das Gefühl einfach nicht kurz in einem Forenpost zusammenfassen, das entsteht, wenn man eine kleine Änderung hier oder dort im Verhalten haben will und auf einmal das ganze Kartenhaus zusammenbricht oder das Projekt anfängt, auf der Stelle zu treten. Und wenn es für alles eine Patentlösung gäbe, die man aus dem Lehrbuch mal fix nach zehn Regeln runterfummelt, dann wären entsprechende Produke in der Lizenz auch nicht so teuer.
-
Und eine zweite Anmerkung:
Wenn jemand mit dem Ziel, ein bestimmtes Spiel umzusetzen, anfängt eine flexible Engine zu programmieren, so wird das Spiel in 99% der Fälle nie auch nur ansatzweise fertig. Das kann schlecht sein oder auch nicht, hauptsache es macht Spaß und man bereut die Zeit anschließend nicht.
-
Und noch eine dritte Bemerkung:
Wenn man irgendwann feststellt, dass man so ziemlich überall die Basisklassenfertigkeit überschreibt oder die Basisklassenfertigkeit mehrfach flexibilisiert, dann ist das ein gutes Indiz dafür, dass die Fertigkeit nicht in die Basisklasse gehörte.@dot: Ich lese im OP einen Pfeil von GameObject in Richtung PhysicalObject... Bedeutet das nach UML nicht, dass GameObject von PhysicalObject ableitet?
-
keine angst vor der editierfunktion
-
Decimad schrieb:
@dot: Ich lese im OP einen Pfeil von GameObject in Richtung PhysicalObject... Bedeutet das nach UML nicht, dass GameObject von PhysicalObject ableitet?
Ja, in UML, ich seh dort aber kein UML.
Aber gut, der Gedanke ist mir auch gekommen, für die Argumentation ist es aber irrelevant in welche Richtung die Ableitungsbeziehung genau läuft und die Variante schien mir rein intuitiv naheliegender.
-
dot schrieb:
cooky451 schrieb:
Ist jedes Objekt sichtbar? Nein. Hat jedes sichtbare Objekt eine Textur? Nein. Ein Sprite? Nein. Ist jedes Objekt mit Textur animiert? Nein. Und das wird, wie gesagt, immer schlimmer je mehr Features deine Objekte bekommen. Abgesehen davon: Für jedes Objekt die Textur im Speicher halten? Keine gute Idee würde ich sagen.
Zusätzlich: Wieso sollte PhysicalObject von GameObject erben und wieso genau std::shared_ptr?
Abgesehen davon: Für jedes Objekt die Textur im Speicher halten? Keine gute Idee würde ich sagen.
Ist doch ein klassischer Fall von shared Ownership. Eine Textur bzw ein Animator wird von mehreren Objekten geteilt. Ich verwalte zwar beide auch zentral, aber die Lebensdauer von Texturen/Animatoren an die Objekte zu binden erscheint mir als die richtige Lösung.
Und warum sollte PhysicalObject nicht von GameObject erben? Ein PhysicalObject ist immerhin auch ein GameObject, nur dass es parallel dazu noch in der "Physicsworld" lebt.
Decimad schrieb:
Das einzig schlimme an der hier gerade auftretenden Situation ist, dass man sich an solcherlei Fragen so lange aufreiben kann (auf verschiedensten Erfahrungsleveln), dass man in dieser Zeit auch mit Gottesobjekten etwas schönes hinbekommt, Erfahrung sammelt, die Anforderungen besser kennen lernt, usw. Man kann das Gefühl einfach nicht kurz in einem Forenpost zusammenfassen, das entsteht, wenn man eine kleine Änderung hier oder dort im Verhalten haben will und auf einmal das ganze Kartenhaus zusammenbricht oder das Projekt anfängt, auf der Stelle zu treten. Und wenn es für alles eine Patentlösung gäbe, die man aus dem Lehrbuch mal fix nach zehn Regeln runterfummelt, dann wären entsprechende Produke in der Lizenz auch nicht so teuer.
Ach, ich habe Zeit mir Gedanken zu machen weil der X-Server von Fedora17 nen Bug hat und der Patch noch nicht in den Repos ist ... und wegen dem Bug funktioniert SFML im Moment nicht. :p
Ansonsten gehe ich ja recht pragmatisch vor, nur das it den Objekten soll optimal umgesetzt sein.Vielleicht wirfst Du einmal einen Blick in den Source von Doom3. JC ist auch nicht so der Anhänger modernster C++-Techniken und das vielleicht auch mit gutem Grund. Das trifft bestimmt Deinen Geschmack.
Gute Idee, danke!
Das ist aber eben gerade das Tolle an der Idee. Wir sehen doch sogar bei dir schon, dass du anfängst ein Gottobjekt zu bauen. Ist jedes Objekt sichtbar? Nein. Hat jedes sichtbare Objekt eine Textur? Nein. Ein Sprite? Nein. Ist jedes Objekt mit Textur animiert?
Naja, meiner Meinung ist ein GameObject eigentlich immer etwas, das ich auch auf dem Bildschirm sehe. Von daher hat es immer Texture/Sprite. Den Animator dahin zu packen war eine Kurzschlussreaktion, aber ich fand/finde es nicht falsch die Teile, die mit der Darstellung des Objektes zu tun haben, auch zusammen zu haben.
-
Ethon schrieb:
dot schrieb:
cooky451 schrieb:
Ist jedes Objekt sichtbar? Nein. Hat jedes sichtbare Objekt eine Textur? Nein. Ein Sprite? Nein. Ist jedes Objekt mit Textur animiert? Nein. Und das wird, wie gesagt, immer schlimmer je mehr Features deine Objekte bekommen. Abgesehen davon: Für jedes Objekt die Textur im Speicher halten? Keine gute Idee würde ich sagen.
Zusätzlich: Wieso sollte PhysicalObject von GameObject erben und wieso genau std::shared_ptr?
Abgesehen davon: Für jedes Objekt die Textur im Speicher halten? Keine gute Idee würde ich sagen.
Ist doch ein klassischer Fall von shared Ownership. Eine Textur bzw ein Animator wird von mehreren Objekten geteilt. Ich verwalte zwar beide auch zentral, aber die Lebensdauer von Texturen/Animatoren an die Objekte zu binden erscheint mir als die richtige Lösung.
Mehrere Objekte verwenden die selbe Textur bzw. den selben Animator. Das wäre unter Umständen vielleicht eine notwendige, aber ganz sicher keine hinreichende Bedingung für shared Ownership.
Überleg dir nochmal, ob dir das tatsächlich als die richtige Lösung, oder nicht doch nur als die einfache Lösung erscheint...
Hint: Wieso genau sollte bei der Zerstörung eines GameObject auch dessen Textur oder Animator zerstört werden?
Ethon schrieb:
Und warum sollte PhysicalObject nicht von GameObject erben? Ein PhysicalObject ist immerhin auch ein GameObject, nur dass es parallel dazu noch in der "Physicsworld" lebt.
Ist jedes PhysicalObject wirklich auch ein GameObject? Wo genau kommt jetzt das AnimatedObject ins Spiel? Dein initiales Problem zeigt doch, dass deine Konzepte GameObject, PhysicalObject und AnimatedObject irgendwie im Widerspruch zueinander stehen...
Ethon schrieb:
Das ist aber eben gerade das Tolle an der Idee. Wir sehen doch sogar bei dir schon, dass du anfängst ein Gottobjekt zu bauen. Ist jedes Objekt sichtbar? Nein. Hat jedes sichtbare Objekt eine Textur? Nein. Ein Sprite? Nein. Ist jedes Objekt mit Textur animiert?
Naja, meiner Meinung ist ein GameObject eigentlich immer etwas, das ich auch auf dem Bildschirm sehe. Von daher hat es immer Texture/Sprite. Den Animator dahin zu packen war eine Kurzschlussreaktion, aber ich fand/finde es nicht falsch die Teile, die mit der Darstellung des Objektes zu tun haben, auch zusammen zu haben.
eigentlich immer oder immer?
Stell dir einfach folgende Frage: Ist es für das Konzept eines GameObject wesentlich, dass jedes GameObject immer aus einer Texture bzw. eine Sprite besteht? Wenn die Antwort nich klipp und klar "Ja" lautet, dann sind Texture bzw. Sprite dort fehl am Platz.
-
@Ethon Und schon bist du bei PhysicalObject und AnimatedObject, aber der gute Tom kann leider beides und tadaa, virtual inheritance.
-
dot schrieb:
Mehrere Objekte verwenden die selbe Textur bzw. den selben Animator. Das wäre unter Umständen vielleicht eine notwendige, aber ganz sicher keine hinreichende Bedingung für shared Ownership.
Überleg dir nochmal, ob dir das tatsächlich als die richtige Lösung, oder nicht doch nur als die einfache Lösung erscheint...
Hint: Wieso genau sollte bei der Zerstörung eines GameObject auch dessen Textur oder Animator zerstört werden?
Naja, da ich mir offen halten will etwas Programmiertes auch mal auf einem Smartphone oä. mit wenig RAM laufen zu lassen, versuche ich im vorhinein die Möglichkeit offen zu halten oft ungenutzte Resourcen freizugeben.
So wie es jetzt ist kann ich meinen ganzen Texturencache bzw einzelne Texturenklassen mit einer einzigen Zeile Code freigeben ohne dass es irgendwo dangling ponter gibt. Würde ich normale Referenzen auf das geladene Texturobjekt haben, müsste ich beim Löschen jeder Textur aus dem Cache eine Garantie geben, dass sie nirgendwo sonst verwendet wird. Und da nicht nur die Spielobjekte die Texturen verwenden ist das unglaublich umständlich und gewinnen würde ich dabei auch nichts. (shared_ptr ist in diesem Kontext ja praktisch kostenlos)Deswegen gefällt mir die Logik: Ist eine Textur nicht mehr im Cache und wird sie von keinem Objekt mehr verwendet => Ab in die Tonne damit.
....
Ansonsten bin ich durch diesen Blogpost ( http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/ ) mittlerweile auch der Meinung dass soetwas eine recht sinnvolle Sache ist. Vor allem gefällt mir die Idee dass so der Renderer sehr leicht austauschbar ist.
Jetzt heißt es brainstormen wie ich das einigermaßen performant und mit möglichst wenig dynamisch angefordertem Speicher hinbekomme.
-
Ethon schrieb:
Naja, da ich mir offen halten will etwas Programmiertes auch mal auf einem Smartphone oä. mit wenig RAM laufen zu lassen, versuche ich im vorhinein die Möglichkeit offen zu halten oft ungenutzte Resourcen freizugeben.
Wenn du das willst, machst du dir mit std::shard_ptr das Leben schwer, denn:
Ethon schrieb:
So wie es jetzt ist kann ich meinen ganzen Texturencache bzw einzelne Texturenklassen mit einer einzigen Zeile Code freigeben [...]
Nope, kannst du nicht. Deine Ressource wird erst freigegeben, wenn der letzte shared_ptr darauf zerstört wird...
Ethon schrieb:
[...] ohne dass es irgendwo dangling ponter gibt.
Wie genau funktioniert das? Wenn du deinen "Texturenchace" "freigibst", sind die Texturen immer noch alle da, du weißt lediglich nicht mehr wirklich, dass sie da sind. Wenn dann einer kommt und eine Textur, die eigentlich noch da wäre, anfordert, dann lädst du sie gleich noch ein zweites Mal. Man könnte drüber Diskutieren, ob da nicht gerade eine völlig neue Art zu Leaken entdeckt hast, shared_ptr macht's möglich...
Ethon schrieb:
Würde ich normale Referenzen auf das geladene Texturobjekt haben, müsste ich beim Löschen jeder Textur aus dem Cache eine Garantie geben, dass sie nirgendwo sonst verwendet wird.
Jap, oder mit anderen Worten: Du müsstest dafür sorgen, dass Texturen erst aus dem Cache verschwinden, wenn sie nirgendwo mehr verwendet werden. Mit shared_ptr dagegen, müsstest du genau das Gleiche tun...
Shared Ownership führt schnell zu einem extrem komplizierten, undurchsichtigen, unvorhersehbaren und nur schwer beherrschbaren System...
-
Okay, danke, ich verstehe. Ich denke ein Smartpointer, der mir verrät wie viele Referenzen existieren, wäre die Lösung? Dann könnte ich im Cache auf das Löschen verzichten falls der größer 1 ist ...
Hab mal ein bischen Grundlegend herumgespielt, hier mal heruntergebrochen wie ich das mit dem Entitysystem umsetzen würde ... okay, oder hab ich da etwas falsch verstanden?
struct Component { inline virtual std::type_index id() { return std::type_index(typeid(*this)); } }; struct Position : public Component { static std::type_index const ID = std::type_index(typeid(Position)); float x, y; }; Component* findComponent(std::type_index const& component) { auto result = components.find(component); if(result == components.end()) return nullptr; return result->second; } void MovementSystem::move() { for(Entity : entities) { Component* component = findComponentInEntity(entity, Position.ID); if(component) doStuff(static_cast<Position*>(component)); } }
-
Ich habe das bei mir so geregelt, dass der shared_ptr einen deleter bekommt, der die Ressource beim Manager austrägt und der Manager hat nur einen weak_ptr auf die Ressource. So bestimmen die Clients über die Lebenszeit, aber wenn die Ressource "lebt", kann der Manager drankommen.