Comment puis-je intercepter un objet avec un mouvement circulaire

23

Je crée un jeu spatial 2D et je dois faire en sorte que le vaisseau spatial intercepte une planète. J'ai un code de travail pour les interceptions en ligne droite, mais je ne sais pas comment calculer l'emplacement des planètes sur une orbite circulaire.

Le jeu n'est pas scientifiquement précis, donc je ne m'inquiète pas de l'inertie, de la gravité, des orbites elliptiques, etc.

Je connais l'emplacement et la vitesse des vaisseaux spatiaux ainsi que l'orbite des planètes (Radius) et la vitesse

entrez la description de l'image ici

Ausa
la source
1
Non, j'essaie de calculer l'angle dont le navire a besoin pour se déplacer afin d'intercepter la planète.
Ausa
4
Cela fonctionnerait probablement mieux dans math.stackexchange.com ..
Jari Komppa
2
Votre navire est-il capable de changer de vitesse et de direction, ou est-ce constant? De plus, cette question sur le fait d'éviter que des missiles n'entourent une cible pourrait être utile.
thegrinner
4
Pour clarifier, est la situation? données pour la planète: centre de l'orbite, rayon de l'orbite, vitesse angulaire, emplacement actuel; pour le navire : emplacement actuel, vitesse actuelle; déterminer la direction du mouvement du navire afin d'intercepter la planète
AakashM
6
Comme une note historique intéressante: les planètes tournent généralement dans la même direction que leur orbite, qui est donc également dans le sens inverse des aiguilles d'une montre vu du dessus de l'hémisphère nord. De ce fait, nous pouvons déduire que les cadrans solaires ont été inventés dans l'hémisphère nord . Cadrans solaires ont -elles été inventées dans l'hémisphère sud alors dans le sens horaire serait l' autre façon.
Eric Lippert

Réponses:

3

Une solution analytique à cela est difficile, mais nous pouvons utiliser la recherche binaire pour trouver une solution avec la précision requise.

Le navire peut atteindre le point le plus proche sur l'orbite dans le temps t_min :

shipOrbitRadius = (ship.position - planet.orbitCenter).length;
shortestDistance = abs(shipOrbitRadius - planet.orbitRadius);
t_min = shortestDistance/ship.maxSpeed;

Le navire peut atteindre N'IMPORTE QUEL point sur l'orbite dans un temps inférieur ou égal à t_max :

(Ici, pour plus de simplicité, je suppose que le navire peut traverser le soleil. Si vous voulez éviter cela, vous devrez passer à des chemins non linéaires dans au moins certains cas. Les "cercles de baisers" peuvent sembler agréables et orbitaux. mécanique-y, sans changer l'algorithme de plus d'un facteur constant)

if(shipOrbitRadius > planet.orbitRadius)
{
   t_max = planet.orbitRadius * 2/ship.maxSpeed + t_min;
}
else
{
   t_max = planet.orbitRadius * 2/ship.maxSpeed - t_min;
}

Si notre période orbitale est courte, nous pourrions être en mesure d'améliorer cette limite supérieure en choisissant la t_maxpour la première fois après t_minque la planète s'approche le plus près de la position de départ du navire. Prenez la plus t_maxpetite de ces deux valeurs de . Voir cette réponse ultérieure pour une dérivation de pourquoi cela fonctionne.

Maintenant, nous pouvons utiliser la recherche binaire entre ces extrêmes, t_min et t_max . Nous rechercherons une valeur t qui ramènera l' erreur près de zéro:

error = (planet.positionAtTime(t) - ship.position).squareMagnitude/(ship.maxSpeed*ship.maxSpeed) - t*t;

(En utilisant cette construction, erreur @ t_min> = 0 et erreur @ t_max <= 0, donc il doit y avoir au moins une interception avec erreur = 0 pour une valeur t entre les deux)

où, pour être complet, la fonction de position est quelque chose comme ...

Vector2 Planet.positionAtTime(float t)
{
  angle = atan2(startPosition - orbitCenter) + t * orbitalSpeedInRadians;
  return new Vector2(cos(angle), sin(angle)) * orbitRadius + orbitCenter;
}

Notez que si la période orbitale de la planète est très courte par rapport à la vitesse du navire, cette fonction d'erreur peut changer plusieurs fois les signes sur la plage de t_min à t_max. Gardez simplement la trace de la première paire + ve & -ve que vous rencontrez et continuez à chercher entre elles jusqu'à ce que l'erreur soit assez proche de zéro ("assez proche" étant sensible à vos unités et au contexte de jeu. Le carré de la moitié de la durée de l'image peut fonctionne bien - qui garantit que l'interception est précise dans un cadre)

Une fois que vous avez un joli t minimisant les erreurs, vous pouvez simplement pointer le vaisseau sur planet.positionAtTime (t) et aller à plein régime, confiant que la planète atteindra ce point en même temps que vous.

Vous pouvez toujours trouver une solution dans les itérations Log_2 ((2 * orbitRadius / ship.maxSpeed) / errorThreshold). Ainsi, par exemple, si mon vaisseau peut traverser l'orbite en 60 images et que je souhaite une interception précise à l'intérieur d'une image, j'aurai besoin d'environ 6 itérations.

DMGregory
la source
1
Beaucoup de bonnes réponses ici, également quelques options alternatives intéressantes, mais d'après ce que j'avais déjà, ces solutions semblent les meilleures pour mon cas. J'ai créé une petite démo JavaScript de mes résultats. Démo
Ausa
11

Ne compliquons pas trop cela. Ce n'est pas une solution "parfaite" mais devrait fonctionner pour la plupart des jeux et toute imperfection devrait être invisible pour le joueur.

if(!OldTargetPoint)
  TargetPoint = PlanetPosition;
else
  TargetPoint = OldTargetPoint;
Distance = CurPosition - TargetPoint;
TimeNeeded = Distance / Speed;
TargetPoint = PlanetPositionInFuture(TimeNeeded);
SteerTowards(TargetPoint);
[...repeat this every AI update, for example every second...]
  1. Calculez le temps nécessaire pour atteindre le point cible.
  2. Calculez à quelle position la planète sera à l'heure calculée.
  3. Avancez vers le point calculé.
  4. Répéter

Cela fonctionne parce que plus le vaisseau spatial se rapproche, plus l'erreur devient faible. Le calcul devient donc plus stable dans le temps.

L'erreur est la différence entre le temps nécessaire calculé pour atteindre la planète (TimeNeeded) et le temps réel nécessaire pour atteindre la planète (après avoir pris en compte le nouveau TargetPoint).

API-Beast
la source
1
Vous voudrez peut-être exécuter 2 itérations de cela lors du démarrage d'un cours d'interception, sinon vous pouvez voir le navire scintiller momentanément entre deux directions (la deuxième supposition peut être bien meilleure que la première et entraîner un cap très différent - en particulier si le navire est proche ou à l'intérieur de l'orbite de la planète)
DMGregory
1
@DMGregory Oh! Nous pourrions simplement prendre la position actuelle de la planète au lieu du centre orbital comme point de départ. Lorsque nous sommes proches, c'est beaucoup plus proche, si nous sommes loin, cela n'a pas d'importance.
API-Beast
Il convient également de noter que cela fonctionne mieux lorsque la planète se déplace lentement par rapport au navire. Si la vitesse de la planète est comparable ou supérieure à celle du navire, vous pouvez voir des oscillations sur la trajectoire du navire. À des rapports de vitesse pathologiques, le navire peut chasser la planète pour toujours sur une orbite concentrique. Si vos planètes sont rapides et que vous remarquez que cela se produit, vous voudrez peut-être planifier tout votre parcours d'interception à l'avance plutôt que d'itérer à mi-vol.
DMGregory
3

Commençons par jeter un œil aux mathématiques derrière le problème.

Étape 1:

Trouver l'intersection entre une ligne et une forme consiste simplement à insérer l'équation de la ligne dans l'équation de la forme, qui est un cercle dans ce cas.

Ligne coupant avec cercle

Prenez un cercle de centre c et de rayon r . Un point p est sur le cercle si

|pc|2=r2

Avec une ligne exprimée comme p=p0+μv (où v est un vecteur, http://en.wikipedia.org/wiki/Euclidean_vector ), vous insérez la ligne dans la formule du cercle et obtenez

|p0+μvc|2=r2

La distance au carré peut être réécrite en tant que produit scalaire ( http://en.wikipedia.org/wiki/Dot_product ).

(p0+μvc)(p0+μvc)=r2

a=cp0(μva)(μva)=r2

μ2(vv)2μ(av)+aa=r2

|v|=1

μ22μ(av)+|a|2r2=0

qui est une simple équation quadratique, et nous arrivons à la solution

μ=av+sqrt((av)2a2r2)

μ<0

μ=0

μ

Étape 2:

μ

Que pouvons-nous en faire? Eh bien, nous savons maintenant la distance que le navire doit parcourir et à quel point il finira!

p=p0+μvμv

Maintenant, tout ce qui reste à faire est de calculer où la planète devrait être lorsque le navire commence à se diriger vers son orbite. Ceci est facilement calculé avec des soi-disant coodinates polaires ( http://mathworld.wolfram.com/PolarCoordinates.html )

x=c+rcos(θ)

y=c+rsin(θ)

tangularVelocity

Sommaire

Choisissez une ligne pour votre vaisseau et exécutez le calcul pour voir s'il entre en collision avec l'orbite des planètes. Si c'est le cas, calculez le temps qu'il faudra pour arriver à ce point. Utilisez ce temps pour revenir en orbite à partir de ce point avec la planète pour calculer où la planète devrait être lorsque le navire commence à se déplacer.

Tholle
la source
8
Bonne analyse, mais elle ne semble pas répondre à la question (clarifiée dans un commentaire): "Non, j'essaie de calculer l'angle dont le navire a besoin pour se déplacer afin d'intercepter la planète." Vous prenez l'angle du navire comme une donnée et calculez la position de la planète, et non l'inverse.
Chaosed0
4
Je ne vais pas dévaloriser cela car c'est une analyse utile, mais je suis d'accord avec @ Chaosed0 qu'il ne répond pas à la question. Dans votre résumé, vous dites "Choisissez une ligne pour votre navire ..." mais choisir cette ligne est exactement la partie difficile.
Drake
1

Voici deux solutions légèrement «prêtes à l'emploi».

La question est: étant donné que le navire se déplace en ligne droite à une vitesse donnée, et que la planète se déplace dans un cercle de rayon donné à une vitesse angulaire donnée, et les positions de départ de la planète et du navire, déterminent le vecteur de direction du navire. une ligne droite doit être utilisée pour tracer une trajectoire d'interception.

Première solution: nier la prémisse de la question. La quantité qui est "glissable" dans la question est l'angle. Au lieu de cela, corrigez cela. Pointez le navire directement au centre de l'orbite.

  • Calculez la position à laquelle le navire rencontrera la planète; c'est facile.
  • Calculez la distance du navire à la position d'interception; aussi facile.
  • Calculez le temps qu'il faudra pour que la planète atteigne ensuite la position d'interception. Facile.
  • Divisez la distance du navire à l'interception par le temps jusqu'à ce que la planète arrive à l'interception.
  • Si celle-ci est inférieure ou égale à la vitesse maximale du navire, vous avez terminé. Réglez le navire se déplaçant à cette vitesse directement vers le soleil.
  • Sinon, ajoutez la période orbitale de la planète au temps et réessayez. Continuez à faire cela jusqu'à ce que vous obteniez une vitesse raisonnable pour le navire.

Deuxième solution: ne le faites pas du tout sur le pilote automatique. Faites un mini-jeu où le joueur doit utiliser des propulseurs pour s'approcher de la planète, et s'ils le frappent à une vitesse relative trop élevée, ils explosent, mais ils ont également un carburant limité. Faites apprendre au joueur comment résoudre le problème d'interception!

Eric Lippert
la source
1

(x,y,t)

tv=x2+y2

vest la vitesse du navire. On suppose que le navire commence à zéro.

La position de la planète dans l'espace et le temps peut être paramétrée par exemple

X=X0+rcos(wu+une)y=y0+rsjen(wu+une)t=u

u va de 0 vers le haut. w est la vitesse angulaire et une is the starting angle of the planet at time zero. Then solve where the ship and planet could meet in time and space. You get an equation for u to solve:

uv=(x0+rcos(wu+a))2+(y0+rsin(wu+a))2u2v2=(x0+rcos(wu+a))2+(y0+rsin(wu+a))2u2v2=x02+y02+r2+2x0rcos(wu+a)+2y0rsin(wu+a)

This equation needs to be solved numerically. It may have many solutions. By eyeballing it, it seems it always has a solution

Toni Makkonen
la source
1

Here's part of a solution. I didn't get to finish it in time. I'll try again later.

If I understand correctly, you have a planet's position & velocity, as well as a ship's position and speed. You want to get the ship's movement direction. I'm assuming the ship's and planet's speeds are constant. I also assume, without loss of generality, that the ship is at (0,0); to do this, subtract the ship's position from the planet's, and add the ship's position back onto the result of the operation described below.

Unfortunately, without latex, I can't format this answer very well, but we'll attempt to make do. Let:

  • s_s = the ship's speed (s_s.x, s_s.y, likewise)
  • s_a = the ship's bearing (angle of movement, what we want to calculate)
  • p_p = the planet's initial position, global coords
  • p_r = the planet's distance (radius) from the center of orbit, derivable from p_p
  • p_a = the planet's initial angle in radians, relative to the center of orbit
  • p_s = the planet's angular velocity (rad/sec)
  • t = the time to collision (this turns out to be something we must calculate as well)

Here's the equations for the position of the two bodies, broken down into components:

ship.x = s_s.x * t * cos(s_a)
ship.y = s_s.y * t * sin(s_a)

planet.x = p_r * cos(p_a + p_s * t) + p_p.x
planet.y = p_r * sin(p_a + p_s * t) + p_p.y

Since we want ship.x = planet.x and ship.y = planet.y at some instant t, we obtain this equation (the y case is nearly symmetrical):

   s_s.x * t * cos(s_a) = p_r * cos(p_a + p_s * t) + p_p.x
   s_s.y * t * sin(s_a) = p_r * sin(p_a + p_s * t) + p_p.y

Solving the top equation for s_a:

   s_s.x * t * cos(s_a) = p_r * cos(p_a + p_s * t) + p_p.x
=> s_a = arccos((p_r * cos(p_a + p_s * t) + p_p.x) / (s_s.x * t))

Substituting this into the second equation results in a fairly terrifying equation that Wolfram alpha won't solve for me. There may be a better way to do this not involving polar coordinates. If anyone wants to give this method a shot, you're welcome to it; I've made this a wiki. Otherwise, you may want to take this to the Math StackExchange.

Chaosed0
la source
2
I would love to have TeX enabled for this site. It would make some graphics related stuff (e.g. vector, matrices, quaternions..) easier to represent.
mvw
0

I would fix the location at which to intercept (graze the circle, at the "outgoing" side of the orbit.)

Now you just have to adjust the spaceship's speed so that planet and ship reach that point at the same time.

Note that the rendez-vous could be after N more orbits, depending how far away the ship is, and how fast the planet is orbiting the star.

Pick the N that in time, comes nearest to the ship's journey duration at current speed.

Then speed up or slow down ship to match the timestamp for those N orbits exactly.

In all this, the actual course is already known! Just not the speed.

Bram
la source
This could give unnecessarily long trips. Let's say we're positioned so that the planet is coming toward us and we can actually reach the "incoming" grazing point at the same time the planet does. If we're only looking at the "outgoing" grazing point, then we could end up spending half an extra half a year in transit!
DMGregory
True... depends on orbital speeds. But it also minimizes the delta-speed if you always graze at outgoing. At "incoming" you could burn up in the atmosphere, whereas in "outgoing" you are more likely to be matched. @DMGregory
Bram