Vielen Dank für die Antworten!
Chromanoid hat geschrieben:Du könntest pro Entity ein Bitset speichern, das angibt, welche Komponenten ein Entity besitzt. Du könntest dann eine zweidimensionale Struktur pflegen, die "Component-Type-ID" und "Component-Data-Index" verknüpft. Dann hast Du nur in dem Fall O(N), wenn das Entity die Komponente auch wirklich benutzt.
An so etwas ähnliches hatte ich auch schon gedacht, aber wenn ich das richtig verstanden habe, wie Du das meinst, muss das Bitset mindestens so viele Bits haben, wie es Komponenten gibt. Bei aktuell 33 Komponenten also eine 64 Bit Variable. Ich fürchte aber, dass ich über 64 Komponenten kommen werde, was die Sache dann verkompliziert. Ich hatte mir da überlegt, dass ich nur den 32 meist genutzten Komponenten so ein Such-Flag gebe und bei allen anderen Komponenten muss ich immer die Liste durchgehen.
Chromanoid hat geschrieben:Component-Data-Index deswegen, weil Du vielleicht besser diesen Ansatz verfolgen willst, um die Daten der Komponenten pro Entity zu pflegen (Pointer geht natürlich auch, aber vielleicht will man die Indirektion absichtlich haben):
Wikipedia hat geschrieben:In other ECS architectures, every component type are stored in separate lists, so whatever systems operate on those type of components are only iterating over objects they care about by default. In this common ECS architecture, the described disadvantage actually becomes a major performance advantage, by more efficiently leveraging the CPU instruction and data caches.
Die Verarbeitung der Objekte/Entities erfolgt über spezielle Strukturen. Solche Komponenten, wie z.B. für die KI, werden gesondert bei einem "Verarbeiter" registriert und dort behandelt. Im Prinzip hat der "Verarbeiter" auch nur eine Liste aller aktiven KI und geht diese pro Zyklus durch.
Chromanoid hat geschrieben:Ich weiß nicht, wie das bei CPP ist, aber wenn Du wirklich ein dynamisches ECS-Framework entwickeln willst, solltest Du auf Enums und so verzichten und stattdessen eine Komponenten-Registry einführen, die "Component-Typ-IDs" dynamisch vergibt. Ansonsten musst Du an zentraler Stelle Code ändern, um neue Komponenten hinzuzufügen.
So eine Dynamik wäre natürlich schön, aber das bringt auch andere Probleme mit sich. In meiner aktuellen Variante benötigt jede Komponente eine statische/static Funktion, die eine eindeutige Nummer zur Identifizierung der Komponentenart zurückgibt. Diese Nummer/Id muss ich aktuell leider zentral per Enum pflegen. Ohne diese satische Funktion, kann ich folgendes nicht mehr machen, bzw. weiß nicht wie:
Code: Alles auswählen
ClassComponentContainer* pContainer = pComponentList->Find();
ClassComponentLock* pLock = pComponentList->Find();
Ich muss darüber mehr nachdenken, wobei ich dieses eine zentrale Enum nicht so schlimm finde. Das Projekt muss ich so oder so oft komplett neu kompilieren, mehr Zeit für Youtube-Videos! ;-)
Chromanoid hat geschrieben:Edit: Ansonsten wäre ich übrigens wie Helmut für eine Hash-Map pro Entity. Das mag zwar mehr Speicher fressen, ist aber sehr flexibel und schön performant.
Ich habe das mal getestet anhand von einer Liste mit 5 Einträgen.
Listen-Suche: 312 ms
std::unordered_map: 734 ms
Bei std::hash_map meckert mein Compiler und verweist auf die std::unordered_map. Das Ergebnis überrascht mich jetzt aber, ich habe nur die Suche getestet, nicht das Hinzufügen. Muss ich bei der std::unordered_map noch was anderes machen? Die Elemente habe ich einfach per operator [] hinzugefügt und dann in der Suche find(X).
Code: Alles auswählen
std::unordered_map<keytype, valuetype> um;
um[1] = pC1;
um[2] = pC2;
...
pC1 = um.find(1);
STL zu debuggen ist immer die absolute Krätze! Für jeden Furz werden 1000 Codezeilen ausgeführt... Template-Hölle :?
Chromanoid hat geschrieben:Noch ein edit: Eine Matrix aus allen Komponenten und allen Entities ist vielleicht auch nicht schlecht. So teuer ist das auch nicht, insbesondere wenn Du pro Zelle nur einen 24bit-Index speicherst, der auf die eigentliche Datenhaltung in den Komponenten verweist.
Ich verstehe nicht genau, wie Du das meinst, bzw. fallen mir mehrere Konzepte ein, die zu deiner Matrix passen würden. Kannst Du das bitte genauer erläutern, bzw. ein Link, wo das genauer erklärt wird?
MasterQ32 hat geschrieben:Aua, dachte nicht, dass dynamic_cast SO lahm ist.
Finde ich auch verdächtig komisch. Ich habe noch ein wenig rum experimentiert, kann die Zeit per dynamic_cast aber nicht reduzieren. Ich dachte, dass dies über die VTables geregelt wird, also sollte das eigentlich nicht so langsam sein.
MasterQ32 hat geschrieben:Compiler-Optimierung ist auch sicher angeschaltet?
Ich habe in VS 2015 ein komplett neues Projekt erstellt (Win32-Konsolenanwendung) und die Zeit im Release-Modus gemessen.
Selbst wenn ich voll auf Codegeschwindigkeit optimieren lasse, wird es nicht schneller.
Vielleicht kann einer aufgrund folgender Assembler-Auszüge erkennen, warum die dynamic_cast-Version so lahm ist.
Listen-Suche:
Code: Alles auswählen
ClassComponentArmor* pStats = FindComponent();
00281306 xor edx,edx
00281308 nop dword ptr [eax+eax]
00281310 mov eax,dword ptr [ecx+edx*4]
00281313 cmp dword ptr [eax+4],3
00281317 je ClassComponentArray::TestDec+28h (0281328h)
00281319 inc edx
0028131A cmp edx,5
0028131D jl ClassComponentArray::TestDec+10h (0281310h)
0028131F mov dword ptr [ebp-4],0
00281326 jmp ClassComponentArray::TestDec+2Bh (028132Bh)
00281328 mov dword ptr [ebp-4],eax
dynamic_cast-Suche:
Code: Alles auswählen
ClassComponentArmor* pStats = FindComponent();
00BE13BB mov esi,dword ptr [ebx+14h]
00BE13BE mov edi,dword ptr [ebx+18h]
00BE13C1 cmp esi,edi
00BE13C3 je ClassComponentArray::TestDec+46h (0BE13F6h)
00BE13C5 nop word ptr [eax+eax]
00BE13D0 push 0
00BE13D2 push offset ClassComponentArmor `RTTI Type Descriptor' (0BE617Ch)
00BE13D7 push offset ClassComponent `RTTI Type Descriptor' (0BE61A0h)
00BE13DC push 0
00BE13DE push dword ptr [esi]
00BE13E0 call ___RTDynamicCast (0BE3094h)
00BE13E5 add esp,14h
00BE13E8 mov dword ptr [pStats],eax
00BE13EB test eax,eax
00BE13ED jne ClassComponentArray::TestDec+4Dh (0BE13FDh)
00BE13EF add esi,4
00BE13F2 cmp esi,edi
00BE13F4 jne ClassComponentArray::TestDec+20h (0BE13D0h)
00BE13F6 mov dword ptr [pStats],0
EDIT: Hier der Quellcode vom Testprogramm: