Advanced Games Physics
6. Kapitel

Vektorbasierte Kollisionserkennung

Vektorbasierte Verfahren für Polygone

Kreis trifft auf Polygon

Eingedenk der Tatsache, dass die Leistungsfähigkeit künftiger Prozessoren immer weiter getrieben wird, soll hier ein Verfahren vorgestellt werden, dessen Präzission beliebig gesteigert werden kann. Voraussetzung für die Anwendung dieses Verfahrens ist, dass die Objekte - ob photorealsistisch oder nicht - als Polygon vorliegen müssen. Um zu einem solchen Polygon zu kommen, schau Dir das Kapitel Photorealistische Objekte an. Dort wird ein Programm zur Digitalisierung - besser Konturerfassung - von Images vorgestellt und zur Anwendung bereit gestellt.

Während die einfachen Verfahren meist die Abstände zwischen den Mittelpunkten der kollidierenden Objekte messen, liegen die Dinge in der Regel nicht so einfach: Von gleichen Abständen zum Mittelpunkt (was ist hier eigentlich der Mittelpunkt?) kann ja nicht mehr die Rede sein! So behelfen wir uns mit einer stückweise linearen Approximation der Kontur des Objektes. D.h. wir verteilen auf der Kontur charakteristische Punkte Pi, die durch Geradenstücke miteinander verbunden werden. Benachbarte Punkte, z.B. Pi bzw. Pi+1 grenzen die durch beide Punkte verlaufende Gerade zu einem Segment ein. Beide Punkte werden geometrisch durch ihre Ortsvektoren x i und x i+1 beschrieben.
Wir bleiben bei dem oben beschriebenen Szenarium: der Komet Tschuri wird von dem Lander Philae besucht. Der Einfachheit halber betrachten wir das kollidierende Objekt, also Philae, als Kreis (bzw. Kugel in 3D), deren Lage durch den Ortsvektor x Obj beschrieben wird. Philae als Kreis (bzw. Kugel in 3D) zu betrachten, ist bei den gegebenen Größen­verhältnissen zu rechtfertigen.
2D-Approximation des Kometen Tschuri durch ein Polygon

Abb. 2D-Approximation des Kometen Tschuri durch ein Polygon
Quelle: ESA/Rosetta/MPS for OSIRIS Team MPS / UPD / LAM / IAA / SSO / INTA / UPM / DASP / IDA License: CC-BY-SA

Die Aufgabe besteht jetzt darin, die kürzeste Entfernung zwischen dem Objekt Kreis und dem Objekt Tschuri zu finden. Zu diesem Zweck sind zunächst einmal alle Entfernungen zwischen dem Kreis (Philae) und den einzelnen Segmenten des Polygons, das die Kontur von Tschuri beschreibt, zu berechnen. Dazu sind I Berechnungen erforderlich, wenn das Polygon aus I Punkten bzw. Segmenten besteht. Sind aber beide Objekte als Polygone umschrieben, dann sind I1·I2 Berechnungen erforderlich!
Nun zur Berechnung der Distanzen. Siehe hierzu und .

Betrachtung eines Polygonsegmentes

Abb. Betrachtung eines Polygon­seg­men­tes

Benennung der Vektoren

Abb. Benennung der Vek­toren


Zur Berechnung der Distanzen sind für jedes Segment i im einzelnen die folgenden Schritte notwendig:
  1. Berechnung der einzelnen Vektoren:
    ()
    O i = P O b j P i
    und
    ()
    S i = P i + 1 P i

  2. Berechnung der Distanz:
    Da die Distanzvektoren D stets senkrecht auf den Segmentvektoren S stehen, handelt es sich hier um rechtwinklige Dreiecke. Die Beträge der Distanzvektoren | D i | stellen die Gegenkatheten zum Betrag des jeweiligen Objektvektors | O i | dar. So kann der Betrag des Distanzvektors | D i | wie folgt errechnet werden:
    ()
    | D i | = | O i | · sin α i

    Erweitern wir nun mit dem Betrag des Segmentvektors | S i | so erhalten wir
    ()
    | D i | = | O i | · | S i | · sin α i | S i |

    Der eingeschlossen Winkel αi kann aus den gegebenen Größen berechnet werden, aber das ist gar nicht erforderlich, denn wir sehen, dass der Zähler von genau der Definition des vektoriellen Kreuzproduktes entspricht. Deshalb kann unter Verwendung des Kreuzproduktes auch geschrieben werden:
    ()
    | D i | = | O i × S i | | S i | = d i

    Somit können jetzt alle Abstände di - die ja den Beträgen der Distanzvektoren | D i | entsprechen - zwischen dem Objekt Kreis und den einzelnen Segmenten des Objektes Tschuri berechnet und miteinander verglichen werden. Der kleinste Distanzwert macht das Rennen und kann mit dem Kreisradius verglichen werden.

    Wenn die kleinste Distanz
    ()
    d i r O b j
    dann liegt möglicher Weise eine Kollision vor!

  3. Auswahl des Segmentes mit der kürzesten Distanz:

    Wie zeigt, gibt es in einem Polygon nicht nur eine "kürzeste" Distanz, die nach der Rechenvorschrift ermittelt wird. Wir dürfen nicht vergessen, dass zu jedem Segment ein - verschieblicher - Segmentvektor gehört, zu dem stets auch eine kürzeste Distanz zum Objekt existiert!
    Auskunft kann der part-Vektor Part i geben. Das ist der Vektor, der den Abstand des Distanzvektors D i auf den Segmentvektor S i vom Ausgangspunkt Pi angibt. Es ist die Länge des Partvektors, die wir als Kriterium für die wirklich kürzeste Distanz aus allen Segmenten heranziehen können: Es gilt

    • ist der Partvektor länger als die Länge des jeweiligen Segmentes, liegt der Distanzvektor außerhalb des aktuellen Segmentes (in im Segment i-1). Diese Distanz ist zu verwerfen!

    • ist die Länge des Partvektors negativ (in im Segment i+1), ist die gefundene Distanz ebenfalls zu verwerfen.

    • nur im Segment i findet keine Verletzung der Auswahlkriterien statt! Die Bedingung für das zutreffende Segment und damit die wirklich kürzeste Distanz lautet:
    ()
    l S e g m e n t i l p a r t i 0
    Auswahl des Segmentes mit der kürzesten Distanz

    Abb. Auswahl des Segmentes mit der kürzesten Distanz

    Berechnen wir die Länge des Part-Vektors Part i . Die Länge (nicht der Betrag, die Länge kann ja auch negativ werden!) des Partvektors lpart stellt die Ankathete zum Betrag des Objektvektors | O i | dar. Somit kann dem obigen Schema folgend, die Länge des Partvektors als das vektorielle Punktprodukt (auch Skalarprodukt genannt) geschrieben werden:
    ()
    l p a r t = | S i | · | O i | · cos α | S i |
    also
    ()
    l p a r t = S i · O i | S i |
  4. Berechnung des Stoßortes:

    Nun dürfte noch von Interesse sein, wo auf dem Segment die Kollision stattfindet. Auch hier hilft uns wieder der Part-Vektor. Für die Ortsbestimmung reicht die Länge des Part-Vektors aber nicht aus, hierfür ist ein Vektor erforderlich. Da das Skalarprodukt im Ergebnis einen Skalar liefert, muss die Umwandlung in einen Vektor in einem weiteren Schritt erfolgen. Da die Partvektoren P a r t i parallel zu den Segmentvektoren S i verlaufen, können wir von diesen die Richtung übernehmen, d.h. wir bilden den Einheitsvektor der Segmentvektoren und multiplizieren diese mit dem jeweiligen Betrag des Partvektors:
    ()
    P a r t i = l p a r t · S i | S i |

  5. Berechnung des Normalenwinkels:

    Die Kenntnis des Normalenwinkels β () ist für die Berechnung des Ausfallswinkels nach einem Stoß wichtig. Er berechnet sich, sofern der Stoßort auf einem Segment liegt, aus den Koordinaten des Partvektors und des Objektvektors:
    ()
    β i = arctan y O b j y P a r t x O b j x P a r t

Nun gibt es noch eine Kleinigkeit zu beachten. Dort wo benachbarte Segmente aneinander stoßen, entsteht eine Ecke ().

  • Konvexe Ecken
    Eine konvexe Ecke liegt dann vor, wenn das Objekt links außerhalb des aktuellen Segmentes liegt. Dann wird der Partvektor negativ. Oder das Objekt liegt rechts außerhalb des aktuellen Segmentes, dann ist der Partvektor größer als der Segment­vektor. Beide Situationen sind programm­technisch leicht abzutesten. Hier gilt als Distanz die einfache Entfernung zwischen Objekt­mittel­punkt und dem jeweiligen Eckpunkt gegeben durch den Objektvektor O i . Es gilt .
    Und der Normalenwinkel entspricht dem Winkel der Verbindungsline Objekt - Eckpunkti:
    ()
    β i = arctan y O b j y i x O b j x i

  • Konkave Ecken
    Konkave Ecken bedürfen keiner besonderen Behandlung bei den Berechnung. Allerdings kann der Fall eintreten, dass der Kreis genau beide benachbarten Segmente berührt. Da alle Segmente aufeinander folgend abgetestet werden, wird in einem solchen Fall erst das Segment mit dem kleineren Index und anschließend das Segment mit höherem Index abgeprüft. Es gibt also zwei kürzeste Entfernungen bzw. es werden zwei Kontaktorte und zwei Normalenwinkel ermittelt!
Sonderfall Ecke

Abb. Sonderfall Ecke


demonstriert die Funktion der Vektor basierten Kollisions­erkennung. Du kannst den grünen Ball mit der Maus über das gesamte Feld bewegen. Sobald Du damit aber ein Kontursegment berührst wird eine Kollision signalisiert (roter Punkt). Gleichzeitig werden auch die entsprechenden Vektoren angezeigt (diese sind farblich gekennzeichnet, ihre Bedeutung wird rechts in der Agenda benannt). Besondere Beachtung verdienen die Ecken und die Anfangs- und Endpunkte der Kontur. Bei konkaven Ecken treten mehrfache Kontakte (touch)s auf. Zusätzlich erfolgt eine Information darüber, welcher Typ Ecke vorliegt. Das Sympol < steht für eine konvexe Ecke, das Symbol > steht für keine oder eine konkave Ecke.
Die Kollisions­erkennung ist unabhängig von der Seite, von der Du Dich der Kontur näherst.
Neben dem Kontaktort P(x, y) werden der Normalenwinkel β und die Eindringtiefe dErr numerisch angegeben.

Bitte einen Augenblick Geduld
während das Programm geladen wird!
Abb. Vektor basierte Kollisionserkennung


Das nebenstehende Beispielprogramm wird so wie das in gezeigte Programm bedient. Hinzu kommt eine Auswahl unterschiedlicher Konturen, die mit dem Button Profil ausgewählt werden können.
download processing
download p5.js
run program


Zusammenfassung Die ausschließlich Distanz besierte Kollisionserkennung erfordert einen relativ geringen Rechenaufwand, der sich auf das Quadrieren von Abstandsdifferenzen der Ortskomponenten beschränkt. Allerdings erkennt er eine Kollision immer erst nachdem die Kollision statt gefunden hat. Das kann in dynamischen Anwendungen zu Problemen führen. Darüber werden wir in den folgenden Abschnitten sprechen.

Polygon trifft auf Polygon

Vom geometrischen Ansatz her ist die Lösung für die Kollisionserkennung von zwei oder mehreren Polygonen nicht schwerer als die vorangehend beschriebene Aufgabe. Die Herausforderung besteht hier darin, den Rechenauswand so klein wie möglich zu halten. Weil hier nicht nur ein Punkt (Kreismittelpunkt) mit den I Punkten des Polygons interagiert, sondern die I Punkte des einen mit den J Punkten des anderen Polygons in Abhängigkeit zu bringen sind. Damit müssten theoretisch I·J Berechnungen ausgeführt werden.
Deshalb werden, bevor die eigentliche Ermittlung der Kollisionen beginnt, alle Maßnahmen ergriffen, um die Zahl der Berechnungen zu minimieren.
  1. Zunächst werden die Polygone (2D) wie Kreise, deren Radius Rext dem Abstand des am weitesten vom Mittelpunkt entfernten Konturpunktes entspricht, behandelt. So kann vorab geprüft werden, ob eine Kollision zwischen zwei oder mehreren Polygonen überhaupt auftreten kann. Diese Aufgabe entspricht dem Standardverfahren und bedarf nur eines geringen Rechenaufwandes. Dieser geringe Zusatzaufwand lohnt sich insbesondere dann, wenn mehrere Objekte kollidieren können.
  2. Im nächsten Schritt wird geprüft, welche Konturpunkte überhaupt kollidieren können. Es ist einsichtig, dass die vom Kollisionsgegner abgewendeten Punkte nie in eine Kollision verwickelt werden können. Wenn zwei Objekte aufeinander treffen und kollidieren, ist die Frage "wer trifft auf wen". Bei Polygonen ist klar, dass immer nur Konturpunkte des einen Objektes auf die Verbindungslinien (Segmente) oder ebenfalls Konturpunkte des anderen Objektes treffen. Das eine Objekt benenne ich in Bezug auf die Kollisionserkennung als Initiator und das andere als Opponent. Dabei spielt es keine Rolle, wer sich auf wen zubewegt.
    zeigt diese Situation für zwei Objekte (alle Objekte werden mit dem Index k numeriert, hier also mit 0 und 1). Das betrifft alle Konturpunkte, die aus Sicht des Initiator-Objektes (k = 1) hinter einer gedachten Linie, die einerseits durch den Mittelpunkt (oder besser Schwerpunkt) des Opponent-Objektes (k= 0) und andererseits rechtwinklig zur Verbindungslinie zwischen den Mittelpunkten beider Objekte verläuft, liegen.
    Im folgenden wird nur eine Kollisionssituation, nämlich die nach , betrachtet.

    Um zu prüfen, ob eine beliebiger Konturpunkt Pk,i vor oder hinter der gedachten Linie liegt, genügt es, die Länge dk,i ins Verhältnis zum Mittelpunktsabstand Dk zu setzen. Die Länge dk,i wiederum ist dem vektoriellen Skalarprodukt aus Quasi-Ortsvektor Q k,i und dem Distanzvektor D k proportional:
    ()
    | d k , i | = D k · Q k , i | D k |
    Quasi-Ortsvektor, weil dieser Vektor vom Schwerpunkt des Initiator-Objektes ausgeht und nicht wie üblich vom Ursprung des Koordinatensystems!

    Im folgenden werde ich mich nur auf die in gezeigten Objekte und Wirkrichtung beschränken. Das heißt, es wird geprüft, ob der i-te Punkt des 0-ten Objektes (Opponent) im Kollisionsbereich des 1-ten Objektes (Initiator) liegt.

    Mit den Vektoren:
    (a)
    D = X 0 X 1

    und
    (b)
    Q = P X 1

    den Abkürzungen:
    (a)
    D y = X 0 . y X 1 . y
    und
    (b)
    D x = X 0 . x X 1 . x
    sowie
    (c)
    Q y = P . y X 1 . y
    und
    (d)
    Q x = P . x X 1 . x
    ergeben sich die Vektoren zu
    (a)
    D = D x D y
    und
    (b)
    Q = Q x Q y
    Auswahl der nächstliegenden Konturpunkte

    Abb. Auswahl der nächstliegenden Konturpunkte

    Jetzt können wir das Skalarprodukt - ohne die p5.Vector()-Funktion zu verwenden - berechnen
    ()
    | d | = 1 | D | · D x D y T · Q x Q y
    denn so erhalten wir ein Ergebnis, das mit einer sehr viel geringeren Anzahl von Rechenoperationen auskommt!
    Mit dem Betrag
    ()
    | D | = D x 2 + D y 2
    ergibt sich der auf den Teststrahl projezierte Abstand zu:
    ()
    d = D x · Q x + D y · Q y D x 2 + D y 2
    Also, alle Konturpunkte i, die die Bedingung d > D erfüllen, liegen außerhalb des Kollisionbereiches (grauer Bereich in ). Ausgedrückt durch unsere Hilfsgrößen Dx und Dy bedeutet das
    ()
    d i > D x 2 + D y 2
    Mit dem Ergebnis von lautet die Bedingung für den Ausschluß aus der Kollisionsermittlung:
    ()
    D x · Q x + D y · Q y > D x 2 + D y 2

Nun zur Kollisionserkennung. Die Kollisionserkennung zwischen Polygonen hat mehrere Anforderungen zu erfüllen:
  1. Im Gegensatz zur Kollision zwischen Polygon und Kreis (bzw. Kugel), wie sie im Abschnitt Kreis trifft auf Polygon beschrieben wird, gibt es beim Aufeinandertreffen von Polygonen ein Problem. Während im ersten Fall eine Prüfung, ob der Betrag des Distanzvektors im i-ten Segment | D i | kleiner als der Kreisradius ist, genügt, funktioniert dies hier nicht. Warum? Weil die Konturelemente, die kollidieren können, Punkte sind. Deren Radius ist aber gleich 0! Das führt in Hinblick auf die Unschärfen, bedingt durch die zeitliche Quantisierung, dazu, dass kaum ein Treffer registriert werden wird. Wir suchen also nach einem Verfahren, dass auch für Punkte, die Linien treffen oder diese unterschreiten, geeignet ist.
  2. Der Regelfall wird sein, dass die Ecke des einen Polygons auf eine Verbindungslinie des anderen Polynoms trifft. Was aber, wenn die kollidierenden Linien auf beiden Seiten parallel verlaufen? Dann treffen zwei Konturpunkte auf eine Linie!

zeigt eine Situation, in der ein Konturpunkt (blau) des einen Objekts (Initiator: k = 1) auf ein Segment (rot) eines anderen Objektes (Opponent: k = 0) zu treffen scheint. Es soll nun geprüft werden, ob der blaue Konturpunkt das rote Segment berührt oder ggf. darüber hinaus geht.
Eine wichtige Rolle bei der Ermittlung des Kollisionsortes spielen die Schwerpunkte X0 (Opponent) und X1 (Initiator). Da Schwerpunkte als invariant gegenüber rotatorischen Bewegungen sind, kann jedes beliebig geformte Objekt durch eine Punktmasse genau im Schwerpunkt repräsentiert werden. Das bedeutet für translatorische Bewegungen, dass möglich Kollisionen längs eines Teststrahls, der vom Schwerpunkt X1 des Initiators ausgeht und durch den betreffenden Konturpunkt (P1,j) verläuft, auftreten können.
Auf Seiten des Opponenten schneidet der Teststrahl ein Kontursegment, begrenzt durch die Konturpunkte P0,i und P0,i+1. Herauszufinden, wo sich diese beiden Geraden schneiden, ist eine Standardaufgabe der Geometrie.

Zunächst werden beide Geraden als Vektoren behandelt. Zunächst die Gerade g0, die parallel zum Segmentvektor S0,i verläuft:
(a)
g 0 = P 0, i + λ · S
worin
(b)
S = P 0, i + 1 P 0, i = S x S y
den Segmentvektor darstellt.

Und eine weitere Gerade g1 repräsentiert den Teststrahl, der parallel zum Radialvektor R1,j verläuft:
(a)
g 1 = X 1 + μ · R
worin
(b)
R = P 1, j X 1 = R x R y
den Radialvektor darstellt.
Die Parameter der Geradengleichungen λ und μ erlauben, jeden beliebigen Punkt auf diesen Geraden zu erreichen.

Bestimmung des Kollisionsortes

Abb. Bestimmung des Kollisionsortes

Um den Schnittpunkt beider Geraden zu bestimmen, werden die und einander gleich gesetzt. Die Gleichheit muss sowohl für die x- als auch für die y-Komponenten gelten. So erhalten wir nach Umsortieren ein Gleichungssystem bestehend aus zwei Gleichungen (2D!) für die Unbekannten λ und μ:
(a)
P 0, i X 1 . x = μ · R . x λ · S . x
und
(b)
P 0, i X 1 . y = μ · R . y λ · S . y
Noch eine weitere Abkürzung für die Differenz des Vektors auf der linken Seite der a) bzw. b) ist sinnvoll:
()
Q = P 0, i X 1 = Q x Q y
Dieser Vektor stellt im Prinzip den Quasi-Ortsvektor dar (wie schon erwähnt liegt dessen Ursprung im Schwerpunkt des Initiator-Objektes und nicht wie gewöhlich im Koordinatenursprung!)

Jetzt können wir das Gleichungssystem lösen. Das liefert für
(a)
μ = S x · Q y Q x · S y S x · R y R x · S y
und für
(b)
λ = R x · Q y Q x · R y S x · R y R x · S y
Mit den so bestimmten Werten für λ und μ kann nun der Schnittpunkt der beiden Geraden durch Einsetzen in eine der beiden a) oder a) ermittelt werden. Liegt dieser Schnittpunkt aber wirklich auf dem ausgewählten Segment des Opponent-Objekts? Darüber gibt der Wert von λ Auskunft. λ muss die Bedingung

0 λ 1

erfüllen. Und wenn μ noch diese Bedingung

0 μ 1

erfüllt, dann berühren oder überdecken sich Initiator- und Opponenten-Objekt, d.h. eine Kollision liegt vor!

Zusammenfassung
Nach dem Einlesen und Entpacken der Konturdaten werden die oben aufgeführten Schritte, die der Aufwandsminimierung dienen, der Reihe nach ausgeführt.
  1. Schon in der setup()-Funktion werden die maximalen Abmessungen Rext der einzelnen Objekte bestimmt. Damit und mit dem Abstand von Schwerpunkt zu Schwerpunkt wird in der draw()-Funktion geprüft, ob eine Kollision überhaupt möglich ist.
  2. Im nächsten Schritt werden die Konturpunkte bestimmt, die dem gegenüber liegenden Objekt zugewandt sind. Dies geschieht wechselseitig und das Resultat wird im Array closestPoints für jedes Objekt gespeichert.
  3. Erst im letzten Schritt erfolgt die Prüfung auf Kollisionen. Mit den beiden vorgeschalteten Schritten wurde die Anzahl der Prüfungen auf Kollision auf das notwendige Maß reduziert.
Da jeder Konturpunkt jedes Objektes die Rolle des Initiators, sowie auch die des Opponenten, einnehmen kann, müssen die Kollisionserkennungen mit allen vorgeschalteten Schritten wechselseitig für jedes Objekt ausgeführt werden! Also, jedes Objekt ist gleichzeitig sowohl Initiator als auch Opponent! Weil nun jedes Objekt durch mehrere Konturpunkte charakterisiert ist, müssen wechselseitig alle infrage kommenden Konturpunkte des einen Objektes auf Kollisionen mit dem (oder den) anderen Objekt(en) geprüft werden.

Im Beispielprogramm sehen wir die beiden Objekte Tschuri und Philae. Da es in diesem Beispiel nur um die Funktionalität der Kollisionserkennung geht, werden hier Objektbewegungen nur durch Mausinteraktionen simuliert. Wird die Maus auf einen der roten Punkte (Schwerpunkt) gesetzt, werden translatorische Bewegungen per Maus möglich. Fasst die Maus den gelben Punkt, können Drehbewegungen ausgeführt werden.
Betätigung des Buttons Debug zeigt die Konturen und die Aktionsradien der Objekte an. Überdecken sich diese Radien, d.h. Kollisionen sind möglich, werden die aktiven Konturpunkte und die durch sie hindurch laufenden Teststrahlen angezeigt. Der Button Item erlaubt die Auswahl, von welchem Objekt diese Informationen herrühren.
download p5.js
run program