Ich mache eine rudimentäre KI für meinen Side-Scroller und muss wissen, ob eine KI-Einheit Punkt B von Punkt A aus erreichen kann, indem sie einfach einen Sprung macht.
Die Flugbahn meiner Charaktere ist etwas ungewöhnlich, da sie in der Luft Kraft ausüben können (wie zum Beispiel in Jazz Jackrabbit 2), also im Gegensatz zur klassischen Flugbahn eines Projektils, bei der es um ...
Weg, den ein geworfenes oder abgefeuertes Projektil (...) ohne Antrieb nimmt.
... Ich nehme an, mein Problem betrifft eher ein Projektil mit Antrieb (z. B. Rakete).
Um dies zu veranschaulichen, sieht die Flugkurve für meinen Charakter so aus, wenn ich springe und ständig den "linken Knopf" drücke (am linken Ende sieht es anders aus, hier habe ich einige Manöver in der Luft gemacht):
Die während des Fluges ausgeübte Kraft ist immer parallel zur X-Achse, also ist es F = (-f, 0), wenn ich "links" halte, und es ist F = (f, 0), wenn ich "rechts" halte.
Er kann sich sehr wie ein Skispringer bewegen:
Es unterscheidet sich also stark von der klassischen Flugbahn, die einfach eine Parabel ist (Quelle: Wikipedia ):
Um es schwieriger zu machen, simuliere ich einen einfachen Luftwiderstand, damit meine Charaktere nur bis zu einem Höchstgeschwindigkeitswert beschleunigen können.
Dies geschieht durch Aufbringen einer kleinen Kraft in die entgegengesetzte Fahrtrichtung :
b2Vec2 vel = body->GetLinearVelocity();
float speed = vel.Normalize(); //normalizes vector and returns length
body->ApplyForce( AIR_RESISTANCE_MULT * speed * speed * -vel, body->GetWorldCenter() );
AIR_RESISTANCE_MULT ist eine Konstante, die in meinem Fall gleich 0,1 ist.
Nehmen wir an, mein Charakter ist ein unendlich kleiner Punkt.
Und ich berücksichtige KEINE Hindernisse, also geht meine Frage so ...
Wie man bei gegebener Anfangsgeschwindigkeit V einen Impuls J = (0, -j) bestimmt (zumindest zuverlässig errät) , den ich beim Sprung auf das Zeichen anwende, Schwerkraft G = (0, g) , Kraft F = (+ -f , 0) wird während des Fluges und AIR_RESISTANCE_MULT kontinuierlich angewendet, wenn wir uns wirklich dazu entschließen, den Luftwiderstand zu berücksichtigen (dies ist optional) , ob ein Punkt unter der Kurve liegt, die durch den Weg gezogen wird, den mein Charakter nehmen wird?
Ich habe buchstäblich keine Ahnung, wo ich mit den Berechnungen anfangen soll, und tatsächlich bin ich nicht unbedingt an einer genauen Antwort interessiert. Ein gut funktionierender Hack / Approximation wäre großartig, da die KI keineswegs perfekt handeln muss.
edit: Ich habe beschlossen, dieses Problem mithilfe einer Simulation zu lösen, wie Jason es vorschlägt, aber wie soll ich mit einem solchen Fall umgehen?
Soll ich ein Segment von C nach D zeichnen und prüfen, ob der gewünschte Punkt unter diesem Segment liegt?
Oder sollte ich die Zeitschritte zwischen C und D binär durchsuchen , um nach dem Punkt zu suchen, der in horizontaler Entfernung zum gewünschten Punkt nahe genug ist, und erst dann den vertikalen Unterschied überprüfen? (scheint mir ein bisschen übertrieben)
quelle
Antworten:
Wie Sie sagen, ist die beste Wahl die Annäherung, in diesem Fall unter Verwendung eines numerischen Schemas. Teilen Sie die Zeit in große Zeitschritte (z. B. 100-300 ms) und verwenden Sie die parabolische Näherung für jeden Zeitschritt. Die Kräfte sind bis auf den Luftwiderstand überall gleich. Der parabolische Pfad dient im Wesentlichen der konstanten Beschleunigung, aber mit dem Luftwiderstand ändert sich die Beschleunigung, da die Kraft von der Geschwindigkeit abhängt. Eine vernünftige Annäherung besteht darin, den Luftwiderstand über jeden Zeitschritt als konstant zu behandeln. Die Verwendung einer quadratischen (dh parabolischen) Näherung bei der Integration ermöglicht es Ihnen jedoch, viel größere Zeitschritte zu verarbeiten. Dann berechnen Sie einfach, bis eine Parabel den gewünschten Punkt in horizontaler Richtung kreuzt, und vergleichen dann die Höhen.
EDIT: Ein bisschen mehr Details zum Vergleich. Sie wissen, dass der Spieler über den Zeitschritt (der in Spielrahmen viele sein kann) das Ziel überquert
<targetx,targety>
. Ihr Weg wird durch die Position beschrieben, an der<ax*t^2 + bx*t + cx, ay*t^2 + by*t + cy>
:t
ist die Zeit durch den Zeitschritt (0 <= t <= dt
) und ähnlich füry
. Wenn sicht=0
der Charakter also an der vorherigen Position befindet und wannt=dt
, befinden sie sich an der nächsten Position. Beachten Sie, dass dies im Grunde das Euler-Update ist, das durchdt
ersetzt wird,t
damit wir überall entlang der Flugbahn berechnen können. Jetzt wissen wir, dass die x-Position eine quadratische Funktion ist, sodass wir während des Schritts, in dem sich das Zeichen direkt über oder unter dem Ziel befindet, zwei Mal lösenax*t^2 + bx*t + cx = targetx
und (bis zu) erhalten können. Dann werfen wir alle Lösungen aus, die nicht im Bereich [0,dt
], da diese nicht im aktuellen Zeitschritt sind. (Fügen Sie aus Gründen der Robustheit den Enden des Bereichs eine kleine Konstante hinzu, damit Sie keine Rundungsprobleme haben.) Jetzt konnten wir keine Lösungen finden (nach dem Filtern). In diesem Fall treffen wir das Ziel in diesem Zeitschritt nicht. Ansonsten bewerten wiray*t^2 + by*t + cy
die Lösungen und vergleichen diese mittargety
. Beachten Sie, dass Sie sich an einem Punkt Ihrer Flugbahn über dem Ziel und später darunter befinden können (oder umgekehrt). Sie müssen solche Situationen so interpretieren, wie Sie es möchten.Das Betrachten einer Reihe von Zeitschritten ist viel einfacher als das Finden einer analytischen Lösung für das ursprüngliche Problem und weitaus flexibler, da Sie das Bewegungsmodell ändern können und dies dennoch ungefähr funktioniert.
Bonuspunkte für die Verwendung variabler Schritte, z. B. 100 ms für die erste Sekunde (zehn Punkte), 200 ms für die nächsten zwei (zehn weitere Punkte), 400 ms über 4 Sekunden usw. Wenn sich Ihr Charakter der Endgeschwindigkeit nähert, ändert sich die Geschwindigkeit in Der Widerstand nimmt ab und Sie benötigen sowieso keine größeren Zeitschritte. Auf diese Weise können Sie wirklich lange Sprünge ohne zu viel Verarbeitung verarbeiten, da die Komplexität für T Sekunden eher O (log T) als O (T) ist.
Sie können auch simulieren, was passiert, wenn der Charakter während seines Sprunges nicht mehr oder in die andere Richtung verstärkt. Mit dem obigen Trick ist die Komplexität O ((log T) ^ 2), was nicht schlecht ist.
quelle
x'= x + v*dt
. Verwenden Sie stattdessenx' = x + v*dt + 1/2*a*dt*dt
. Wenndt
es klein ist,dt^2
ist es winzig, so dass es bei der traditionellen Euler-Integration in Spielen im Allgemeinen weggelassen wird. Hierdt
ist nicht klein, also brauchen Sie den Beschleunigungsterm. Dadt
auf die zweite Potenz angehoben wird, ist dies eine quadratische Integration, und der Pfad ist eine Parabel, daher eine parabolische Approximation. RK4 berechnet im Wesentlichen höhere Ableitungen und könnte so kubische, quartische, quintische usw. Näherungen ergeben. RK4 ist dafür höchstwahrscheinlich übertrieben, da Stabilität nicht wichtig ist.v' = v + a*dt
Yay! Ich habe es gemacht!
Ich verwende eine einfache Simulation, bei der die erste Position hinter der vertikalen Achse des Zielpunkts landet. Von dort aus nehme ich die vorherige simulierte Position und erstelle ein Segment. Jetzt überprüfe ich, ob der Zielpunkt unter diesem Segment liegt. Wenn ja, können wir dorthin springen.
Es ist ein vom Spieler kontrollierter Charakter im GIF. Pink ist der vorhergesagte Pfad, gelbe Segmente werden nachfolgende Schrittpositionen vorhergesagt, und das letzte Segment wird weiß, wenn der Zielpunkt darunter liegt, andernfalls rot. Die rote Kurve ist die tatsächliche Flugbahn. Es gibt einige leichte Ungenauigkeiten aufgrund der aktivierten Interpolation des Physikzustands.
Die Berechnungen erwiesen sich als überraschend einfach, aber meine Umgebung so zu machen, wie diese reinen Berechnungen ... war ein massiver Schmerz im Hintern. Zumindest habe ich da draußen einige schwerwiegende Fehler behoben, also war es doch eine nützliche Übung.
Hier ist der vollständige Code in Lua, der zur Lösung des ursprünglichen Problems verwendet wird (der Code setzt voraus, dass Sie über eine eigene Routine "debug_draw" und eine eigene Vektorklasse mit grundlegenden Methoden wie "length_sq" (Länge im Quadrat), "normalize" oder Operatoren +, * verfügen ::
Akzeptieren geht an Jason, der mich in die richtige Richtung gebracht hat! Vielen Dank!
quelle
Vielleicht möchten Sie die Antwort "nur berechnen", aber ich bin sicher, dass Sie sie aufgrund der hochgradig interaktiven Natur Ihrer "freien Fall" -Physik als unzureichend empfinden, wenn Sie sie erhalten haben.
Ziehen Sie einen anderen Ansatz in Betracht: Suchen. So geht's für Super Mario AI: http://aigamedev.com/open/interview/mario-ai/
Die Suche nach möglichen Pfaden von A nach B ermöglicht unbegrenzte Interaktivität in der Luft und ist dennoch rechnerisch effizient.
quelle