Comment déplacer un objet le long de la circonférence d'un autre objet?

11

Je suis tellement à court de maths que ça fait mal, mais pour certains d'entre vous, cela devrait être un morceau de gâteau. Je veux déplacer un objet autour d'un autre le long de son âge ou de sa circonférence sur une simple trajectoire circulaire. À l'heure actuelle, mon algorithme de jeu sait comment déplacer et positionner un sprite juste au bord d'un obstacle et maintenant il attend que le point suivant se déplace en fonction de diverses conditions.

Donc, le problème mathématique ici est de savoir comment obtenir les positions (aX, aY) et (bX, bY) , quand je connais le centre (cX, cY), la position de l'objet (oX, oY) et la distance requise pour se déplacer (d)

entrez la description de l'image ici

Lumis
la source
1
Est-ce dune distance linéaire ou est-ce un arc?
MichaelHouse
C'est une distance linéaire en pixels
Lumis
Connaissez-vous vraiment les vecteurs et leurs opérations de base?
Patrick Hughes
@Patrick Non, je suppose que je vais devoir suivre un cours sur les vecteurs. Comme il s'agit d'une animation image par image, le code doit être rapide et optimisé.
Lumis

Réponses:

8

( CAVEAT: j'utilise deux approximations ici: la première prend d comme longueur d'arc et la seconde prend comme longueur orthogonale. Ces deux approximations devraient être bonnes pour des valeurs relativement petites de d, mais elles ne remplissent pas la question précise telle que clarifiée dans les commentaires.)

Heureusement, les calculs à ce sujet sont relativement simples. Tout d'abord, nous pouvons trouver le vecteur relatif de notre position centrale à notre position actuelle:

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

Et une fois que nous avons ce vecteur relatif, nous pouvons alors connaître le rayon du cercle sur lequel nous travaillons en trouvant sa longueur:

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

De plus, à partir de notre vecteur relatif, nous pouvons trouver l'angle précis de la ligne de cX à oX:

curTheta = atan2(deltaX, deltaY);

Maintenant, les choses deviennent un peu plus compliquées. Tout d'abord, comprenez que la circonférence d'un cercle - c'est-à-dire la «longueur d'arc» d'un arc avec une mesure angulaire de 2π - est de 2πr. En général, la longueur d'arc d'un arc avec une mesure angulaire de θ le long d'un cercle de rayon r est juste θr. Si nous devions utiliser le d dans votre diagramme comme longueur d'arc, et puisque nous connaissons le rayon, nous pouvons trouver le changement de thêta pour nous amener à la nouvelle position en divisant simplement:

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

Dans le cas où d doit être une distance linéaire, les choses sont un peu plus compliquées, mais heureusement pas beaucoup. Là, d est un côté d'un triangle isocèle dont les deux autres côtés sont le rayon du cercle (de cX / cY à oX / oY et aX / aY respectivement), et la bissection de ce triangle isocèle nous donne deux triangles rectangles, dont chacun a d / 2 comme un côté et un rayon comme l'hypoténuse; cela signifie que le sinus de la moitié de notre angle est (d / 2) / rayon, et donc l'angle complet est juste le double de ceci:

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

Remarquez comment si vous retiriez l'asin de cette formule et annuliez les 2, ce serait la même chose que la dernière formule; cela revient à dire que sin (x) est approximativement x pour de petites valeurs de x, ce qui est une approximation utile à connaître.

Maintenant, nous pouvons trouver le nouvel angle en ajoutant ou en soustrayant simplement:

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

Une fois que nous avons le nouvel angle, nous pouvons utiliser un trig de base pour trouver notre vecteur relatif mis à jour:

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

et à partir de notre position centrale et de notre vecteur relatif, nous pouvons (enfin) trouver le point cible:

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

Maintenant, avec tout cela, il y a de grandes mises en garde à prendre en compte. D'une part, vous remarquerez que ce calcul est principalement à virgule flottante, et en fait il doit presque l'être; essayer d'utiliser cette méthode pour mettre à jour dans une boucle et arrondir à des valeurs entières à chaque étape peut faire tout, de faire que votre cercle ne se ferme pas (soit en spirale vers l'intérieur ou vers l'extérieur chaque fois que vous faites le tour de la boucle) jusqu'à ne pas le démarrer dans la première endroit! (Si votre d est trop petit, vous découvrirez peut-être que les versions arrondies de aX / aY ou bX / bY sont exactement là où se trouvait votre position de départ oX / oY.) Pour un autre, cela coûte très cher, surtout pour ce qu'il essaie de faire; en général, si vous savez que votre personnage va se déplacer dans un arc circulaire, vous devez planifier tout l'arc à l'avance et noncochez-le d'une image à l'autre comme ceci, car la plupart des calculs les plus coûteux peuvent être téléchargés en amont pour réduire les coûts. Un autre bon moyen de réduire les coûts, si vous voulez vraiment mettre à jour progressivement comme ceci, est de ne pas utiliser trig en premier lieu; si d est petit et que vous n'en avez pas besoin pour être exact mais juste très proche, alors vous pouvez faire une `` astuce '' en ajoutant un vecteur de longueur d à oX / oY, orthogonal au vecteur vers votre centre (notez qu'un le vecteur orthogonal à (dX, dY) est donné par (-dY, dX)), puis le réduire à la bonne longueur. Je ne vais pas expliquer ce code pas à pas, mais j'espère que cela aura du sens compte tenu de ce que vous avez vu jusqu'à présent. Notez que nous «rétrécissons» le nouveau vecteur delta implicitement à la dernière étape,

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
la source
1
Steven Je pense que je vais d'abord essayer l'approximation car c'est juste un jeu où il est plus important de se sentir naturel et intéressant que précis. La vitesse est également importante. Merci pour ce long et bon tutoriel!
Lumis
Wow, Steven, votre approximation fonctionne comme un rêve! Pouvez-vous me dire comment changer votre code pour obtenir bX, bY. Je ne suis pas encore clair sur votre concept orthogonal ...
Lumis
2
Sûr! Vous voudrez vraiment comprendre les mathématiques vectorielles à un moment donné, et une fois que vous l'avez fait, je pense que ce sera une seconde nature; pour obtenir bX / bY, il suffit de faire «l'inverse» - en d'autres termes, plutôt que d'ajouter le vecteur orthogonal (particulier), il suffit de le soustraire. En termes de code ci-dessus, ce serait «newDeltaX = deltaX-orthoX; newDeltaY = deltaY-orthoY; ', suivi du même calcul de newLength, puis' bX = cX + newDeltaX radius / newLength; bY = cY + rayon NewDeltaY / newLength; '.
Steven Stadnicki
Fondamentalement, ce code pointerait newDeltaX / newDeltaY dans la direction de bX / bY (au lieu de dans la direction de aX / aY), puis couper pour s'adapter et ajouter au centre comme vous obtiendrez aX / aY.
Steven Stadnicki
9

Formez un triangle en utilisant les deux côtés que vous avez déjà (un côté est de «c» à «o», l'autre de «o» à «a»), et le troisième côté va de «a» à «c». Vous ne savez pas encore où est «a», imaginez simplement qu'il y a un point pour l'instant. Vous aurez besoin de trigonométrie pour calculer l'angle de l'angle opposé au côté «d». Vous avez la longueur des côtés c <-> o et c <-> a, car ils sont tous les deux le rayon du cercle.

Maintenant que vous avez la longueur des trois côtés de ce triangle que vous ne pouvez pas encore voir, vous pouvez déterminer l'angle opposé au côté «d» du triangle. Voici la formule SSS (side-side-side) si vous en avez besoin: http://www.teacherschoice.com.au/maths_library/trigonometry/solve_trig_sss.htm

En utilisant la formule SSS, vous avez l'angle (que nous appellerons «j») qui est opposé au côté «d». Donc, maintenant, nous pouvons calculer (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) );

Assurez-vous que les angles que vous calculez sont toujours en radians.

Si vous devez calculer le rayon du cercle, vous pouvez utiliser la soustraction vectorielle, soustraire le point «c» du point «o», puis obtenir la longueur du vecteur résultant.

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

float radius = Math.sqrt(lengthSquared);

Quelque chose comme ça devrait faire, je crois. Je ne connais pas Java, j'ai donc deviné la syntaxe exacte.

Voici l'image donnée par l'utilisateur Byte56pour illustrer à quoi pourrait ressembler ce triangle: triangle de cao

Nic Foster
la source
1
Je faisais une réponse, mais c'est tout. Vous êtes invités à utiliser l'image que j'ai créée
MichaelHouse
@ Byte56: Merci, je n'avais aucune poignée d'éditeur d'image pour illustrer.
Nic Foster
Notez que le rayon doit également être calculé; il devrait y avoir des façons plus simples d'obtenir j que le calcul SSS complet, car nous avons un triangle isocèle.)
Steven Stadnicki
Oui, cela me semble simple, même pour moi! Android n'a pas Vector2, donc je suppose que je peux simplement utiliser les valeurs séparément. De manière intéressante, j'ai trouvé la classe Vector2 créée manuellement pour Android ici: code.google.com/p/beginning-android-games-2/source/browse/trunk/…
Lumis
(J'ai modifié ma propre réponse pour trouver la bonne distance linéaire - le deuxième calcul de deltaTheta là-bas, comme 2 * asin (d / (2 * rayon)), est la façon dont vous trouveriez j ici.)
Steven Stadnicki
2

Pour faire tourner obj2 autour de obj1, essayez peut-être:

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
la source
Cela ne montre pas comment obtenir l'angle en premier lieu, et vous semblez montrer comment orbiter, au lieu de trouver les coordonnées comme le demande la question.
MichaelHouse
1
Lewis, merci d'avoir montré comment faire tourner un objet autour d'un point. Cela peut s'avérer utile ...
Lumis