Question

Je suis en œuvre un jeu en 2D avec des navires dans l'espace.

Pour le faire, j'utilise LÖVE, qui enveloppe Box2D avec Lua. Mais je crois que ma question peut répondre à toute personne ayant une meilleure compréhension de la physique que moi -. Si le code pseudo est accepté comme réponse

Mon problème est que je ne sais pas comment déplacer mes vaisseaux spatiaux correctement sur un monde physique compatible 2D. Plus concrètement:

Un navire de m masse est située à une position initiale {x, y}. Il a un vecteur de vitesse initiale de {vx, vy} (peut être {0,0}).

L'objectif est un point {xo,yo}. Le navire doit atteindre l'objectif ayant une vitesse de {vxo, vyo} (ou à proximité), suivant la trajectoire la plus courte.

Il y a une fonction appelée update(dt) qui est fréquemment appelée (à savoir 30 fois par seconde). Sur cette fonction, le navire peut modifier sa position et la trajectoire, en appliquant « impulsions » à lui-même. L'ampleur des impulsions est binaire: vous pouvez appliquer soit dans une direction donnée, ou de ne pas l'appliquer du tout). Dans le code, il ressemble à ceci:

function Ship:update(dt)
  m = self:getMass()
  x,y = self:getPosition()
  vx,vy = self:getLinearVelocity()
  xo,yo = self:getTargetPosition()
  vxo,vyo = self:getTargetVelocity()
  thrust = self:getThrust()

  if(???)
    angle = ???
    self:applyImpulse(math.sin(angle)*thrust, math.cos(angle)*thrust))
  end
end

La première ??? est là pour indiquer que, dans certaines occasions (je suppose), il serait préférable de « non à l'impulsion » et quitter le navire « dérive ». La deuxième partie est constituée ??? sur la façon de calculer l'angle d'impulsion sur une dt donnée.

Nous sommes dans l'espace, afin que nous puissions ignorer des choses comme la friction de l'air.

Bien qu'il serait très bien, je ne suis pas à la recherche d'une personne à coder pour moi; Je mets le code là pour mon problème est bien compris.

Ce que j'ai besoin est une stratégie - une façon d'attaquer cela. Je sais que certains physique de base, mais je ne suis pas expert. Par exemple, est-ce problème a un nom? Ce genre de chose.

Merci beaucoup.

EDIT:. Beta fourni une stratégie valable pour cela et juge aimablement mis en œuvre directement dans LÖVE, dans les commentaires

EDIT2: Après plus je googler également trouvé OpenSteer . Il est sur C ++, mais il fait ce que je faisais semblant. Il sera probablement utile à tous ceux qui arriver à cette question.

Était-ce utile?

La solution

Il appelle la planification de mouvement, et ce n'est pas trivial.

Voici un moyen simple d'obtenir une trajectoire non optimale:

  1. Arrêter. Appliquer une poussée opposée à la direction de la vitesse jusqu'à ce que la vitesse est nulle.
  2. Calculer la dernière étape, qui sera à l'opposé de la première, une poussée constante d'un départ permanent qui obtient le navire à X0 et v0. Le point de départ sera à une distance de | v0 | ^ 2 / (2 * poussée) de x0.
  3. arriver à ce point de départ (et ensuite faire la dernière étape). Obtenir d'un point à un autre debout est facile: poussée vers elle jusqu'à ce que vous êtes à mi-chemin, puis poussée vers l'arrière jusqu'à ce que vous arrêtez.

Si vous voulez une approche rapide et sale à une trajectoire optimale, vous pouvez utiliser une approche itérative: Commencez par l'approche non-optimale, ci-dessus; qui est juste une séquence temporelle d'angles de poussée. Maintenant, essayez de faire de petites variations de cette séquence, en gardant une population de séquences qui se rapprochent du but. rejeter le pire, l'expérience avec le meilleur - si vous vous sentez gras que vous pourriez faire un algorithme génétique -. et avec de la chance il va commencer à arrondir les angles

Si vous voulez la réponse exacte, utilisez le calcul des variations. Je vais prendre une fissure à cela, et si je réussis je posterai la réponse ici.

EDIT:. Voici la solution exacte à un problème plus simple

Supposons au lieu d'une poussée que nous pouvons diriger dans toutes les directions, nous avons quatre propulseurs fixes pointant dans les directions {+ X, + Y, -X, -Y}. À un moment donné, nous allons tirer au plus un des +/- X et au plus un des +/- Y (il n'y a pas de point de tir + x et -X en même temps). Alors maintenant, les X et Y sont indépendants des problèmes (ils ne sont pas le problème d'origine, car la poussée doit être partagée entre X et Y). Nous devons résoudre le problème 1-D -. Et l'appliquer deux fois

Il se trouve la meilleure trajectoire implique fourrant dans une direction, puis l'autre, et ne retourne pas à la première fois. (Cabotage est utile que si la solution de l'autre axe prendra plus de temps que le vôtre si vous avez du temps à tuer.) Résoudre le problème de la vitesse d'abord: supposons que (WLOG) que votre vitesse cible est supérieure à votre vitesse initiale. Pour atteindre la vitesse cible, vous aurez besoin d'une période de poussée (+) de durée

T = (Vf - Vi)/a

(J'utilise Vf: vitesse finale, Vi: vitesse initiale, une. Amplitude de poussée)

On remarque que si c'est tout ce que nous faisons, l'emplacement ne sortira pas droit. L'emplacement final réelle sera

X = (Vi + Vf)T/2

Nous devons donc ajouter une correction de

D = Xf - X = Xf -(Vi+Vf)T/2

Maintenant, pour faire l'emplacement sortir à droite, nous ajoutons une période de poussée dans une direction avant que, et une période égale dans le sens inverse après . Cela laissera sans être dérangés la vitesse finale, mais nous donner un certain déplacement. Si la durée de cette première période (et le troisième) est t, alors le déplacement que nous recevons de c'est

d = +/-(at^2 + atT)

Le +/- dépend si nous enfonçons + puis - ou - puis +. Supposons qu'il est +. Nous résolvons le second degré:

t = (-aT + sqrt(a^2 T^2 + 4 a D))/2a

Et nous avons terminé.

Autres conseils

En l'absence d'informations complémentaires, nous pouvons supposer il y a 3 forces qui agissent sur le vaisseau spatial et finalement dicter sa trajectoire:

  • " impulsions ": la force [utilisateur / commandé par programme]
    . L'utilisateur (ou programme) semblent avoir un contrôle total sur ce, à-dire qu'il contrôle la direction de l'impulsion et sa poussée (probablement dans une plage de 0 à max)
  • une force extérieure : appeler gravité, peu importe ...
    Une telle force pourrait être motivée par plusieurs sources, mais nous sommes seulement intéressés par la force combinée résultante: à un moment donné et dans l'espace cette force extérieure agit sur le navire avec une force et direction donnée. L'utilisateur / programme n'a aucun contrôle sur ceux-ci.
  • inertie : cela est lié à la vitesse du courant et la direction du navire. Cette force provoque généralement le navire à poursuivre dans sa direction actuelle à sa vitesse actuelle. Il peut y avoir d'autres paramètres [espace d'âge] contrôlant l'inertie, mais en général, elle est proportionnelle à la vélocité et à la masse du navire (Intuitivement, il sera plus facile de mettre un navire à un arrêt si sa vitesse actuelle est plus petite et / ou si sa masse est plus petite)

Il semble que l'utilisateur / programme de contrôle (dans certaines limites) la première force.
On ne sait pas, de la question, si le problème est à portée de main:

  • [Problème A] pour écrire un programme qui découvre la dynamique du système (et / ou adapter aux changements de ces dynamiques).
    ou ..
  • [Problème B] pour suggérer un modèle -a qui peut être formule- utilisée pour calculer la force combinée éventuellement appliquée au navire: le « pesé » somme de l'impulsion contrôlée par l'utilisateur et les deux autres systèmes / axée sur la physique forces.

La dernière question, problème B, est plus facilement et succinctement expliqué, donc nous allons suggérer le modèle suivant:

Constant Parameters:
  ExternalForceX   = strength of the external force in the X direction
  ExternalForceY   = id. Y direction
  MassOfShip       = coeficient controlling 
Variable Parameters:
  ImpulseAngle     = direction of impulse
  ImpulseThrust    = force of thrust
Formula:
  Vx[new] = (cos(ImpulseAngle) * ImpulseThrust) + ExternalForceX  + (MassOfShip * Vx[current])
  Vy[new] = (sin(ImpulseAngle) * ImpulseThrust) + ExternalForceY  + (MassOfShip * Vy[current])

Notez que le modèle ci-dessus suppose une force externe constante (constante tant en termes de sa force et de la direction); qui est: semblable à celle d'un champ gravitationnel relativement éloigné de la zone affichée (comme dire la gravité de la Terre, considérée en l'espace d'un terrain de football). Si l'échelle de la zone affichée est grande par rapport à la source (s) des forces extérieures, à moyen terme des formules ci-dessus doit ensuite être modifié pour inclure: un facteur trigonométrique en fonction de l'angle entre le centre de la source et le courant la position et / ou un facteur proportionnel [inverse] sur la base de la distance entre le centre de la source et la position actuelle.
De même, on suppose la masse du navire à rester constante, il pourrait bien être une variable, basée dire sur la masse du navire à vide, à laquelle on enlève le poids du carburant / ajouté que le jeu progresse.

... Tout ce qui précède suppose que la dynamique du système sont contrôlés par le concepteur de jeu: essentiellement le choix d'un ensemble de valeurs pour le paramètre mentionné et peut-être ajouter un peu de complexité dans le calcul de la formule (et assurer une bonne mise à l'échelle de « garder » généralement le navire dans la zone d'affichage).

Et si au lieu, la dynamique du système ont été facilement programmées dans le jeu (et supposés être cachés / aléatoire), et la tâche est d'écrire un programme qui décidera progressivement la valeur de direction et de poussée des impulsions à conduire le navire à sa destination ciblée, de manière que sa vitesse à la cible soit aussi proche que possible de getTargetVelocity ()? Ceci est le "problème A".

Ce type de problème peut être abordé avec un PID Controller . Dans un nuthell, un tel contrôleur « décide » dont le montant de l'action, sur la base de trois, pesé, facteurs ci-dessous vaguement définis (dans ce jeu de cas = l'angle sous lequel l'impulsion et la quantité de poussée à appliquer):

  • dans quelle mesure nous-off sont les valeurs actuelles de « point de consigne »: c'est le P = Proportional partie du PID
  • A quelle vitesse nous approche du « point de consigne »: tel est le D = partie dérivée du PID
  • combien de temps et combien nous avons été loin du « point de consigne »: c'est la partie I = Intergral de PID

Un contrôleur moins sophistiqué pourrait par exemple utiliser uniquement le facteur proportionnel. Cela aboutirait à osciller, parfois avec beaucoup d'amplitude de chaque côté du point de consigne ( « Je suis X unités loin de là où je suis censé être: permettez-moi de tirer sur le volant et la presse sur le gaz »). Un tel dépassement du point de consigne sont tempérés par le facteur dérivé ( « Ouais, je ne suis toujours pas là où je suis censé être, mais les progrès que j'accomplis depuis la dernière fois que je vérifie est très grand: mieux ralentir un peu ») . Enfin, la partie intégrale tient compte du fait que toutes choses étant égales par ailleurs en ce qui concerne la partie proportionnelle combinée et dérivés, une plus petite ou plus serait appropriée selon que nous avons été « hors piste » pendant une longue période ou non et de la piste bien au large, nous avons tout ce temps (par exemple. « Dernièrement, nous avons suivi assez près de là où nous sommes censés être, inutile de faire éruption se déplace »)

Nous pouvons discuter des détails de mise en œuvre des contrôleurs PID pour les besoins spécifiques du jeu de vaisseau spatial, si tel est effectivement ce qui est nécessaire. L'idée était de donner un aperçu de ce qui peut être fait.

Pour simplement obtenir de la position actuelle à la destination avec une vitesse initiale, puis appliquer une poussée sur la différence normalisée entre le chemin le plus court et la vitesse actuelle. Vous ne devez pas réellement l'angle.

-- shortest path minus initial velocity
dx,dy = x0 - x - vx, y0 - y - vy

-- normalize the direction vector
magnitude = sqrt(dx*dx + dy*dy)
dx,dy = dx/magnitude, dy/mangitude

-- apply the thrust in the direction we just calculated
self:applyImpulse(thrust*dx, thrust*dy)

Notez que cela ne prend pas la vitesse cible en compte car cela devient très compliqué.

J'ai un très petit module Lua pour la manipulation des vecteurs 2D ce bac à pâte . Nous vous invitons à l'utiliser. Le code ci-dessus serait réduire à:

d = destination - position - velocity
d:normalize()
d = d * thrust
self:applyImpulse(d.x, d.y)

Êtes-vous expulsez carburant? Votre masse changera avec le temps si vous êtes.

La poussée est une force de réaction. Il est le taux de variation de la masse, fois la vitesse de l'échappement par rapport au vaisseau spatial.

Avez-vous des forces extérieures? Si vous le faites, ceux-ci doivent entrer dans votre calcul d'impulsion.

Supposons une poussée magique sans masse étant expulsé, et pas de forces extérieures.

Impulse a des unités de mouvement. Il est l'intégrale d'une force au fil du temps.

Tout d'abord, vous aurez besoin de savoir exactement ce que l'API appelle « poussée » et de l'impulsion. Si vous êtes une poussée, il alimentation multiplié par un scalaire (nombre), puis applyImpulse doit faire quelque chose d'autre à votre entrée pour pouvoir l'utiliser comme une impulsion, parce que les unités ne correspondent pas.

Si l'on suppose votre « poussée » est une force, vous multipliez par poussée l'intervalle de temps (1/30 seconde) pour obtenir l'impulsion, et de briser les composants.

Je ne sais pas si je réponds à votre question, mais nous espérons que vous aide à comprendre la physique un peu.

Il est plus facile de penser si vous séparez la vitesse du navire en composants, parallèle et perpendiculaire au vecteur de vitesse cible.

Considérant le long de l'axe perpendiculaire, le navire veut venir en ligne avec la position cible le plus tôt possible, puis y rester.

Le long de l'axe parallèle, il devrait accélérerons dans quelle direction va l'amener près de la vitesse cible. (Il est évident que si cette accélération prend loin à partir du point cible, vous aurez besoin de décider quoi faire. Survolent le point puis double-retour?)

Je traiterais les deux d'entre eux séparément, et probablement perpendiculaire en premier. Une fois que cela fonctionne, et si cela ne prouve assez bien, vous pouvez commencer à ne pas penser à s'il y a des façons de le navire au feu des angles intelligents entre perpendiculaires et parallèles.

(EDIT:. Aussi, j'oublié de mentionner, cela prendra un certain ajustement pour faire face au scénario où vous compensez beaucoup dans la direction perpendiculaire, mais pas beaucoup dans la direction parallèle La leçon importante ici est de prendre les composants, ce qui donne des numéros utiles sur lesquels fonder une décision.)

Votre angle est l'inverse Tangent la face / Adjacent

angle = InvTan (VY / VX)

Pas sûr de ce que vous parlent au sujet de vouloir dériver ??

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top