In der Entwicklung moderner Spiele spielt die Verwaltung von Kollisionen zwischen zahlreichen Entitäten eine entscheidende Rolle. Insbesondere für Projekte, die auf dem Entity-Component-System (ECS) basieren, ergeben sich hier spezifische Herausforderungen und Chancen. Im vierten Teil der ECS Survivors Blogserie beschäftigt sich der Autor intensiv mit dem Thema Kollisionssysteme und stellt verschiedene Ansätze vor, die speziell im Kontext von "purem" ECS implementiert wurden. Dieser Beitrag fasst die wichtigsten Erkenntnisse und Umsetzungen zusammen, um Entwicklern einen praxisnahen Leitfaden für effiziente Kollisionsverarbeitung zu bieten. Der Startpunkt des Projekts liegt in der Generierung vieler Kollider im Spiel, denn ohne den Aufbau einer ausreichenden Anzahl kollidierender Einheiten macht die nachfolgende Kollisionslogik wenig Sinn.
Dafür wurde ein spezielles System in einem neuen Gameplay-Modul eingerichtet, das regelmäßig Gegner an den Bildschirmrändern spawnt. Dieses System nutzt einen sogenannten Spawner-Komponentenansatz, bei dem festgelegt wird, welcher Prefab-Typ generiert werden soll. Die Platzierung der Gegner erfolgt abwechselnd auf der X- oder Y-Achse des Bildschirms, um so eine semi-zufällige, aber durchdachte Verteilung sicherzustellen. Diese Methode gewährleistet eine dynamische und herausfordernde Spielumgebung mit hunderten Kollisionsträgern. Neben der bloßen Positionierung der Entitäten kommt der visuelle Aspekt nicht zu kurz.
Die Implementierung von Texturen anstelle simpler farbiger Kreise sorgt nicht nur für ein ästhetisch ansprechenderes Setting, sondern profitiert auch von Performance-Optimierungen. Dies liegt vor allem daran, dass Raylib Texturen effizienter in Batches rendert, wodurch die CPU-GPU-Kommunikation deutlich entlastet wird. Dieser Schritt verdeutlicht einen wichtigen Grundsatz im Game-Development: Optische Qualität und technische Effizienz müssen sich nicht ausschließen, wenn man die richtigen Mittel einsetzt. Sobald die Grundlagen der Entitäten und ihrer visuellen Darstellung gelegt sind, richtet sich der Fokus auf den zentralen Aspekt des Projekts: die Kollisionsverarbeitung. Der Prozess wird dabei in zwei Hauptphasen unterteilt.
Zunächst erfolgt die Kollisionserkennung, in der überprüft wird, welche Entitäten sich berühren oder überschneiden. Im zweiten Schritt folgt die Kollisionsauflösung, bei der diese Überlappungen korrigiert werden, um unnatürliche Zustände wie Durchdringungen zu vermeiden. Da ausschließlich Kreise als Kollisionskörper zum Einsatz kommen, gestaltet sich die Kollisionsdetektion vergleichsweise unkompliziert. Die Distanz zwischen zwei Entitäten wird mit der Summe ihrer Radien verglichen. Übersteigt der Abstand nicht diese Summe, liegt eine Kollision vor.
Zusätzlich sorgen Filtermechanismen mittels Bitoperationen dafür, dass nur relevante Kollisionen behandelt werden – beispielsweise werden Flug- und Bodeneinheiten voneinander getrennt, sodass keine unnötigen Checks oder Reaktionen stattfinden. Die eigentliche Auflösung einer Kollision basiert auf der Berechnung des Überlappungsvektors zwischen den kollidierenden Einheiten. Beide Entitäten werden um die Hälfte der Überlappung voneinander weggeschoben. Dieses Prinzip stellt sicher, dass sich keine Einheit einseitig bewegt und erzeugt insgesamt natürlichere Bewegungen. Auch die Anpassung der Geschwindigkeiten erfolgt entsprechend, um das physikalische Verhalten im Spiel glaubwürdig abzubilden.
Im Kern des Blogbeitrags stehen drei verschiedene Kollisionsmethoden, die alle vollständig mit „purem“ ECS umgesetzt wurden und eine theoretische Komplexität von O(n²) besitzen, wie sie bei der vollständigen Paarweisen Überprüfung unvermeidlich ist. Die erste Methode nutzt ECS-Beziehungen, um festgestellte Kollisionen direkt über sogenannte Relationship-Komponenten abzubilden. Obwohl der Ansatz intuitiv klingt, zeigt sich hier ein signifikantes Performanceproblem. Die Fragmentierung der sogenannten Archetypen führt dazu, dass sich viele kleine Tabellen bilden, die nur wenige Entitäten enthalten. Diese starke Fragmentierung führt zu einem hohen Overhead bei der Abfrage-Performance und verlangsamt das System erheblich, besonders bei einer großen Anzahl an Kollisionen.
Als zweite Alternative wurde ein Ansatz getestet, bei dem für jede Kollision eine neue Entität mitsamt einer CollisionRecord-Komponente erzeugt wird. Dies verbessert die Performance im Vergleich zur Relationship-Methode zwar um etwa den Faktor zwei, bleibt aber durch den anfallenden Overhead von schnellen Erzeugungen und Löschungen von Entitäten dennoch ineffizient. Die ständige Tabellenerstellung und anschließende Löschung in Flecs verursacht einen merklichen Performanceschluckauf, welcher bei Echtzeitspielen problematisch sein kann. Die dritte und zuletzt favorisierte Methode umgeht diese Probleme, indem alle Kollisionsdaten in einem zentralen Singelton gespeichert werden. Hierbei wird eine global zentrale CollisionRecordList geführt, in der alle Kollisionen im laufenden Frame vermerkt werden.
Dadurch erspart man sich jeglichen Overhead durch die Verwaltung zusätzlicher Entitäten oder fragmentierte Archetypen. Der Performance-Gewinn ist beeindruckend, da neben der besseren Cache-Nutzung auch der Verwaltungsaufwand der ECS-Datenstrukturen massiv sinkt. Laut Autor ist diese Lösung ausreichend performant für Projekte mit bis zu etwa 4800 gleichzeitig aktiven Kollisionskörpern – mehr braucht dann meist auch kein Spiel. Die Auswahl der Kollisionsmethode alleine reicht natürlich nicht aus. Wichtig ist neben der Erkennung auch die Reaktion der Entitäten auf Kollisionsereignisse.
Ein gängiges Beispiel ist die gegenseitige Schadenszufügung bei Zusammenstößen von Spieler und Gegnern. Interessanterweise wird hierfür trotzdem die Relationship-Methode verwendet, obwohl sie in Sachen Performance als problematisch gilt. Die Begründung liegt hier in der intuitiven und klaren Vorgehensweise, wie mit Kollisionsereignissen umgegangen wird. Die Beziehung „CollidedWith“ wird nur zwischen unterschiedlichen Kollisionsschichten gesetzt, um unnötige Behandlung von Kollisionen gleicher Typen zu vermeiden. So werden etwa Kollisionen zwischen Gegnern ignoriert, Kollisionen zwischen Spieler und Gegner hingegen lösen Schaden aus.
Die praktische Umsetzung folgt einem System, das für alle Entitäten mit Damage-Komponente und einer „CollidedWith“-Beziehung den Schaden an das Kollisionsziel weitergibt. Ein nachfolgendes System verarbeitet die TakeDamage-Komponente, reduziert die Lebenspunkte und löscht verletzte Entitäten bei Bedarf. Trotz der Leistungseinbußen entsteht so ein klar strukturierter und wartbarer Mechanismus für Spielereignisse basierend auf Kollisionen. Ein wesentliches Problem der Relationship-Methode wurde durch das Löschen von leeren Tabellen adressiert. Flecs bietet Mechanismen, um nicht mehr benötigte Tabellen automatisch zu entfernen, wodurch die Fragmentierung zumindest teilweise eingeschränkt wird.
Obwohl eine umfassendere Lösung in Aussicht steht, trägt dieser Workaround aktuell zur Laufzeitverbesserung bei und macht die Methode möglicherweise wieder attraktiver, insbesondere für Situationen mit niedrigeren Entitätenzahlen. Neben den erwähnten reinen ECS-Methoden existieren zwei weitere gängige Ansätze, die im Beitrag nur kurz gestreift werden. Spatial Hashing ist eine Technik, die den Spielraum in kleinere Bereiche unterteilt und so die Kollisionsprüfungen stark einschränkt. Statt jede Entität gegen jede andere zu prüfen, werden nur Entitäten innerhalb desselben Bereichs auf Kollision überprüft. Dies reduziert den Aufwand theoretisch auf etwa O(n/m), wobei m die Anzahl der Bereiche ist.
Die Komplexität sinkt damit deutlich und ist vor allem bei großen Welten mit vielen Objekten sehr hilfreich. Eine weitere Option ist die Integration externer physikalischer Bibliotheken wie Box2D. Box2D arbeitet mit einer dynamischen AABB-Baumstruktur, die es ermöglicht, Kollisionsabfragen in logarithmischer Zeit durchzuführen. Damit stellt Box2D im High-Performance-Bereich eine sehr effiziente Lösung dar und ist zudem für Entwickler mit viel Erfahrung in der Physiksimulation hervorragend geeignet. Die Integration in ECS-Systeme ist gut machbar und bietet einen ausgereiften Technologie-Stack für komplexe Spielphysik.
Im Gesamtbild entschied sich der Autor jedoch für den Kollisionslisten-Ansatz aufgrund des für ihn passenden Kompromisses zwischen Performance, Einfachheit und Implementierungskomplexität. Für das vorgesehene Spiel und dessen Anzahl an Entitäten reicht dieser Ansatz mehr als aus, um flüssige Abläufe und eine intuitive Entwicklungsumgebung sicherzustellen. Abschließend hat eine wichtige Änderung in der Rendering-Pipeline stattgefunden, die den Übergang von simplen Kreisen zu Texturen beinhaltet. Dieser Wechsel wird durch die Einführung einer Renderable-Komponente realisiert, die neben der Textur auch Skalierung, Drehung, Farbmodifikationen und Offsets beinhaltet. Daraus ergibt sich ein modularer und einheitlicher Rendering-Workflow, der flexibel und performant gleichzeitig ist.
Dies harmoniert ausgezeichnet mit der Kollisions- und Gameplay-Architektur und setzt den Grundstein für weiterführende Spielmechaniken. Zusammenfassend zeigt der Blogbeitrag, wie die Arbeit mit ECS nicht nur bei der Organisation der Spielobjekte sondern auch bei der effizienten Umsetzung von essenziellen Systemen wie Kollisionsverarbeitung von Vorteil sein kann. Die Wahl der Kollisionsmethode hängt stark von den Projektanforderungen ab, doch mit Wissen um die Vor- und Nachteile verschiedener Techniken lassen sich fundierte Entscheidungen treffen. Gerade der Fokus auf Performance, Speichermanagement und Wartbarkeit macht diesen Beitrag für Entwickler interessant, die komplexe Spiele mit hoher Entitätsdichte entwickeln wollen. Mit dem soliden Fundament aus effizienten Kollisionssystemen und einer ausgereiften Renderingarchitektur ist das Projekt ECS Survivors bestens vorbereitet, um nun in Richtung komplexer Gameplay-Mechaniken und dynamischer Weltgestaltung zu schreiten.
Die zukünftigen Updates versprechen spannende Erweiterungen wie Kamera-Folgesysteme, Angriffshandlungen und eine ansprechende Spielwelt – alles aufbauend auf einem stabilen und nachvollziehbaren ECS-Framework.