Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!
Werbeanzeige
Ich glaube C# ist nicht wirklich gut geeignet, um DOD-Architekturen zu implementiere, da ist C++ und andere etwas mehr low-level Sprachen besser. Man hat halt keine Kontrolle, wie man die Daten übergibt. Structs werden immer kopiert und Klassen immer referenziert. Ich finde es auch schlecht, wenn die Entities immer kopiert werden, vor allem, wenn man sie dann wirklich referenzieren will muss man immer ref benutzen, bäh.
Mein Tipp ist eher: Wie lange dauert es denn, deine Entities zu speichern? Für kleinere bis mittlerle Spiele sollte Cachefreundlichkeit nicht wirklich eine Rolle spielen denke ich.
Um vielleicht auch deine Frage mit dem "wie"-Speichern: Meine aktuelle Implementierung in einem Spiel funktioniert so, dass ich json benutze. Jedes Entity hat ein Array an Komponenten. Jede Komponente hat eine String-Id und eine Liste von Key-Value pairs (string, string), um seine Daten zu serialisieren. Das Laden der Komponente kann eine Factory machen und das klappt hervorragend. Ich lade mehrere tausend Entities innerhalb von einer halben Sekunde.
C#-Quelltext |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
[Flags] enum ComponentSign { HealthComponent = 0, EnergyComponent = 1 << 0, PositionComponent = 1 << 1, // ... } enum ComponentId { HealthComponent = 0, EnergyComponent, PositionComponent, // ... } interface IComponent { // marker } class ComponentManager { List<List<IComponent>> comps = new List<List<IComponent>>(); List<IComponent> pos = new List<Position>(); List<IComponent> health = new List<Health>(); List<IComponent> energy = new List<Energy>(); // ... public void Init() { comps.Insert(pos, ComponentId.PositionComponent); comps.Insert(health, ComponentId.HealthComponent); comps.Insert(energy, ComponentId.EnergyComponent); // ... } public void AddComponent(uint entityID, ComponentId cid, IComponent c) { var desiredCompList = comps[cid]; // get the desired component list desiredCompList.Insert(entityID, c); // insert component based on entity id to desired component list // ... } public T GetComponent<T>(uint entityID, ComponentId cid) where T : IComponent { // ... return comps[cid][entityID] as T; } // ... } |
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »TrommlBomml« (10.07.2016, 21:11)
Frage ist, wie viel besser und hast du schlimme FPS-Drops/Ruckler? Ich würde erstmal tippen, dass es dann doch eher am regelmäßigen Remove/Insert liegt. Da werden ja intern Arrays benutzt, und dann müssen da ja immer die elemente kopiert werden. Vielleicht solltest du dir für diese Fälle eine einfache, eigene List-Klasse bauen, die intern ein Array hält. Wenn jetzt ein Element entfernt wird, wird es einfach mit dem letzten aktiven getauscht und das gelöschte genullt oder nur als deleted markiert und gehalten wird.
EDIT: Ich bin mir nicht sicher, ob du das mit den Cache misses richtig verstanden hast und was das im Spielekontext bedeutet: Cache misses hast du immer, irgendwann muss die CPU halt aus dem RAM nachladen. Allerdings bei cachefreundlichen Strukturen möchtest du für eine große Anzahl an Daten, die du in kurzer Zeit lesen möchtest, möglichst in nacheinander liegenden Speicher haben. Dazu spielt auch noch die Größe eines Elements eine Rolle. Wenn du pro Objekt sehr viele Daten hast, passen deutlich weniger Objekte in den Cache bei selber Objektanzahl, und das ist ja glaube auch noch CPU-Abhängig. Damit ist an einigen Stellen der Nutzen für DoD meiner Meinung nach gar nicht gerechtfertigt.
Zitat
Hickups habe ich keine, außer ich lege viele neue Components an. Das liegt wahrscheinlich an dem Kopieren, wie du schon beschrieben hast. Eine eigene Liste anzulegen, bzw. die Components zu reusen ist eine gute Idee. Die Umsetzung ist nur ein wenig schwierig, da ich an dieser Stelle nicht iterieren möchte.
C#-Quelltext |
|
1 |
Absolut richtig, so habe ich es in der Vorlesung auch aufgeschnappt. Meine Components halten nicht viele Daten, manche sind nur als "Marker" gedacht, ich vermute mal, dass sie dementsprechend nicht viel Speicher schlucken. |
Jetzt ist die Frage, was bei dir die meiste Zeit gemacht wird und was schnell sein soll: Iterieren oder einfügen/löschen. Beides geht nicht schnell, du musst dich entscheiden. Normalerweise geht man davon aus, dass einfügen/löschen eher selten ist und die meiste Zeit iteriert wird, dann würde ich mit dem Overhead leben und lieber das wegschmeissen von Speicher reduzieren. Beim einfügen kannst du viel Zeit sparen, wenn du vor dem Einfügen schonmal eine List mit initialCapacity anlegst, das macht einen deutlichen Unterschied!
Musst du ausgeben und dann mal rechnen. Ich hatte zu dem Thema gestern einen sehr spannenden Vortrag von einem Engine-Entwickler genau zu dem Thema: Data Oriented Design C++. Ist zwar C++, aber er hat ein paar sehr interessante Punkte zu dem Thema, die sich auch auf C# Anwenden lassen.
Werbeanzeige