Ich mache ein Spiel, das aus vielen Bildschirmobjekten besteht, von denen eines der Spieler ist. Ich muss wissen, welche Objekte bei jeder Iteration kollidieren.
Ich habe so etwas gemacht:
for (o in objects)
{
o.stuff();
for (other in objects)
if (collision(o, other))
doStuff();
bla.draw();
}
Dies hat O (n ^ 2), was mir gesagt wurde, ist schlecht. Wie mache ich das effizienter, ist das überhaupt möglich? Ich mache das in Javascript und n ist normalerweise niedriger als 30, wird es ein Problem sein, wenn dies gleich bleibt?
Antworten:
Mit maximal 30 Objekten sollten Sie nicht viel Optimierung benötigen, außer dass Sie dieselben zwei Paare nicht mehr als einmal pro Frame gegeneinander prüfen. Was das folgende Codebeispiel abdeckt. Wenn Sie jedoch an verschiedenen Optimierungen interessiert sind, die eine Physik-Engine verwenden würde, lesen Sie den Rest dieses Beitrags weiter.
Was Sie benötigen, ist eine Implementierung zur räumlichen Partitionierung , z. B. ein Octree (für 3D-Spiele) oder ein Quadtree (für 2D-Spiele). Diese unterteilen die Welt in Unterabschnitte, und dann wird jeder Unterabschnitt im selben Herrenhaus weiter unterteilt, bis sie auf eine Mindestgröße unterteilt sind. Auf diese Weise können Sie sehr schnell überprüfen, welche anderen Objekte sich in derselben Region der Welt befinden wie andere, wodurch die Anzahl der Kollisionen begrenzt wird, gegen die Sie prüfen müssen.
Zusätzlich zur räumlichen Partitionierung würde ich empfehlen, für jedes Ihrer Physikobjekte einen AABB ( Axis-Aligned Bounding Box ) zu erstellen . Auf diese Weise können Sie den AABB eines Objekts mit einem anderen vergleichen. Dies ist viel schneller als eine detaillierte Prüfung pro Poly zwischen Objekten.
Dies kann für komplizierte oder große Physikobjekte noch einen Schritt weiter gehen, wobei Sie das Physiknetz selbst unterteilen können, wobei jeder Unterform ein eigenes AABB zugewiesen wird, gegen das Sie nur prüfen können, ob sich die AABBs zweier Objekte überlappen.
Die meisten Physik-Engines deaktivieren die aktive Physiksimulation auf Physikkörpern, sobald sie zur Ruhe kommen. Wenn ein Physikkörper deaktiviert ist, muss er nur in jedem Frame auf Kollision mit seinem AABB prüfen. Wenn etwas mit dem AABB kollidiert, wird er reaktiviert und führt eine detailliertere Kollisionsprüfung durch. Dies hält die Simulationszeiten niedrig.
Außerdem verwenden viele Physik-Engines "Simulationsinseln", auf denen eine Gruppe von Physikkörpern, die nahe beieinander liegen, zusammen gruppiert werden. Wenn alles auf der Simulationsinsel in Ruhe ist, wird die Simulationsinsel selbst deaktiviert. Der Vorteil der Simulationsinsel besteht darin, dass alle Körper in ihr aufhören können, nach Kollisionen zu suchen, sobald die Insel inaktiv ist. Die einzige Überprüfung jedes Frames besteht darin, festzustellen, ob etwas in den AABB der Insel gelangt ist. Erst wenn etwas in den AABB der Insel gelangt, muss jeder Körper auf der Insel nach Kollisionen suchen. Die Simulationsinsel wird auch reaktiviert, wenn sich ein Körper in ihr selbstständig wieder bewegt. Wenn sich ein Körper weit genug vom Zentrum der Gruppe entfernt, wird er von der Insel entfernt.
Am Ende bleibt dir so etwas (im Pseudocode):
Ich würde auch empfehlen, nicht so viele Schleifen in solchen Schleifen zu haben. Das obige Beispiel war nur, damit Sie auf die Idee kamen. Ich würde es in mehrere Funktionen aufteilen, die Ihnen die gleiche Funktionalität bieten wie oben gezeigt.
Stellen Sie außerdem sicher, dass Sie den AABBNodes-Container während des Durchlaufens nicht ändern, da dies zu fehlenden Kollisionsprüfungen führen kann. Das mag nach gesundem Menschenverstand klingen, aber Sie wären überrascht, wie einfach es ist, wenn Dinge auf Kollisionen reagieren und Änderungen verursachen, die Sie nicht erwarten würden. Wenn beispielsweise eine Kollision dazu führte, dass eines der kollidierenden Objekte seine Position so weit änderte, dass es aus dem AABB des von Ihnen überprüften Octree-Knotens entfernt wurde, konnte dieser Container geändert werden. Um dies zu lösen, empfehle ich, eine Liste aller Kollisionsereignisse zu führen, die während der Überprüfungen auftreten. Nachdem alle Überprüfungen abgeschlossen sind, durchlaufen Sie die Liste und senden Sie alle Kollisionsereignisse aus.
quelle
In Ihrem Beispiel wird jedes Objektpaar mehrmals getestet.
Nehmen wir ein sehr einfaches Beispiel mit einem Array, das 0,1,2,3 enthält
Mit Ihrem Code erhalten Sie Folgendes:
Sehen wir uns nun den folgenden Code an:
Wenn wir das Array mit 0,1,2,3 erneut verwenden, haben wir das folgende Verhalten:
Mit dem zweiten Algorithmus haben wir 6 Kollisionstests, während der vorherige 12 Kollisionstests verlangte.
quelle
N(N-1)/2
Vergleiche durch, bei denen es sich immer noch um eine O (N ^ 2) -Leistung handelt.Entwerfen Sie Ihren Algorithmus entsprechend Ihren Anforderungen, aber halten Sie die Implementierungsdetails gekapselt. Auch in Javascript gelten grundlegende OOP-Konzepte.
Denn das
N =~ 30, O(N*N)
ist kein Problem, und Ihre lineare Suche ist wahrscheinlich genauso schnell wie jede andere Alternative. Sie möchten jedoch keine Annahmen in Ihren Code fest codieren. Im Pseudocode hätten Sie eine SchnittstelleDas beschreibt, was Ihre Artikelliste tun kann. Anschließend können Sie eine ArrayContainer-Klasse schreiben, die diese Schnittstelle implementiert. In Javascript würde der Code folgendermaßen aussehen:
Und hier ist ein Beispielcode, der 300 Begrenzungsrahmen erstellt und alle Schnittpunkte abruft. Wenn Sie ArrayContainer und QuadTreeContainer korrekt implementiert haben, müssen Sie in Ihrem Code nur Änderungen
var allMyObjects = new ArrayContainer()
an vornehmenvar allMyObjects = QuadTreeContainer()
.Ich habe die Implementierung für den Standard-ArrayContainer hier erstellt:
http://jsfiddle.net/SKkN5/1/
quelle
Sie sollten auch die Arten von Objekten berücksichtigen, die sinnvoll kollidieren können.
Zum Beispiel muss der Spieler wahrscheinlich auf Kollision mit allem außer seinen eigenen Kugeln überprüft werden. Feinde müssen jedoch möglicherweise nur gegen die Kugeln des Spielers geprüft werden. Kugeln müssen mit ziemlicher Sicherheit nicht miteinander kollidieren.
Um dies effizient zu implementieren, möchten Sie wahrscheinlich separate Objektlisten führen, eine pro Objekttyp.
quelle