Wie bewege ich ein Objekt entlang eines Umfangs eines anderen Objekts?

11

Ich habe so wenig Mathe, dass es weh tut, aber für einige von euch sollte dies ein Kinderspiel sein. Ich möchte ein Objekt entlang seines Alters oder Umfangs auf einer einfachen Kreisbahn um ein anderes bewegen. Im Moment weiß mein Spielalgorithmus, wie man ein Sprite direkt am Rand eines Hindernisses bewegt und positioniert, und wartet nun darauf, dass sich der nächste Punkt abhängig von verschiedenen Bedingungen bewegt.

Das mathematische Problem hier ist also, wie man (aX, aY) und (bX, bY) Positionen erhält , wenn ich das Zentrum (cX, cY), die Objektposition (oX, oY) und die Entfernung kenne, die erforderlich ist, um sich zu bewegen (d)

Geben Sie hier die Bildbeschreibung ein

Lumis
quelle
1
Ist dein linearer Abstand oder ein Bogen?
MichaelHouse
Es ist ein linearer Abstand in Pixel
Lumis
Kennen Sie sich überhaupt mit Vektoren und grundlegenden Operationen aus?
Patrick Hughes
@Patrick Nein, ich denke, ich muss einen Kurs über Vektoren machen. Da dies eine Frame-für-Frame-Animation ist, sollte der Code schnell und optimiert sein.
Lumis

Antworten:

8

( CAVEAT: Ich verwende hier zwei Näherungen: Die erste nimmt d als Bogenlänge und die zweite als orthogonale Länge. Beide Näherungen sollten für relativ kleine Werte von d gut sein, aber sie erfüllen nicht die genaue Frage, wie in den Kommentaren geklärt.)

Die Mathematik dazu ist glücklicherweise relativ einfach. Zunächst können wir den relativen Vektor von unserer Mittelposition zu unserer aktuellen Position finden:

deltaX = oX-cX;
deltaY = oY-cY;

Und sobald wir diesen relativen Vektor haben, können wir den Radius des Kreises, an dem wir arbeiten, ermitteln, indem wir dessen Länge ermitteln:

radius = sqrt(deltaX*deltaX+deltaY*deltaY);

Darüber hinaus können wir anhand unseres relativen Vektors den genauen Winkel ermitteln, in dem sich die Linie von cX nach oX befindet:

curTheta = atan2(deltaX, deltaY);

Jetzt wird es etwas schwieriger. Verstehen Sie zunächst, dass der Umfang eines Kreises - dh die 'Bogenlänge' eines Bogens mit einem Winkelmaß von 2π - 2πr beträgt. Im Allgemeinen beträgt die Bogenlänge eines Bogens mit einem Winkelmaß von θ entlang eines Kreises mit dem Radius r nur θr. Wenn wir das d in Ihrem Diagramm als Bogenlänge verwenden und den Radius kennen, können wir die Änderung in Theta finden, um an die neue Position zu gelangen, indem wir einfach teilen:

deltaTheta = d/radius; // treats d as a distance along the arc

Für den Fall, dass d ein linearer Abstand sein muss, sind die Dinge etwas komplizierter, aber zum Glück nicht viel. Dort ist d eine Seite eines Isozelendreiecks, dessen andere zwei Seiten der Radius des Kreises sind (von cX / cY zu oX / oY bzw. aX / aY), und die Halbierung dieses Isozelendreiecks ergibt zwei rechtwinklige Dreiecke, von denen jedes hat d / 2 als eine Seite und Radius als Hypotenuse; Dies bedeutet, dass der Sinus der Hälfte unseres Winkels (d / 2) / Radius ist und der volle Winkel nur doppelt so groß ist:

deltaTheta = 2*asin(d/(2*radius)); // treats d as a linear distance

Beachten Sie, dass wenn Sie das Asin aus dieser Formel herausnehmen und die 2s stornieren würden, dies dasselbe wäre wie die letzte Formel. Dies ist dasselbe wie zu sagen, dass sin (x) für kleine Werte von x ungefähr x ist, was eine nützliche Annäherung ist, um zu wissen.

Jetzt können wir den neuen Winkel finden, indem wir einfach addieren oder subtrahieren:

newTheta = curTheta+deltaTheta; // This will take you to aX, aY. For bX/bY, use curTheta-deltaTheta

Sobald wir den neuen Winkel haben, können wir einen grundlegenden Trigger verwenden, um unseren aktualisierten relativen Vektor zu finden:

newDeltaX = radius*cos(newTheta);
newDeltaY = radius*sin(newTheta);

und von unserer Mittelposition und unserem relativen Vektor können wir (endlich) den Zielpunkt finden:

aX = cX+newDeltaX;
aY = cY+newDeltaY;

Bei alledem sind einige große Vorbehalte zu beachten. Zum einen werden Sie feststellen, dass diese Mathematik meistens Gleitkomma ist, und tatsächlich muss es fast so sein; Wenn Sie versuchen, diese Methode zum Aktualisieren in einer Schleife zu verwenden und bei jedem Schritt auf ganzzahlige Werte zurückzurunden, kann dies dazu führen, dass Ihr Kreis nicht geschlossen wird (entweder jedes Mal, wenn Sie die Schleife umrunden, nach innen oder außen spiralförmig), bis er im ersten Schritt nicht gestartet wird Platz! (Wenn Ihr d zu klein ist, stellen Sie möglicherweise fest, dass die gerundeten Versionen von aX / aY oder bX / bY genau dort sind, wo Ihre Startposition oX / oY war.) Zum anderen ist dies sehr teuer, insbesondere für das, was es versucht tun; Wenn Sie wissen, dass sich Ihr Charakter in einem Kreisbogen bewegen wird, sollten Sie im Allgemeinen den gesamten Bogen im Voraus planen und nichtKreuzen Sie es so von Bild zu Bild an, da viele der teuersten Berechnungen hier vorab geladen werden können, um die Kosten zu senken. Eine andere gute Möglichkeit, die Kosten zu senken, wenn Sie wirklich schrittweise aktualisieren möchten, besteht darin, Trigger überhaupt nicht zu verwenden. Wenn d klein ist und Sie es nicht benötigen, um genau zu sein, sondern nur sehr nahe, können Sie einen 'Trick' ausführen, indem Sie einen Vektor der Länge d zu oX / oY hinzufügen, orthogonal zum Vektor in Richtung Ihrer Mitte (beachten Sie, dass a Der zu (dX, dY) orthogonale Vektor ist durch (-dY, dX) gegeben und wird dann auf die richtige Länge verkleinert. Ich werde diesen Code nicht so Schritt für Schritt erklären, aber hoffentlich macht er angesichts dessen, was Sie bisher gesehen haben, Sinn. Beachten Sie, dass wir den neuen Delta-Vektor im letzten Schritt implizit verkleinern.

deltaX = oX-cX; deltaY = oY-cY;
radius = sqrt(deltaX*deltaX+deltaY*deltaY);
orthoX = -deltaY*d/radius;
orthoY = deltaX*d/radius;
newDeltaX = deltaX+orthoX; newDeltaY = deltaY+orthoY;
newLength = sqrt(newDeltaX*newDeltaX+newDeltaY*newDeltaY);
aX = cX+newDeltaX*radius/newLength; aY = cY+newDeltaY*radius/newLength;
Steven Stadnicki
quelle
1
Steven Ich denke, ich werde zuerst die Annäherung versuchen, da dies nur ein Spiel ist, bei dem es wichtiger ist, sich natürlich und interessant zu fühlen als präzise. Auch Geschwindigkeit ist wichtig. Vielen Dank für dieses lange und gute Tutorial!
Lumis
Wow, Steven, deine Annäherung funktioniert wie ein Traum! Können Sie mir sagen, wie Sie Ihren Code ändern können, um bX, bY zu erhalten? Ich bin noch nicht klar über Ihr orthogonales Konzept ...
Lumis
2
Sicher! Sie werden irgendwann wirklich die Vektormathematik verstehen wollen, und wenn Sie dies einmal tun, vermute ich, dass dies eine zweite Natur sein wird. Um bX / bY zu erhalten, müssen Sie nur "umgekehrt" vorgehen - mit anderen Worten, anstatt den (bestimmten) orthogonalen Vektor zu addieren, subtrahieren Sie ihn einfach. In Bezug auf den obigen Code wäre dies 'newDeltaX = deltaX-orthoX; newDeltaY = deltaY-orthoY; ', gefolgt von der gleichen Berechnung von newLength und dann' bX = cX + newDeltaX radius / newLength; bY = cY + newDeltaY Radius / newLength; '.
Steven Stadnicki
Grundsätzlich würde dieser Code newDeltaX / newDeltaY in Richtung bX / bY zeigen (anstatt in Richtung aX / aY), dann zuschneiden und zur Mitte hinzufügen, genau wie Sie es tun würden, um aX / aY zu erhalten.
Steven Stadnicki
9

Bilden Sie ein Dreieck mit den beiden Seiten, die Sie bereits haben (eine Seite ist von 'c' bis 'o', die andere von 'o' bis 'a'), und die dritte Seite geht von 'a' bis 'c'. Sie wissen noch nicht, wo 'a' ist. Stellen Sie sich vor, es gibt vorerst einen Punkt. Sie benötigen Trigonometrie, um den Winkel des Winkels zu berechnen, der der Seite 'd' entgegengesetzt ist. Sie haben die Länge der Seiten c <-> o und c <-> a, weil beide der Radius des Kreises sind.

Nachdem Sie die Länge der drei Seiten dieses Dreiecks haben, die Sie noch nicht sehen können, können Sie den Winkel bestimmen, der der 'd'-Seite des Dreiecks entgegengesetzt ist. Hier ist die SSS-Formel (Side-Side-Side), falls erforderlich: http://www.teacherschoice.com.au/maths_library/trigonometry/solve_trig_sss.htm

Mit der SSS-Formel haben Sie den Winkel (den wir 'j' nennen), der der Seite 'd' entgegengesetzt ist. Jetzt können wir also berechnen (aX, aY).

// This is the angle from 'c' to 'o'
float angle = Math.atan2(oY - cY, oX - cX)

// Add the angle we calculated earlier.
angle += j;

Vector2 a = new Vector2( radius * Math.cos(angle), radius * Math.sin(angle) );

Stellen Sie sicher, dass die Winkel, die Sie berechnen, immer im Bogenmaß sind.

Wenn Sie den Radius des Kreises berechnen müssen, können Sie die Vektorsubtraktion verwenden, den Punkt 'c' vom Punkt 'o' subtrahieren und dann die Länge des resultierenden Vektors ermitteln.

float lengthSquared = ( inVect.x * inVect.x
                      + inVect.y * inVect.y
                      + inVect.z * inVect.z );

float radius = Math.sqrt(lengthSquared);

So etwas sollte reichen, glaube ich. Ich kenne Java nicht, daher habe ich die genaue Syntax erraten.

Hier ist das vom Benutzer gegebene Bild, um Byte56zu veranschaulichen, wie dieses Dreieck aussehen könnte: Cao Dreieck

Nic Foster
quelle
1
Ich habe eine Antwort gegeben, aber das ist es. Du kannst gerne das Bild verwenden, das ich gemacht habe :) i.imgur.com/UUBgM.png
MichaelHouse
@ Byte56: Danke, ich hatte kein Bildeditor-Handle zum Illustrieren.
Nic Foster
Beachten Sie, dass auch der Radius berechnet werden muss. Es sollte einfachere Möglichkeiten geben, j zu erhalten als die vollständige SSS-Berechnung, da wir ein Isozelen-Dreieck haben.)
Steven Stadnicki
Ja, das scheint mir einfach zu sein! Android hat kein Vector2, daher kann ich die Werte wohl separat verwenden. Interessanterweise fand ich die manuell für Android erstellte Vector2-Klasse hier: code.google.com/p/beginning-android-games-2/source/browse/trunk/…
Lumis
(Ich habe meine eigene Antwort angepasst, um den richtigen linearen Abstand zu finden. Die zweite Berechnung von deltaTheta als 2 * asin (d / (2 * Radius)) ist, wie Sie j hier finden würden.)
Steven Stadnicki
2

Um obj2 um obj1 drehen zu lassen, versuchen Sie vielleicht:

float angle = 0; //init angle

//call in an update
obj2.x = (obj1.x -= r*cos(angle));
obj2.y = (obj1.y += r*sin(angle));
angle-=0.5;
Lewis
quelle
Dies zeigt nicht, wie man den Winkel überhaupt erhält, und Sie scheinen zu zeigen, wie man umkreist, anstatt die Koordinaten wie in der Frage zu finden.
MichaelHouse
1
Lewis, danke, dass du gezeigt hast, wie man ein Objekt um einen Punkt kreist. Es kann nützlich sein ...
Lumis