Wenn Sie diese Frage zum ersten Mal beantworten, empfehle ich, zuerst den nachstehenden Teil vor dem Update und dann diesen Teil zu lesen. Hier ist eine Zusammenfassung des Problems:
Grundsätzlich habe ich eine Kollisionserkennungs- und -auflösungs-Engine mit einem räumlichen Rasterteilungssystem, bei dem Kollisionsreihenfolge und Kollisionsgruppen eine Rolle spielen. Es muss sich jeweils ein Körper bewegen, dann eine Kollision erkennen und dann die Kollisionen auflösen. Wenn ich alle Körper auf einmal bewege und dann mögliche Kollisionspaare generiere, ist dies offensichtlich schneller, aber die Auflösung bricht ab, weil die Reihenfolge der Kollisionen nicht eingehalten wird. Wenn ich einen Körper nach dem anderen bewege, muss ich dafür sorgen, dass die Körper Kollisionen überprüfen, und das wird zu einem ^ 2-Problem. Fügen Sie Gruppen in die Mischung ein, und Sie können sich vorstellen, warum es mit vielen Körpern sehr langsam und sehr schnell wird.
Update: Ich habe wirklich hart daran gearbeitet, konnte aber nichts optimieren.
Ich habe auch ein großes Problem entdeckt: Mein Motor ist abhängig von der Reihenfolge der Kollisionen.
Ich habe versucht, eine einzigartige Kollisionspaargenerierung zu implementieren , die definitiv alles um ein Vielfaches beschleunigt, aber die Kollisionsreihenfolge gebrochen hat .
Lassen Sie mich erklären:
In meinem ursprünglichen Design (ohne Paare zu generieren) passiert Folgendes:
- ein einzelner Körper bewegt sich
- Nachdem es sich bewegt hat, aktualisiert es seine Zellen und holt sich die Körper, gegen die es kollidiert
- Wenn es einen Körper überlappt, gegen den es aufgelöst werden muss, beheben Sie die Kollision
Dies bedeutet, dass, wenn sich ein Körper bewegt und gegen eine Wand (oder einen anderen Körper) stößt, nur der Körper, der sich bewegt hat, seine Kollision löst und der andere Körper davon nicht betroffen ist.
Das ist das Verhalten, das ich mir wünsche .
Ich verstehe, dass es für Physik-Engines nicht üblich ist, aber es hat viele Vorteile für Spiele im Retro-Stil .
Im üblichen Gitterdesign (Erzeugung eindeutiger Paare) geschieht dies:
- Alle Körper bewegen sich
- Nachdem sich alle Körper bewegt haben, aktualisieren Sie alle Zellen
- erzeugen eindeutige Kollisionspaare
- Behandeln Sie für jedes Paar die Kollisionserkennung und -auflösung
In diesem Fall hätte eine gleichzeitige Bewegung dazu führen können, dass sich zwei Körper überlappen, und sie lösen sich gleichzeitig auf. Dadurch werden die Körper effektiv "gegeneinander gedrückt" und die Kollisionsstabilität mit mehreren Körpern wird aufgehoben
Dieses Verhalten ist für Physik-Engines üblich, in meinem Fall jedoch nicht akzeptabel .
Ich habe auch ein anderes Problem gefunden, das sehr wichtig ist (auch wenn es in einer realen Situation nicht wahrscheinlich ist):
- Betrachten Sie Körper der Gruppen A, B und W
- A kollidiert und löst sich gegen W und A auf
- B kollidiert und löst sich gegen W und B auf
- A tut nichts gegen B
- B macht nichts gegen A
Es kann eine Situation geben, in der viele A-Körper und B-Körper dieselbe Zelle belegen. In diesem Fall gibt es eine Menge unnötiger Iterationen zwischen Körpern, die nicht aufeinander reagieren dürfen (oder Kollisionen nur erkennen, aber nicht auflösen). .
Für 100 Körper, die dieselbe Zelle belegen, sind es 100 ^ 100 Iterationen! Dies geschieht, weil keine eindeutigen Paare generiert werden - aber ich kann keine eindeutigen Paare generieren , da ich sonst ein Verhalten bekomme, das ich nicht wünsche.
Gibt es eine Möglichkeit, diese Art von Kollisionsmotor zu optimieren?
Dies sind die Richtlinien, die eingehalten werden müssen:
Reihenfolge der Kollision ist extrem wichtig!
- Körper bewegen muss einen nach dem anderen , dann überprüfen Sie auf Kollisionen einen nach dem anderen , und Entschlossenheit nach der Bewegung eines nach dem anderen .
Körper müssen 3 Gruppenbitsätze haben
- Gruppen : Gruppen, zu denen der Körper gehört
- GroupsToCheck : Gruppen, die der Körper erkennen muss Kollision
- GroupsNoResolve : Gruppen, die der Body nicht auflösen darf Kollision
- Es kann Situationen geben, in denen ich nur möchte, dass eine Kollision erkannt, aber nicht behoben wird
Pre-Update:
Vorwort : Ich bin mir bewusst, dass die Optimierung dieses Engpasses keine Notwendigkeit ist - der Motor ist bereits sehr schnell. Ich würde jedoch aus Spaß- und Bildungsgründen gerne einen Weg finden, den Motor noch schneller zu machen.
Ich erstelle eine universelle C ++ 2D-Kollisionserkennungs- / Antwort-Engine mit Schwerpunkt auf Flexibilität und Geschwindigkeit.
Hier ist ein sehr einfaches Diagramm seiner Architektur:
Grundsätzlich ist die Hauptklasse World
, die den Speicher von a ResolverBase*
, a SpatialBase*
und a besitzt (verwaltet) vector<Body*>
.
SpatialBase
ist eine rein virtuelle Klasse, die sich mit der Erkennung von Kollisionen in weiten Phasen befasst.
ResolverBase
ist eine rein virtuelle Klasse, die sich mit Kollisionsauflösung befasst.
Die Körper kommunizieren World::SpatialBase*
mit SpatialInfo
Objekten, die den Körpern selbst gehören.
Derzeit gibt es eine räumliche Klasse: Dies ist ein Grid : SpatialBase
festes 2D-Grundraster. Es hat eine eigene Info-Klasse,GridInfo : SpatialInfo
.
So sieht die Architektur aus:
Die Grid
Klasse besitzt ein 2D-Array von Cell*
. Die Cell
Klasse enthält eine Sammlung von (nicht im Besitz) Body*
: a, vector<Body*>
die alle Körper enthält, die sich in der Zelle befinden.
GridInfo
Objekte enthalten auch nicht-besitzende Zeiger auf die Zellen, in denen sich der Körper befindet.
Wie ich bereits sagte, basiert die Engine auf Gruppen.
Body::getGroups()
gibt a zurückstd::bitset
der Gruppen zurück, zu denen der Körper gehört.Body::getGroupsToCheck()
Gibt einestd::bitset
der Gruppen zurück, gegen die der Körper die Kollision überprüfen muss.
Körper können mehr als eine einzelne Zelle einnehmen. GridInfo speichert immer nicht im Besitz von Zeigern auf die belegten Zellen.
Nachdem sich ein einzelner Körper bewegt hat, findet eine Kollisionserkennung statt. Ich gehe davon aus, dass alle Körper achsenausgerichtete Begrenzungsrahmen sind.
So funktioniert die breitphasige Kollisionserkennung:
Teil 1: Geodaten-Update
Für jeden Body
body
:
- Die Zellen, die ganz oben links belegt sind, und die Zellen, die ganz unten rechts belegt sind, werden berechnet.
- Wenn sie sich von den vorherigen Zellen unterscheiden,
body.gridInfo.cells
werden sie gelöscht und mit allen Zellen gefüllt, die der Körper einnimmt (2D für Schleife von der Zelle ganz oben links zur Zelle ganz unten rechts).
body
Jetzt wissen Sie garantiert, welche Zellen es belegt.
Teil 2: Aktuelle Kollisionsprüfungen
Für jeden Body
body
:
body.gridInfo.handleCollisions
wird genannt:
void GridInfo::handleCollisions(float mFrameTime)
{
static int paint{-1};
++paint;
for(const auto& c : cells)
for(const auto& b : c->getBodies())
{
if(b->paint == paint) continue;
base.handleCollision(mFrameTime, b);
b->paint = paint;
}
}
void Body::handleCollision(float mFrameTime, Body* mBody)
{
if(mBody == this || !mustCheck(*mBody) || !shape.isOverlapping(mBody->getShape())) return;
auto intersection(getMinIntersection(shape, mBody->getShape()));
onDetection({*mBody, mFrameTime, mBody->getUserData(), intersection});
mBody->onDetection({*this, mFrameTime, userData, -intersection});
if(!resolve || mustIgnoreResolution(*mBody)) return;
bodiesToResolve.push_back(mBody);
}
Die Kollision wird dann für jeden Körper in aufgelöst
bodiesToResolve
.Das ist es.
Deshalb versuche ich schon seit einiger Zeit, diese breitphasige Kollisionserkennung zu optimieren. Jedes Mal, wenn ich etwas anderes als die aktuelle Architektur / das aktuelle Setup ausprobiere, läuft etwas nicht wie geplant oder ich gehe von einer Simulation aus, die sich später als falsch herausstellt.
Meine Frage ist: Wie kann ich die breite Phase meines Kollisionsmotors optimieren ?
Gibt es eine Art magische C ++ - Optimierung, die hier angewendet werden kann?
Kann die Architektur umgestaltet werden, um mehr Leistung zu ermöglichen?
- Aktuelle Implementierung: SSVSCollsion
- Body.h , Body.cpp
- World.h , World.cpp
- Grid.h , Grid.cpp
- Cell.h , Cell.cpp
- GridInfo.h , GridInfo.cpp
Callgrind-Ausgabe für die neueste Version: http://txtup.co/rLJgz
getBodiesToCheck()
5.462.334 Mal aufgerufen wurde, und nahm 35,1% der gesamten Profilierungs Zeit (Instruction Lesezugriffszeit)