Objektmanagement sinnvoll gestalten



  • Dann bin ich mal gespannt in welcher Weise ein Partikel so wie man (du) es mach(s)t anders ist als eine Komponente. Erzähl mal 🙂



  • Man implementiert eine Emitterklasse als Komponente. Diese Klasse hält dann Dinge wie maximal/minimale Lebenszeit, Schwerkraft, Textur, .. und natürlich einen Container mit Partikeln, wobei ein Partikel nur eine Struktur ist die Position, Geschwindigkeit, Farbe, Startzeit, Lebensdauer etc. hält.



  • OK. Einer von uns beiden hat das "component based entities" System falsch verstanden.

    Ich bin z.B. der Meinung dass es den Grundprinzipien widerspricht, dass jeder Emitter seinen eigenen Container von Partikeln hat.
    Wozu auch?, alles in einen einzigen globalen Container rein.

    Dann hast du also einen globalen Container, der Dinge (Structs, wie auch immer man es nennen will) eines ganz bestimmten Typs speichert. Über diese Dinge lässt man pro Frame eine ganz bestimmte "Transformation" laufen, tut sie dann alle auf die selbe Art und Weise rendern etc.

    Und wenn ich das Prinzip richtig verstanden habe, dann nennt man genau das "Komponente". Die Partikel kommen dabei ohne Entity aus, OK, macht ja nix. If it walks like a component and quacks like a component...



  • hustbaer schrieb:

    Ich bin z.B. der Meinung dass es den Grundprinzipien widerspricht, dass jeder Emitter seinen eigenen Container von Partikeln hat.

    Ja, in unserer natürlichen Umgebung würde man einfach jedes Teilchen dort rein packen. Aber man baut (normalerweise) ein Spiel - keine perfekte Simulation.

    hustbaer schrieb:

    Wozu auch?

    Performance ohne ernsthafte Einbußen an Funktionalität oder Übersicht. Viel Performance. Der "globale" Objektcontainer ist für so etwas doch gar nicht geschaffen. Die ganzen Speicheranforderungen und Freigaben an sich verschlucken doch schon zu viel. Nein, nein. Wenn man das separat handhabt kann man alles in einen Vektor mit fester Größe stecken und dann swapen etc. Jedenfalls kann man getrennt von dem ganzen anderen Kram experimentieren. Letztlich verhalten Partikel sich in einem Spiel halt auch nicht so, wie man es vielleicht erwartet. Man kann nicht gegen laufen, sie interagieren nicht, sind eigentlich gar nicht da. Nur Grafik. Daher muss auch kein anderes Objekt von ihnen wissen -> spart wieder Rechenleistung.



  • Ich glaube du verstehst unter Komponente ganz was anderes als ich.
    Nochmal konkret die Frage: was hat deiner Meinung nach eine Komponente, was ein Partikel nicht hat (nicht haben muss)?

    EDIT
    Um das nochmal klarzustellen: ich habe nicht vorgeschlagen alle Komponenten überhaupt in einen einzigen Container zu packen. Sondern alle Partikel-Komponenten in einen einzigen Partikel-Komponenten-Container. Bzw. vermutlich sogar noch feiner: jeder Partikel-Typ bekommt einen eigenen Container - man wird ja u.U. verschiedene Partikel haben. Aber eben nur einen pro Typ, nicht einen pro Emitter.

    Wenn du 200 Rauchpartikel-Emitter in einer Szene hast, die alle die selben Rauchpartikel auswerfen... wo macht es dann bitte Sinn diese Rauchpartikel in 200 verschiedenen Containern zu speichern? Und wie soll das bitte zu besserer Performance führen? Das ist doch total widersinnig. Mal ganz davon abgesehen dass man den Emitter künstlich am Leben halten müsste, wenn er "eigentlich" schon weg wäre, nur damit seine Partikel nicht verschwinden.
    /EDIT



  • meint cooky451 nicht, das er für jeden partikel-effeckt den er im Spiel anwendet einen container hat? Klingt für mich zumindest durchaus logisch, da es ja eigentlich keinen sinn macht, alle PARTIKEL in einen container zu werfen.



  • hustbaer schrieb:

    Um das nochmal klarzustellen: ich habe nicht vorgeschlagen alle Komponenten überhaupt in einen einzigen Container zu packen. Sondern alle Partikel-Komponenten in einen einzigen Partikel-Komponenten-Container. Bzw. vermutlich sogar noch feiner: jeder Partikel-Typ bekommt einen eigenen Container - man wird ja u.U. verschiedene Partikel haben. Aber eben nur einen pro Typ, nicht einen pro Emitter.

    Hm.. ja, das ist was anderes. Ich dachte du wolltest alles in den globalen Container stecken. Für jeden Typ einen Container anlegen und die Emitter nur als Positionen nutzen ginge natürlich auch, dann sehen halt nur alle Fackeln genau gleich aus. Aber es wäre wesentlich performanter, das stimmt. Nur würde ich ein Partikel niemals selbst als Entity erstellen, das ist doch viel zu teuer. (Und wie wir uns ja einig sind, sind Partikel auch keine "echten" Objekte, die irgendwie nach Außen kommuniziert werden müssten.)



  • Wieso sollten dann alle Fackeln gleich aussehen?


  • Mod

    hustbaer schrieb:

    rapso schrieb:

    schau dir mal component based entities an, so sollte man das heute eher machen.

    Ich hab mir dazu ein paar Dinge im Inet durchgelesen (u.A. auch deine Links). Was mir dabei nicht ganz klar ist (weil ich nirgends ein Beispiel dazu gefunden habe - die Beschreibungen drücken sich da alle recht elegant drumrum)... wie lässt man Components interagieren?
    Also das was in deinem Link kurz unter "Closing Thoughts" (letzte Seite) angeschnitten wird.

    das haengt stark davon ab, welche daten und wie oft man uebermitteln moechte. oft wird ein event/message/signal system benutzt, wenn z.b. der player stirbt, wird ein event dafuer getriggert, alle elemente die sich fuer so ein event regestriert haben, bekommen das mit. im einfachsten fall gibt es callback von libsig.

    pointer sharen oder desgleichen ist grundsaetzlich nicht der richtige weg, da theoretisch alle systeme unabhaengig laufen koennen, dabei muss es nicht multithreaded sein, es reicht auch dass z.b. in einem RTS die logik fuer die AI componenten time-sliced ist und die updated dann z.b. mit 0.1Hz . bis da jemand mitbekommen wuerde dass ein pointer invalid ist, koennte dieser schonwieder mit was ganz anderem belegt sein.

    in einem hypothetisch perfekten system, wuerde jedes entity nur eine ID sein (perfecterweise variable bit length), beim erstellen wuerde es eine object description geben (ich hatte das mal mit xml gebastelt), und dann wird durch alle system die regestriert sind durchgegangen und die jeweiligen descriptoren, fall vorhanden, wuerden in jedem system ihre components, fuer diese entity ID erstellen.
    so kannst du wirklich extrem flexibel arbeiten, du kannst z.b. ein system garnicht erst einklinken (z.b. sound-dll loeschen), du kannst zur laufzeit die components eine systems serialisieren (gibt ja keinen pointer drauf ausser im system), das system runterfahren, durch ein anderes ersetzen z.b. ogl->d3d und deserialisieren, das laeuft dann weiter als waere nichts.
    du kannst so auch sehr einfach netzwerk kommunikation machen (ich glaube UE3 network code arbeitet zum teil so), du schickst nur events -> die eventqueue repliziert die daten ueber netzwerk (bzw das netzwerk modul ist listener fuer alle events).

    so perfekt geht das nicht wie man an eurem particle beispiel sieht.
    ich wurde schon an code drangesetzt, der so war. ein container wo alle particle sind. entsprechend wurde ich wegen performanceproblemen drangestzt.
    - zum einen wurde auf cpu seite pro particle eine virtual function aufgerufen die fuer jeden particle type anders implementiert war, sehr flexibel und ideologisch gut, aber ein vtable lookup und indirekter function call sind schonmal 20mal mehr als die zumeist darin enthaltenen Pos+=Dir;
    - auf gpu seite ist es natuerlich visuel auch perfekt, alle particle sind nach entfernung sortiert (kommt natuerlich ein grosser quicksort ueber den ganzen container pro frame hinzu, was anhand der coherenten daten sogar langsammer als ein insert-sort war), pro particle wird die textur, der shader, eine lightquelle und der blendmode gesetzt. selbst mit caching brach das nach spaetestens 3000 particlen total ein (ohne was anderes zu rendern).

    -loesung 1. ist dann erstmal ein container pro particle system (mit emittern die dorthin spawnen)
    cpu seite ist ein grosser switch oder ein vtable call, der sonst pro particle gemacht wurde. war locker 10x schneller in real world. gpu seite hat ein drawcall pro particle system, auch schoen nur ein budget/cap pro system, nachteil ist, dass die ganze welt simuliert wird, was u.U. 99% verschwendung ist (fakeln im ganzen quake level obwohl man nur die in einem raum sieht).
    -loesung 2. ist dann pro emitter ein container
    cpu seite ist noch relativ fix bei der simulation, culling anhand von AABBs fuers zeichnen und simulieren ist dann einfach, auch collision detection ist relativ gut, da man pro container die paar objekte picken kann die vermutlich kollidieren koennen. gpu seite ist natuerlich auch recht fix und man hat noch die moeglichkeit die emitter zu sortieren.

    nach der definition von insomniac waere loesung 1 ja noch ein entity system fuer particle. man hat input-transformatin-output. pro system.
    particle sind eigentlich die die perfection von component systemen, da man darauf nicht nur an sich transformationen durchfuehrt, sondern ungleich den sonstigen implementierungen wo pro system daten<->logic abhaengigkeit ist, bei particlen einfach nur auf dieselben daten transformationen durchgefuerht werden. z.b. kann man gravitation als ein durchgang machen, collision+reflection als ein pass, sogar incrementeles bubblesort als pass.

    weil weitere nette links erfragt wurden http://macton.smugmug.com/gallery/8936708_T6zQX#593426709_ZX4pZ
    :xmas1:



  • hustbaer schrieb:

    Wieso sollten dann alle Fackeln gleich aussehen?

    Wenn man für die Fackeln nur ein "Fackel-Feuer" hat, und die Punkte für alle Fackeln in einem Container berechnet werden, sodass man sie nachher rumschieben kann, sehen alle Fackeln genau gleich aus.



  • Ich hätte noch eine Frage zu dem Componenten Basierten Design:

    Könnte man nicht auch die GUI somit sinnvoll Realisieren? So das man eine Komponente namens "Interactive" die Ereignisse (Click,Mousover..) realisiert erstellt.

    Und dann je nach GUI Element einen ButtonRenderer oder einen InputFieldRenderer einer GUI Entität zuweist.

    Somit könnte man die Komponente "Interactive" einen 3D Objekt zuweisen und damit auch mit dem Objekten interagieren. Man müßte nur bei der Ereignesserkennung unterschiedlichen Code verwenden.

    Ist dies sinnvoll?

    Mit freundlichen Grüßen

    Cyphron



  • Nein, GUI würde ich durch Vererbung bauen. GUI einfach keine Vielzahl an Funktionsweisen und kaum Kombinationsmöglichkeiten.



  • Übrigens fällt mir noch eine Sache zum Thema Partikelsystem ein.

    Und zwar wenn die Entität mehr als nur einen Emitter benötigt.
    Bei einen Düsenjäger mit 2 Triebwerken könnte man ja einfach die Flammen 2 mal versetzt Rendern (und evtl. auch Skaliert).

    Aber bei unterschiedlichem von selben Typ aber anderen Aussehen (Farbe,Lebenszeit,Dichte...)?
    Bei cooky451´s Lösung ist ja eine Komponente innerhalb einer Entität unique.

    Auf die schnelle würde mir eine Komponente "EmitterList" einfallen die eine Liste von Emittern enthält.

    Wie lößt ihr dies?



  • Cyphron schrieb:

    Wie lößt ihr dies?

    std::multimap. Wollte ich eh nutzen, die Beschränkung auf eine Komponente pro Typ birgt noch mehr Probleme.

    Aber ich wäre ja eh mal an einer Beispielimplementierung von rapso interessiert, er scheint das ja noch etwas anders gemacht zu haben. Vielleicht lässt er sich ja hinreißen. 🙂



  • Leider habe ich noch Probleme beim umgang mit Smartpointern. Z.B. habe hab ich Probleme einen unique_ptr meiner map hinzuzufügen bzw. den Rawpointer von diesen zu beziehen.

    Da man unique Pointer so wie ich verstanden habe nicht kopieren kann habe ich es mit std::move() probiert, jedoch stelle ich mich wohl zu dumm an dafür.

    Um an den Rawpointer heran zu kommen hab ich es mit get() versucht. Ebenfalls erhalte ich hier nur Fehler 😞

    Könntest du mir zeigen die die die beiden Funktionen getConponent und addComponent implementiert hast?



  • Mit einer multimap wäre es vielleicht sogar sinniger eine Range zurückzugeben, hm.. muss man sich mal was für überlegen. Aber hier die Implementierung, vielleicht hilft es dir ja weiter:

    void Entity::addComponent(std::unique_ptr<Component> component)
    {
      components_.insert(std::make_pair(component->type(), std::move(component)));
    }
    
    Component *Entity::getComponent(ComponentType type) const
    {
      auto found = components_.find(type);
      if (found != std::end(components_))
        return found->second.get();
      return 0;
    }
    


  • Vielen dank erstmal.

    getComponent funktioniert(mit map.end()) jedoch nicht addComponent.

    Fehlermeldung:

    c:\mingw\bin\..\lib\gcc\mingw32\4.5.2\include\c++\bits\unique_ptr.h|207|error:
    deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::unique_ptr(const 
    std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = Component, _Tp_Deleter = 
    std::default_delete<Component>, std::unique_ptr<_Tp, _Tp_Deleter> = 
    std::unique_ptr<Component>]'|
    

    <<dies ist die selbe Fehlermeldung die mich schon die ganze Zeit plagt 😞



  • Wie rufst du die Funktion denn auf?



  • so wie du:

    void Entity::addComponent(unique_ptr<Component> p_Component)
    {
        m_Components.insert(std::make_pair(p_Component->getType(),std::move(p_Component));
    }
    

    hier meine m_Components definition:

    map<string,unique_ptr<Component> >  m_Components;
    


  • Du hast aus Type einen String gemacht? Na ja, wie auch immer, ich meinte eigentlich, wie du die add Funktion aufrufst.


Anmelden zum Antworten