|
|
||
|---|---|---|
| src | ||
| .gitignore | ||
| README.md | ||
| pom.xml | ||
README.md
Entity Component System
Nachdem das von ihnen entwickelte Spiel Racewars veröffentlicht wurde, bekommen sie einige beschwerden über schlechte Performance. Obwohl sie der Meinung sind, dass sich die Leute einfach einen neuen PC kaufen sollen, will ihr Arbeitgeber, dass sie das Spiel optimieren.
Nach einiger Recherche stellen sie fest, dass die derzeitige Implementierung der Wesen Klasse zu schlechter Performance führt.
Ihre Wesen sehen im Arbeitsspeicher etwa so aus:
┌─────────────────────────────────────────────┐
│ Wesen Objekt │
├─────────────────────────────────────────────┤
│ │
│ vtable pointer (8 bytes) │
│ │
├─────────────────────────────────────────────┤
│ │
│ geschwindigkeit : int (4 bytes) │
│ │
├─────────────────────────────────────────────┤
│ │
│ schaden : int (4 bytes) │
│ │
├─────────────────────────────────────────────┤
│ │
│ ruestung : int (4 bytes) │
│ │
├─────────────────────────────────────────────┤
│ │
│ lebenspunkte : double (8 bytes) │
│ │
└─────────────────────────────────────────────┘
Prozessoren laden Daten aus dem Arbeitsspeicher allerdings meist in Blöcken von 64 Byte. Werden nun zum Beipiel bei der Ausgabe der Einheiten nur die Lebenspunkte von jeder Einheit gebraucht, muss der CPU das gesamte Objekt aus dem Arbeitsspeicher laden, obwohl ein Großteil nicht gebraucht wird.
In vielen modernen Computerspielen wird diese ineffizienz umgangen, indem Spielobjekte nicht als einzelne Java-Objekte, sondern als eine Ansammlung von Komponenten gespeichert werden:
┌───────────────────────────────────────────────────────────────────────────┐
│ Entity Component System (ECS) Layout │
├───────────────────────────────────────────────────────────────────────────┤
│ │
│ Entity Table │
│ ┌────────────┬────────────┬────────────┬────────────┐ │
│ │ Entity 1 │ Entity 2 │ Entity 3 │ Entity 4 │ (int[], 4 bytes) │
│ └────────────┴────────────┴────────────┴────────────┘ │
│ │
├───────────────────────────────────────────────────────────────────────────┤
│ │
│ Geschwindigkeit Component │
│ ┌────────────┬────────────┬────────────┬────────────┐ │
│ │ 25 │ 40 │ 30 │ 35 │ (int[], 4 bytes) │
│ └────────────┴────────────┴────────────┴────────────┘ │
│ │
├───────────────────────────────────────────────────────────────────────────┤
│ │
│ Schaden Component │
│ ┌────────────┬────────────┬────────────┬────────────┐ │
│ │ 10 │ 15 │ 12 │ 18 │ (int[], 4 bytes) │
│ └────────────┴────────────┴────────────┴────────────┘ │
│ │
├───────────────────────────────────────────────────────────────────────────┤
│ │
│ Ruestung Component │
│ ┌────────────┬────────────┬────────────┬────────────┐ │
│ │ 5 │ 8 │ 6 │ 3 │ (int[], 4 bytes) │
│ └────────────┴────────────┴────────────┴────────────┘ │
│ │
├───────────────────────────────────────────────────────────────────────────┤
│ │
│ Lebenspunkte Component (Structure of Arrays): │
│ ┌────────────┬────────────┬────────────┬────────────┐ │
│ │ 100.0 │ 85.5 │ 92.3 │ 78.9 │ (double[], 8 bytes)│
│ └────────────┴────────────┴────────────┴────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────────┘
Werden jetzt wie im vorherigen Beispiel die Lebenspunkte aller Wesen gebraucht, liegen diese im Arbeitsspeicher nebeneinander.
Ein Entity-Component-System besteht aus vier Konzepten:
Entities
Entities sind Spielobjekte, sie sind meist nicht mehr als eine fortlaufende ID.
Components
Components sind die zum Spielobjekt gehörenden Daten. Wie viele Daten jede Art von Component enthält kann verschieden sein.
Zum Beispiel kann ein Lebenspunkte Component nur die derzeitigen Lebenspunkte enthalten, während ein Kampfkraft Component Schaden, Geschwindigkeit und Rüstung enthält.
Registry
Die Registry ist eine Datenbank, die alle Entities ihren Components zuweist und folgende Operationen Erlaubt:
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ registerComponentType(componentClass) │
│ └─ Registriert eine neue Art von Component zur Verwaltung │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ addComponent(entity, component) │
│ └─ Fügt der Entity einen neuen Component hinzu │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ removeComponent(entity, componentType) │
│ └─ Löscht den angegebenen Component von der Entity │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ hasComponent(entity, componentType) → boolean │
│ └─ Prüft, ob die Entity einen Component diesen Types hat │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ getComponent(entity, componentType) → component │
│ └─ Gibt den angegebenen Component für die Entity zurück │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ withComponents(componentType...) → entity[] │
│ └─ Gibt alle Entities zurück, die alle angegebenen Components haben │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ removeEntity(entity) │
│ └─ Löscht die Entity mit allen ihren Components │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Sytems
Systems bilden die Anwendungslogik ab. Sie können die Registry nach Entities abfragen und neue Entities oder Components hinzufügen