Je fais de l'IA rudimentaire pour mon défilement latéral et j'ai besoin de savoir si une unité AI peut atteindre le point B à partir du point A simplement en faisant un saut.
La trajectoire de vol de mes personnages est un peu inhabituelle car ils peuvent appliquer une force dans les airs (comme dans Jazz Jackrabbit 2 par exemple), donc contrairement à la trajectoire classique d'un projectile qui est d'environ ...
trajectoire qu'un projectile lancé ou lancé prendra (...) sans propulsion.
... Je suppose que mon problème concerne davantage un projectile à propulsion (par exemple une fusée).
Pour illustrer cela, voici à quoi ressemble la courbe de vol pour mon personnage si je saute et appuie continuellement sur le "bouton gauche" (ça a l'air différent à l'extrémité gauche, c'est là que je faisais des manœuvres en l'air):
La force appliquée pendant le vol est toujours parallèle à l'axe X, donc elle est F = (-f, 0) si je maintiens "gauche" et elle est F = (f, 0) si je maintiens "droite".
Il peut bouger comme un sauteur à ski:
Elle diffère donc beaucoup de la trajectoire classique qui est tout simplement une parabole (source: wikipedia ):
Pour le rendre plus difficile, je simule une simple résistance à l'air afin que mes personnages ne puissent accélérer que jusqu'à une certaine valeur de vitesse maximale.
Cela se fait en appliquant une petite force dans la direction opposée de la marche :
b2Vec2 vel = body->GetLinearVelocity();
float speed = vel.Normalize(); //normalizes vector and returns length
body->ApplyForce( AIR_RESISTANCE_MULT * speed * speed * -vel, body->GetWorldCenter() );
L'AIR_RESISTANCE_MULT est une constante qui dans mon cas est égale à 0,1.
Supposons que mon personnage soit un point infiniment petit.
Et je ne prends PAS en considération les obstructions, alors ma question va comme ça ...
Comment déterminer (au moins de façon fiable), étant donné la vitesse initiale V, une impulsion J = (0, -j) que j'applique au personnage au saut, la gravité G = (0, g) , la force F = (+ -f , 0) appliqué continuellement pendant le vol et AIR_RESISTANCE_MULT si nous décidons vraiment de prendre en compte la résistance de l'air (ceci est facultatif) , si un point se trouve en dessous de la courbe tracée par le chemin que mon personnage prendra?
Je n'ai littéralement aucune idée par où commencer avec les calculs et en fait, je ne suis pas nécessairement intéressé par une réponse exacte; un hack / approximation qui fonctionne bien serait génial car l'IA n'a nullement besoin d'agir parfaitement.
edit: J'ai décidé de résoudre ce problème en utilisant la simulation comme le suggère Jason, mais comment gérer un tel cas?
Dois-je dessiner un segment de C à D et vérifier si le point souhaité se trouve en dessous de ce segment?
Ou dois-je rechercher en binaire les pas de temps entre C et D pour rechercher le point qui est assez proche de la distance horizontale du point souhaité, et ensuite seulement vérifier la différence verticale? (me semble un peu exagéré)
la source
Réponses:
Comme vous le dites, le meilleur choix est d'approximer, dans ce cas en utilisant un schéma numérique. Divisez le temps en grands pas de temps (disons 100-300 ms) et utilisez l'approximation parabolique pour chaque pas de temps. Les forces sont les mêmes partout, sauf la résistance de l'air. Le chemin parabolique est essentiellement pour une accélération constante, mais avec la résistance de l'air, l'accélération change parce que la force dépend de la vitesse. Une approximation raisonnable consiste à traiter la résistance de l'air comme constante à chaque pas de temps. Mais l'utilisation d'une approximation quadratique (c'est-à-dire parabolique) lors de l'intégration vous permet de gérer des pas de temps beaucoup plus grands. Ensuite, vous calculez jusqu'à ce qu'une parabole traverse le point souhaité dans le sens horizontal, puis comparez les hauteurs.
EDIT: Un peu plus de détails sur la comparaison. Vous savez qu'au fil du temps (qui pourrait être nombreux dans les cadres de jeu), le joueur franchit la cible
<targetx,targety>
. Leur chemin est décrit par la position<ax*t^2 + bx*t + cx, ay*t^2 + by*t + cy>
où:t
est le temps à travers le timestep (0 <= t <= dt
) et de même poury
. Donc, quandt=0
le personnage est à la position précédente, et quandt=dt
, ils sont à la position suivante. Notez qu'il s'agit essentiellement de la mise à jour d'Euler avecdt
remplacé part
afin que nous puissions calculer n'importe où le long de la trajectoire. Maintenant, nous savons que la position x est une fonction quadratique, nous pouvons donc résoudreax*t^2 + bx*t + cx = targetx
et obtenir (jusqu'à) deux fois au cours de l'étape au cours de laquelle le personnage est directement au-dessus ou en dessous de la cible. Ensuite, nous jetons toutes les solutions qui ne sont pas dans la plage [0,dt
], car ceux-ci ne sont pas dans le pas de temps actuel. (Pour la robustesse, ajoutez une petite constante aux extrémités de la plage afin de ne pas avoir de problèmes d'arrondi). Maintenant, nous ne pouvions pas avoir de solutions (après filtrage), auquel cas nous n'atteignons pas la cible ce pas de temps. Sinon, nous évaluonsay*t^2 + by*t + cy
les solutions et comparons ce y avectargety
. Notez que vous pourriez être au-dessus de la cible à un moment donné de votre trajectoire, et en dessous plus tard (ou vice-versa). Vous devrez interpréter ces situations en fonction de ce que vous voulez faire.Il est beaucoup plus facile de considérer un tas de pas de temps que de trouver une solution analytique au problème d'origine, et beaucoup plus flexible car vous pouvez changer le modèle de mouvement et cela fonctionnera toujours grosso modo.
Points bonus pour l'utilisation de pas variables, par exemple, 100 ms pour la première seconde (dix points), 200 ms pour les deux suivants (dix points de plus), 400 ms sur 4 secondes, etc. En fait, lorsque votre personnage approche de la vitesse terminale, la variation de la résistance diminue et vous n'avez pas besoin de plus grands pas de temps de toute façon. De cette façon, vous pouvez gérer de très longs sauts sans trop de traitement, car la complexité pour T secondes est O (log T) plutôt que O (T).
Vous pouvez également simuler ce qui se passe lorsque le personnage arrête de booster à mi-chemin de son saut, ou commence à booster dans l'autre sens. Avec l'astuce ci-dessus, la complexité est O ((log T) ^ 2), ce qui n'est pas trop mal.
la source
x'= x + v*dt
. Utilisez plutôtx' = x + v*dt + 1/2*a*dt*dt
. Lorsqu'ildt
est petit,dt^2
est petit, il est donc généralement omis dans l'intégration traditionnelle d'Euler dans les jeux. Cedt
n'est pas petit, vous avez donc besoin du terme d'accélération. Puisquedt
est élevé à la deuxième puissance, il s'agit d'une intégration quadratique, et le chemin est une parabole, d'où une approximation parabolique. RK4 calcule essentiellement des dérivées plus élevées, et pourrait donc donner des approximations cubiques, quartiques, quintiques, etc. Le RK4 est exagéré pour cela, très probablement, car la stabilité n'est pas importante.v' = v + a*dt
Yay! Je l'ai fait!
J'utilise une simulation simple qui prend la première position pour atterrir derrière l'axe vertical du point cible - à partir de là, je prends la position simulée précédente et crée un segment. Maintenant, je vérifie si le point cible est en dessous de ce segment. Si c'est le cas - nous pouvons y sauter.
C'est un personnage contrôlé par le joueur sur le gif. Le rose est la trajectoire prédite, les segments jaunes sont les positions de progression ultérieures prédites et le segment final devient blanc si le point cible se trouve en dessous, rouge sinon. La courbe rouge est la trajectoire de vol réelle. Il y a quelques légères inexactitudes dues à l'interpolation d'état physique activée.
Les calculs se sont révélés étonnamment faciles, mais faire fonctionner mon environnement de la même manière que ces calculs purs ... était une douleur énorme dans le cul. Au moins, j'ai résolu de graves bugs, donc c'était un exercice utile après tout.
Voici le code complet dans Lua utilisé pour résoudre le problème d'origine (le code suppose que vous avez votre propre routine "debug_draw" et votre propre classe vectorielle avec des méthodes de base comme "length_sq" (longueur au carré), "normaliser" ou opérateurs +, * :
Accepter va à Jason pour m'avoir mis dans la bonne direction! Merci!
la source
Vous voudrez peut-être "simplement calculer" la réponse, mais je suis sûr que vous la trouverez insuffisante une fois que vous l'avez en raison de la nature hautement interactive de votre physique de "chute libre".
Envisagez d'utiliser une approche différente: la recherche. Voici comment cela se fait pour Super Mario AI: http://aigamedev.com/open/interview/mario-ai/
La recherche de chemins possibles pour aller de A à B permet une interactivité illimitée dans les airs tout en étant efficace sur le plan des calculs.
la source