Come spostare un oggetto lungo una circonferenza di un altro oggetto?


11

Sono così fuori dalla matematica che fa male, ma per alcuni di voi questo dovrebbe essere un gioco da ragazzi. Voglio spostare un oggetto attorno a un altro lungo le sue età o la sua circonferenza su un semplice percorso circolare. Al momento il mio algoritmo di gioco sa come muovere e posizionare uno sprite proprio sul bordo di un ostacolo e ora aspetta che si muova il punto successivo a seconda delle varie condizioni.

Quindi il problema matematico qui è come ottenere posizioni (aX, aY) e (bX, bY) , quando conosco il centro (cX, cY), la posizione dell'oggetto (oX, oY) e la distanza richiesta per spostarsi (d)

inserisci qui la descrizione dell'immagine


1
È duna distanza lineare o è un arco?
MichaelHouse

È una distanza lineare in pixel
Lumis,

Conoscete bene quali sono i vettori e le operazioni di base su di essi?
Patrick Hughes,

@Patrick No, immagino che dovrò fare un corso sui vettori. Poiché si tratta di un'animazione fotogramma per fotogramma, il codice dovrebbe essere veloce e ottimizzato.
Lumis,

Risposte:


8

( CAVEAT: sto usando due approssimazioni qui: la prima prende d come lunghezza d'arco, e la seconda la prende come lunghezza ortogonale. Entrambe queste approssimazioni dovrebbero essere buone per valori relativamente piccoli di d, ma non soddisfano la domanda precisa come chiarito nei commenti.)

La matematica su questo, per fortuna, è relativamente semplice. Prima di tutto, possiamo trovare il vettore relativo dalla nostra posizione centrale alla nostra posizione corrente:

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

E una volta che abbiamo questo vettore relativo, allora possiamo conoscere il raggio del cerchio su cui stiamo lavorando trovandone la lunghezza:

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

Inoltre, dal nostro vettore relativo possiamo trovare l'angolo preciso in cui si trova la linea da cX a oX:

curTheta = atan2(deltaX, deltaY);

Ora le cose diventano un po 'più complicate. Prima di tutto, capire che la circonferenza di un cerchio - cioè la "lunghezza dell'arco" di un arco con una misura angolare di 2π - è 2πr. In generale, la lunghezza dell'arco di un arco con una misura angolare di θ lungo un cerchio di raggio r è solo θr. Se dovessimo usare la d nel diagramma come lunghezza dell'arco e poiché conosciamo il raggio, possiamo trovare il cambiamento in theta per portarci nella nuova posizione semplicemente dividendo:

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

Nel caso in cui d debba essere una distanza lineare, le cose sono un po 'più complicate, ma fortunatamente non molto. Lì, d è un lato di un triangolo isocele i cui altri due lati sono il raggio del cerchio (rispettivamente da cX / cY a oX / oY e aX / aY), e bisecare questo triangolo isocele ci dà due triangoli retti, ognuno dei quali ha d / 2 come un lato e raggio come ipotenusa; questo significa che il seno di metà del nostro angolo è (d / 2) / raggio, e quindi l'angolo completo è solo il doppio di questo:

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

Nota come se prendessi il asin da questa formula e cancellassi i 2, questo sarebbe lo stesso dell'ultima formula; questo equivale a dire che sin (x) è approssimativamente x per piccoli valori di x, che è un'approssimazione utile da sapere.

Ora possiamo trovare il nuovo angolo semplicemente aggiungendo o sottraendo:

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

Una volta che abbiamo il nuovo angolo, possiamo usare alcuni trig di base per trovare il nostro vettore relativo aggiornato:

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

e dalla nostra posizione centrale e dal nostro vettore relativo possiamo (finalmente) trovare il punto target:

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

Ora, con tutto ciò, ci sono alcuni grandi avvertimenti di cui essere consapevoli. Per uno, noterai che questa matematica è per lo più a virgola mobile, e in effetti deve quasi esserlo; provare ad usare questo metodo per aggiornare in un ciclo e arrotondare indietro a valori interi in ogni passaggio può fare di tutto, dal non chiudere il cerchio (o spirale verso l'interno o verso l'esterno ogni volta che fai il giro del ciclo) al non avviarlo nel primo posto! (Se la tua d è troppo piccola, potresti scoprire che le versioni arrotondate di aX / aY o bX / bY sono esattamente dove si trovava la posizione iniziale oX / oY.) Per un altro, questo è molto costoso, specialmente per quello che sta cercando di fare; in generale, se sai che il tuo personaggio si muoverà in un arco circolare, dovresti pianificare in anticipo l'intero arco e nonspuntalo da un fotogramma all'altro in questo modo, poiché molti dei calcoli più costosi qui possono essere caricati frontalmente per ridurre i costi. Un altro buon modo per ridurre i costi, se si desidera davvero aggiornare in modo incrementale in questo modo, è di non utilizzare il trig in primo luogo; se d è piccolo e non è necessario che sia esatto ma molto vicino, puoi fare un "trucco" aggiungendo un vettore di lunghezza d a oX / oY, ortogonale al vettore verso il tuo centro (nota che un il vettore ortogonale a (dX, dY) è dato da (-dY, dX)), quindi lo riduce alla giusta lunghezza. Non spiegherò questo codice così passo dopo passo, ma spero che abbia senso visto quello che hai visto finora. Tieni presente che "riduciamo" implicitamente il nuovo vettore delta nell'ultimo passaggio,

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;

1
Steven Penso che proverò prima l'approssimazione poiché questo è solo un gioco in cui è più importante sentirsi naturali e interessanti che precisi. Anche la velocità conta. Grazie per questo tutorial lungo e buono!
Lumis,

Caspita, Steven la tua approssimazione funziona come un sogno! Puoi dirmi come modificare il codice per ottenere bX, bY. Non sono ancora chiaro sul tuo concetto ortogonale ...
Lumis,

2
Sicuro! Avrai davvero bisogno di capire la matematica vettoriale ad un certo punto, e una volta che lo faccio sospetto che questa sarà una seconda natura; per ottenere bX / bY devi solo "fare il contrario" - in altre parole, invece di aggiungere il (particolare) vettore ortogonale, sottralo. In termini di codice sopra, questo sarebbe 'newDeltaX = deltaX-orthoX; newDeltaY = deltaY-orthoY; ', seguito dallo stesso calcolo di newLength, e quindi' bX = cX + newDeltaX radius / newLength; bY = cY + newDeltaY radius / newLength; '.
Steven Stadnicki,

Fondamentalmente, quel codice punterebbe newDeltaX / newDeltaY nella direzione di bX / bY (invece che nella direzione di aX / aY), quindi tagliare per adattarsi e aggiungere al centro proprio come si otterrebbe aX / aY.
Steven Stadnicki,

9

Forma un triangolo usando i due lati che hai già (un lato va da 'c' a 'o', l'altro da 'o' a 'a') e il terzo lato va da 'a' a 'c'. Non sai ancora dove sia una "a", immagina che ci sia un punto per ora. Avrai bisogno della trigonometria per calcolare l'angolo dell'angolo opposto al lato 'd'. Hai la lunghezza dei lati c <-> o e c <-> a, perché sono entrambi il raggio del cerchio.

Ora che hai la lunghezza dei tre lati di questo triangolo che non puoi ancora vedere, puoi determinare l'angolo opposto al lato 'd' del triangolo. Ecco la formula SSS (side-side-side) se hai bisogno: http://www.teacherschoice.com.au/maths_library/trigonometry/solve_trig_sss.htm

Usando la formula SSS hai l'angolo (che chiameremo 'j') che è opposto al lato 'd'. Quindi, ora possiamo calcolare (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) );

Assicurati che gli angoli che stai calcolando siano sempre in radianti.

Se è necessario calcolare il raggio del cerchio, è possibile utilizzare la sottrazione vettoriale, sottrarre il punto 'c' dal punto 'o', quindi ottenere la lunghezza del vettore risultante.

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

float radius = Math.sqrt(lengthSquared);

Qualcosa del genere dovrebbe fare, credo. Non conosco Java, quindi ho indovinato la sintassi esatta.

Ecco l'immagine fornita dall'utente Byte56per illustrare come potrebbe apparire questo triangolo: triangolo cao


1
Stavo rispondendo, ma è tutto qui. Puoi usare l'immagine che ho creato :) i.imgur.com/UUBgM.png
MichaelHouse

@ Byte56: Grazie, non ho avuto alcun editor di immagini per illustrare.
Nic Foster,

Si noti che anche il raggio deve essere calcolato; ci dovrebbero essere modi più semplici per ottenere j rispetto al calcolo SSS completo, dato che abbiamo un triangolo isocele.)
Steven Stadnicki,

Sì, sembra semplice, anche a me! Android non ha Vector2, quindi immagino di poter usare i valori separatamente. Stranamente ho trovato la classe Vector2 creata manualmente per Android qui: code.google.com/p/beginning-android-games-2/source/browse/trunk/…
Lumis,

(Ho ottimizzato la mia risposta per trovare la distanza lineare corretta - il secondo calcolo di deltaTheta lì, come 2 * asin (d / (2 * radius)), è come si troverà j qui.)
Steven Stadnicki il

3

Per far ruotare obj2 attorno a obj1, forse prova:

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;

Questo non mostra come ottenere l'angolo in primo luogo e sembra che tu stia mostrando come orbitare, invece di trovare le coordinate come fa la domanda.
MichaelHouse

1
Lewis, grazie per aver mostrato come orbitare un oggetto attorno a un punto. Potrebbe esserti utile ...
Lumis,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.