3D-Kollisionsdetektion
Dieser Artikel bietet eine Einführung in die verschiedenen Begrenzungsvolumentechniken, die zur Implementierung der Kollisionsdetektion in 3D-Umgebungen genutzt werden. Folgeartikel werden Implementierungen in spezifischen 3D-Bibliotheken behandeln.
Achsen-ausgerichtete Begrenzungsboxen
Wie bei der 2D-Kollisionsdetektion sind achsen-ausgerichtete Begrenzungsboxen (AABB) der schnellste Algorithmus, um zu bestimmen, ob die beiden Spielelemente sich überschneiden oder nicht. Diese Methode besteht darin, Spielelemente in eine nicht gedrehte (daher achsen-ausgerichtete) Box einzuschließen und die Positionen dieser Boxen im 3D-Koordinatenraum zu überprüfen, um zu sehen, ob sie sich überschneiden.
Die achsen-ausgerichtete Einschränkung existiert aus Leistungsgründen. Der Überlappungsbereich zwischen zwei nicht gedrehten Boxen kann allein mit logischen Vergleichen überprüft werden, während gedrehte Boxen zusätzliche trigonometrische Operationen erfordern, die langsamer zu berechnen sind. Wenn Sie Elemente haben, die rotieren werden, können Sie entweder die Dimensionen der Begrenzungsbox anpassen, sodass sie das Objekt weiterhin einhüllt, oder eine andere Begrenzungsgeometrie wie Kugeln verwenden (die rotationsinvariant sind). Das untenstehende animierte GIF zeigt ein grafisches Beispiel einer AABB, die ihre Größe an das rotierende Element anpasst. Die Box ändert ständig ihre Dimensionen, um das darin enthaltene Element passgenau zu umschließen.
Hinweis: Schauen Sie sich den Artikel Begrenzungsvolumen mit Three.js an, um eine praktische Implementierung dieser Technik zu sehen.
Punkt vs. AABB
Zu überprüfen, ob ein Punkt innerhalb einer AABB liegt, ist ziemlich einfach — wir müssen nur überprüfen, ob die Koordinaten des Punktes innerhalb der AABB liegen, wobei jede Achse separat betrachtet wird. Wenn wir annehmen, dass Px, Py und Pz die Koordinaten des Punktes sind und BminX–BmaxX, BminY–BmaxY, und BminZ–BmaxZ die Bereiche jeder Achse der AABB sind, können wir mit der folgenden Formel berechnen, ob eine Kollision zwischen den beiden stattgefunden hat:
Oder in JavaScript:
function isPointInsideAABB(point, box) {
return (
point.x >= box.minX &&
point.x <= box.maxX &&
point.y >= box.minY &&
point.y <= box.maxY &&
point.z >= box.minZ &&
point.z <= box.maxZ
);
}
AABB vs. AABB
Ob eine AABB eine andere AABB schneidet, lässt sich ähnlich wie beim Punkt-Test überprüfen. Wir müssen nur einen Test pro Achse durchführen, wobei wir die Begrenzungen der Boxen verwenden. Das untenstehende Diagramm zeigt den Test, den wir über die X-Achse durchführen würden — im Grunde überlappen sich die Bereiche AminX–AmaxX und BminX–BmaxX?
Mathematisch würde das so aussehen:
Und in JavaScript würden wir dies verwenden:
function intersect(a, b) {
return (
a.minX <= b.maxX &&
a.maxX >= b.minX &&
a.minY <= b.maxY &&
a.maxY >= b.minY &&
a.minZ <= b.maxZ &&
a.maxZ >= b.minZ
);
}
Begrenzungskugeln
Das Verwenden von Begrenzungskugeln zur Kollisionsdetektion ist etwas komplexer als AABB, aber immer noch ziemlich schnell zu testen. Der Hauptvorteil von Kugeln ist, dass sie invariant gegenüber Rotation sind, sodass, wenn das umhüllte Element rotiert, die Begrenzungskugel immer noch dieselbe bleibt. Ihr Hauptnachteil ist, dass, es sei denn, das umhüllte Objekt ist tatsächlich kugelförmig, die Umhüllung normalerweise nicht sehr gut passt (zum Beispiel würde das Umhüllen einer Person mit einer Begrenzungskugel viele Fehlalarme verursachen, während eine AABB eine bessere Passform wäre).
Punkt vs. Kugel
Um zu überprüfen, ob eine Kugel einen Punkt enthält, müssen wir den Abstand zwischen dem Punkt und dem Mittelpunkt der Kugel berechnen. Wenn dieser Abstand kleiner oder gleich dem Radius der Kugel ist, befindet sich der Punkt innerhalb der Kugel.
Berücksichtigt man, dass der euklidische Abstand zwischen zwei Punkten A und B ist, würde sich unsere Formel zur Kollisionsdetektion von Punkt vs. Kugel folgendermaßen darstellen:
Oder in JavaScript:
function isPointInsideSphere(point, sphere) {
// we are using multiplications because is faster than calling Math.pow
const distance = Math.sqrt(
(point.x - sphere.x) * (point.x - sphere.x) +
(point.y - sphere.y) * (point.y - sphere.y) +
(point.z - sphere.z) * (point.z - sphere.z),
);
return distance < sphere.radius;
}
Hinweis:
Der obige Code enthält eine Quadratwurzel, deren Berechnung teuer sein kann. Eine einfache Optimierung, um dies zu vermeiden, besteht darin, den quadratischen Abstand mit dem quadratischen Radius zu vergleichen, sodass die optimierte Gleichung stattdessen distanceSqr < sphere.radius * sphere.radius
verwenden würde.
Kugel vs. Kugel
Der Test Kugel vs. Kugel ist dem Test Punkt vs. Kugel ähnlich. Was wir hier testen müssen, ist, dass der Abstand zwischen den Mittelpunkten der Kugeln kleiner oder gleich der Summe ihrer Radien ist.
Mathematisch sieht das so aus:
Oder in JavaScript:
function intersect(sphere, other) {
// we are using multiplications because it's faster than calling Math.pow
const distance = Math.sqrt(
(sphere.x - other.x) * (sphere.x - other.x) +
(sphere.y - other.y) * (sphere.y - other.y) +
(sphere.z - other.z) * (sphere.z - other.z),
);
return distance < sphere.radius + other.radius;
}
Kugel vs. AABB
Zu testen, ob eine Kugel und eine AABB kollidieren, ist etwas komplizierter, aber immer noch einfach und schnell. Ein logischer Ansatz wäre, jeden Scheitelpunkt der AABB zu überprüfen, indem man für jeden einen Punkt-gegen-Kugel-Test durchführt. Dies ist jedoch übertrieben — das Testen aller Scheitelpunkte ist unnötig, da wir uns damit begnügen können, die Entfernung zwischen dem nächsten Punkt der AABB (nicht unbedingt ein Scheitelpunkt) und dem Mittelpunkt der Kugel zu berechnen und zu prüfen, ob sie kleiner oder gleich dem Radius der Kugel ist. Wir können diesen Wert erhalten, indem wir den Mittelpunkt der Kugel an die Grenzen der AABB klemmen.
In JavaScript würden wir diesen Test folgendermaßen durchführen:
function intersect(sphere, box) {
// get box closest point to sphere center by clamping
const x = Math.max(box.minX, Math.min(sphere.x, box.maxX));
const y = Math.max(box.minY, Math.min(sphere.y, box.maxY));
const z = Math.max(box.minZ, Math.min(sphere.z, box.maxZ));
// this is the same as isPointInsideSphere
const distance = Math.sqrt(
(x - sphere.x) * (x - sphere.x) +
(y - sphere.y) * (y - sphere.y) +
(z - sphere.z) * (z - sphere.z),
);
return distance < sphere.radius;
}
Verwendung einer Physik-Engine
3D-Physik-Engines bieten Algorithmen zur Kollisionsdetektion, die meist ebenfalls auf Begrenzungsvolumen basieren. Eine Physik-Engine funktioniert, indem sie einen physischen Körper erstellt, der normalerweise an eine visuelle Darstellung angeheftet ist. Dieser Körper hat Eigenschaften wie Geschwindigkeit, Position, Rotation, Drehmoment usw. und auch eine physische Form. Diese Form wird in den Kollisionsdetektions-Berechnungen berücksichtigt.
Wir haben eine Live-Demo zur Kollisionsdetektion (mit Quellcode) vorbereitet, die Sie betrachten können, um solche Techniken in Aktion zu sehen — diese verwendet die Open-Source-3D-Physik-Engine cannon.js.
Siehe auch
Verwandte Artikel bei MDN:
Externe Ressourcen:
- Einfache Schnittstellentests für Spiele auf Game Developer
- Begrenzungsvolumen auf Wikipedia