Kreis innerhalb Kreis Kollision


9

In einem meiner Projekte habe ich einen Spielbereich in Form eines Kreises. Innerhalb dieses Kreises bewegt sich ein weiterer kleiner Kreis. Ich möchte verhindern, dass sich der kleine Kreis außerhalb des größeren Kreises bewegt. Unten sehen Sie, dass sich in Bild 2 der kleine Kreis teilweise außerhalb befindet. Ich brauche eine Möglichkeit, ihn zurück zu verschieben, kurz bevor er sich nach außen bewegt. Wie kann das gemacht werden?

Grundlegendes Beispiel

Außerdem benötige ich den Kollisionspunkt entlang des Bogens des großen Kreises, damit ich die Geschwindigkeit des kleinen Kreises aktualisieren kann. Wie würde man diesen Punkt berechnen?

Bevor ich den kleinen Kreis bewege, sage ich seine nächste Position voraus und wenn er außerhalb liegt, finde ich die Kollisionszeit zwischen t = 0 und t = 1 (t = 1 Vollzeitschritt). Wenn ich die Kollisionszeit t habe, bewege ich einfach den kleinen Kreis während t anstelle eines Vollzeitschritts. Aber auch hier ist das Problem, dass ich nicht weiß, wie ich zu diesem Zeitpunkt die Kollision erkennen kann, wenn es um zwei Kreise geht und einer im anderen liegt.

BEARBEITEN:

Beispiel für einen Kollisionspunkt (grün), den ich finden möchte. Vielleicht ist das Bild ein bisschen anders, aber Sie haben die Idee.

Geben Sie hier die Bildbeschreibung ein

Antworten:


10

Nehmen wir an, der große Kreis hat Mittelpunkt Aund Radius Rund der kleine Kreis hat Mittelpunkt Bund Radius, die rsich in Richtung Position bewegen C.

Es gibt eine elegante Möglichkeit, dieses Problem mithilfe von Minkovski-Summen (eigentlich Subtraktionen) zu lösen : Ersetzen Sie die Scheibe mit dem Radius Rdurch eine Scheibe mit dem Radius R-rund die Scheibe mit dem Radius rdurch eine Scheibe mit dem Radius 0, d. H. ein einfacher Punkt bei B. Das Problem wird zu einem Linien-Kreis-Schnittpunktproblem.

Sie müssen dann nur noch prüfen, ob der Abstand ACkleiner als ist R-r. Wenn dies der Fall ist, kollidieren die Kreise nicht. Wenn es größer ist , finden Sie den Punkt Dauf , BCim Abstand R-rvon Aund das ist die neue Position der Mitte des kleinen Kreises. Dies ist gleichbedeutend mit der Feststellung k, dass:

  vec(BD) = k*vec(BC)
and
  norm(vec(AD)) = R-r

Ersetzen vec(AD)durch vec(AB) + vec(BD)ergibt:

AB² + k² BC² + 2k vec(AB).vec(BC) = (R-r

Vorausgesetzt, die Anfangsposition lag innerhalb des großen Kreises, hat diese quadratische Gleichung in keine positive Wurzel. So lösen Sie die Gleichung im Pseudocode:

b = - vec(AB).vec(BC) / BC²    // dot product
c = (AB² - (R-r)²) / BC²
d = b*b - c
k = b - sqrt(d)
if (k < 0)
    k = b + sqrt(d)
if (k < 0)
    // no solution! we must be slightly out of the large circle

Mit diesem Wert von kliegt der neue Mittelpunkt des kleinen Kreises Dso, dass BD = kBC.

Bearbeiten : quadratische Gleichungslösung hinzufügen


Danke, es sieht elegant aus, aber ich bin mir nicht ganz sicher, ob ich es verstehe. Zum Beispiel: "Finden Sie einfach den Punkt D auf BC in der Entfernung Rr von A". Ich habe ein Bild gezeichnet, um es besser zu verstehen. Wenn wir also bei B (AX, AY- (Rr)) beginnen und C ist, werden wir mit der aktuellen Geschwindigkeit enden. So verstehe ich den zitierten Text: Finden Sie einen Punkt D auf dem Liniensegment BC, der einen Abstand von Rr von A entfernt ist. Auf dem Bild, das ich gezeichnet habe, sehe ich jedoch, dass nur genau B Rr von A entfernt ist andere Punkte sind> Rr von A entfernt. Was fehlt mir?
Dbostream

@dbostream Du vermisst nichts. Wenn die beiden Kreise bereits in Kontakt sind, ist keine echte Kollision zu erkennen : Die Kollision findet in Bund statt k=0. Nun , wenn es Kollision Auflösung Sie wollen, ich habe das in meiner Antwort nicht gedeckt , weil es Wissen über die physikalischen Eigenschaften der Objekte erfordern würde. Was soll passieren? Sollte der innere Kreis nach innen springen? Oder rollen? Fegen?
Sam Hocevar

Ich möchte, dass der kleine Kreis entlang des Bogens des großen Kreises gleitet. Wenn ich mich also nicht irre, möchte ich den Kollisionspunkt auf dem Bogen des großen Kreises, damit ich seine Geschwindigkeit verwenden kann, um die Geschwindigkeit zu aktualisieren.
Dbostream

@dbostream Wenn die Bewegung so eingeschränkt werden soll, dann schlage ich vor, dass Sie dieser Einschränkung so bald wie möglich folgen: Wenn die Geschwindigkeit gleich ist V, lassen Sie den inneren Kreis V*tentlang des Kreisumfangs vorrücken R-r. Dies bedeutet eine Drehung des Winkelmaßes V*t/(R-r)um den Punkt A. Und der Geschwindigkeitsvektor kann auf die gleiche Weise gedreht werden. Sie müssen die Normalen nicht kennen (die ohnehin immer zum Mittelpunkt des Kreises ausgerichtet sind) oder die Geschwindigkeit auf andere Weise aktualisieren.
Sam Hocevar

Ich muss den kleinen Kreis noch zum Kollisionspunkt bewegen, bevor ich ihn drehe. Und als ich versuchte, die Position mit einer Rotationsmatrix zu drehen, war die neue Position nicht genau (aber fast) Rr vom Mittelpunkt des großen Kreises entfernt, aber dieser winzige Unterschied reichte aus, um meinen Kollisionstest beim nächsten Lauf zum Scheitern zu bringen. Muss ich die Position drehen, um die neue zu finden? Ist es nicht möglich, Vektoroperationen wie Sie zu verwenden, wenn etwas mit einer geraden Wand kollidiert?
Dbostream

4

Angenommen, der große Kreis ist Kreis A und der kleine Kreis ist Kreis B.

Überprüfen Sie, ob sich B in A befindet:

distance = sqrt((B.x - A.x)^2 + (B.y - A.y)^2))
if(distance > A.Radius + B.Radius) { // B is outside A }

Wenn in Bild n-1B innerhalb von A und in Bild nB außerhalb von A war und die Zeit zwischen den Bildern nicht zu groß war (auch bekannt als B bewegte sich nicht zu schnell), können wir den Kollisionspunkt approximieren, indem wir nur die kartesischen Koordinaten von B relativ ermitteln zu einer:

collision.X = B.X - A.X;
collision.Y = B.Y - A.Y;

Wir können diese Punkte dann in einen Winkel umwandeln:

collision.Normalize(); //not 100% certain if this step is necessary     
radians = atan2(collision.Y, collision.X)

Wenn Sie zum tersten Mal genauer wissen möchten, an welcher Stelle B außerhalb von A liegt, können Sie in jedem Frame eine Strahlenkreuzung durchführen und dann vergleichen, ob die Entfernung von B zum Kollisionspunkt größer ist, als die Entfernung, die B zurücklegen kann momentane Geschwindigkeit. In diesem Fall können Sie den genauen Zeitpunkt der Kollision berechnen.


Danke, aber ist es wirklich richtig, bei diesem Schnittpunkttest einen Strahl aus der Mitte des kleinen Kreises zu schießen? Werden wir nicht mit dem Szenario in der Mitte dieses Bildes enden ? Ich meine, der erste Punkt auf dem Bogen des kleinen Kreises, der mit dem großen Kreis kollidiert, ist nicht unbedingt der Punkt auf dem Bogen in Geschwindigkeitsrichtung. Ich glaube, ich brauche so etwas wie im unteren Szenario des Bildes, mit dem ich verlinkt habe. Ich habe im ersten Beitrag ein neues Bild hinzugefügt, das ein Beispiel dafür zeigt, was ich für nötig halte.
Dbostream

Hmm ich nehme an das Szenario ist möglich. Vielleicht testen Sie mit einem neuen Kreis C, der B.Radius + die maximale Bewegung von B in diesem Rahmen hat, prüfen Sie, ob dieser mit A kollidiert, und trainieren Sie dann den Punkt auf C, der weiter von A entfernt ist. (Habe es übrigens nicht versucht)
Roy T.

3
Mit weniger Worten: Wenn (Entfernung (A, B))> (Ra-Rb) eine Kollision auftritt und Sie nur den kleinen Kreis bewegen, um eine Entfernung zu erhalten, die Ra-Rb entspricht. Andernfalls bewegen Sie den kleinen Kreis normal. Übrigens, @dbostream, Sie verwenden etwas Ähnliches wie eine vereinfachte Form spekulativer Kontakte. Versuchen Sie, danach zu suchen.
Darkwings

@Darkwings +1 du hast so absolut Recht, und das klingt viel einfacher!
Roy T.

Es klingt einfach, weil ich die gesamte benötigte Basisgeometrie entfernt habe. Anstatt es "Kollision" zu nennen, hätten Sie es "boundAB" nennen können, da es tatsächlich so ist: der freie Vektor AB, der an (0,0) gebunden ist. Sobald Sie es normalisiert haben, erhalten Sie sowohl die Gleichung für das Bündel von geraden Linien parallel zu AB als auch einen nützlichen Einheitsvektor. Dann können Sie diesen Einheitsvektor für jeden Abstand D multiplizieren und die neu gefundenen Parameter zu A addieren, um den gewünschten Kollisionspunkt zu finden: C (Ax + Dx, Ay + Dy). Jetzt klingt es komplizierter, aber es ist das gleiche: P
Darkwings

0

Sei (Xa, Ya) die Position des großen Kreises und sein Radius R und (Xb, Yb) die Position des kleineren Kreises und sein Radius r.

Sie können überprüfen, ob diese beiden Kreise kollidieren, wenn

DistanceTest = sqrt(((Xa - Xb) ^ 2) + ((Ya - Yb) ^ 2)) >= (R - r)

Um die Position der Kollision herauszufinden, ermitteln Sie den genauen Zeitpunkt der Kollision der Kreise mithilfe einer binären Suche, jedoch mit einer festen Anzahl von Schritten. Abhängig davon, wie Ihr Spiel erstellt wurde, können Sie diesen Teil des Codes optimieren (ich habe diese Lösung bereitgestellt, um unabhängig vom Verhalten des kleinen Balls zu sein. Wenn er eine konstante Beschleunigung oder Geschwindigkeit aufweist, kann dieser Teil des Codes optimiert werden und durch eine einfache Formel ersetzt).

left = 0 //the frame with no collision
right = 1 //the frame after collision
steps = 8 //or some other value, depending on how accurate you want it to be
while (steps > 0)
    checktime = left + (right - left) / 2
    if DistanceTest(checktime) is inside circle //if at the moment in the middle of the interval [left;right] the small circle is still inside the bigger one
        then left = checktime //the collision is after this moment of time
        else right = checktime //the collision is before
    steps -= 1
finaltime = left + (right - left) / 2 // the moment of time will be somewhere in between, so we take the moment in the middle of interval to have a small overall error

Wenn Sie die Kollisionszeit kennen, berechnen Sie die Positionen der beiden Kreise zum endgültigen Zeitpunkt und der endgültige Kollisionspunkt ist

CollisionX = (Xb - Xa)*R/(R-r) + Xa
CollisionY = (Yb - Ya)*R/(R-r) + Ya

0

Ich habe eine Demo eines Balls implementiert, der in einem Kreis auf jsfiddle mit dem von Sam Hocevar beschriebenen Algorithmus springt :

http://jsfiddle.net/klenwell/3ZdXf/

Hier ist das Javascript, das den Kontaktpunkt identifiziert:

find_contact_point: function(world, ball) {
    // see /gamedev//a/29658
    var A = world.point();
    var B = ball.point().subtract(ball.velocity());
    var C = ball.point();
    var R = world.r;
    var r = ball.r;

    var AB = B.subtract(A);
    var BC = C.subtract(B);
    var AB_len = AB.get_length();
    var BC_len = BC.get_length();

    var b = AB.dot(BC) / Math.pow(BC_len, 2) * -1;
    var c = (Math.pow(AB_len, 2) - Math.pow(R - r, 2)) / Math.pow(BC_len, 2);
    var d = b * b - c;
    var k = b - Math.sqrt(d);

    if ( k < 0 ) {
        k = b + Math.sqrt(d);
    }

    var BD = C.subtract(B);
    var BD_len = BC_len * k;
    BD.set_length(BD_len);

    var D = B.add(BD);
    return D;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.