OK, ich habe dies bereits auf math.stackechange.com gepostet, aber keine Antworten erhalten :(
Als erstes hier ein Bild von meinem Problem, die Beschreibung folgt danach:
Also habe ich alle Punkte und Werte festgelegt.
Das Schiff bewegt sich P1
mit einem S=0.27 Degrees
Gametick um den linken Planeten . Wenn es erreicht ist Point A
, folgt es der Bézier-Kurve, bis es erreicht ist Point D
, und bewegt sich dann P2
mit einem S=0.42 Degrees
Tick pro Spiel um den rechten Planeten . Der Unterschied S
besteht darin, dass die Reise mit der gleichen Bewegungsgeschwindigkeit um die Planeten verläuft.
Soweit so gut, ich habe das in Gang gebracht, jetzt mein Problem.
Wenn S P1
und zu S P2
unterschiedlich, springt das Schiff zwischen den beiden Geschwindigkeiten, wenn es sein Ziel erreicht, was ziemlich schlecht aussieht. Also muss ich das Schiff zwischen beschleunigen Point A
und Point D
von S P1
zu S P2
.
Was mir fehlt, ist lila. Das sind:
Eine Möglichkeit, die Ticks zu berechnen, die das Schiff benötigt, um sich unter Berücksichtigung der Beschleunigung entlang des Beziers zu bewegen.
Und eine Möglichkeit, eine Position auf der Bezierkurve basierend auf T zu finden, wiederum unter Berücksichtigung der Beschleunigung.
ATM Ich berechne die Länge des Beziers, indem ich den Abstand zwischen N
seinen Punkten berechne . Ich denke also, ich brauche eine Möglichkeit, das, was T
ich in meine Bezier-Berechnung einbauen muss, entsprechend der Beschleunigung zu skalieren.
quelle
Antworten:
OK, ich habe alles zum Laufen gebracht, es hat ewig gedauert, also werde ich hier meine detaillierte Lösung veröffentlichen.
Hinweis: Alle Codebeispiele sind in JavaScript.
Zerlegen wir das Problem also in die grundlegenden Teile:
Sie müssen die Länge sowie die Punkte zwischen
0..1
den Bezierkurven berechnenSie müssen jetzt die Skalierung anpassen
T
, um das Schiff von einer Geschwindigkeit zur nächsten zu beschleunigenDen Bézier richtig machen
Es ist einfach, einen Code zum Zeichnen einer Bezier-Kurve zu finden. Es gibt jedoch eine Reihe verschiedener Ansätze. Einer davon ist der DeCasteljau-Algorithmus . Sie können jedoch auch einfach die Gleichung für kubische Bézier-Kurven verwenden:
Mit diesem kann man nun eine Bezierkurve zeichnen, indem man aufruft
x
undy
mitt
welchen Bereichen von0 to 1
, schauen wir uns das an:Äh ... das ist nicht wirklich eine gleichmäßige Verteilung der Punkte, oder?
Aufgrund der Beschaffenheit der Bézier-Kurve sind die Punkte auf
0...1
unterschiedlicharc lenghts
, sodass Segmente nahe dem Anfang und dem Ende länger sind als diejenigen, die nahe der Mitte der Kurve liegen.Abbildung T gleichmäßig auf die Kurve AKA Lichtbogenlängenparametrierung
Also, was ist zu tun? Nun, in einfachen Worten, wir brauchen eine Funktion, um unsere
T
auf diet
der Kurve abzubilden , so dass unsereT 0.25
Ergebnisset
auf25%
der Länge der Kurve liegen.Wie machen wir das? Nun, wir googeln ... aber es stellt sich heraus, dass der Begriff nicht so gut lesbar ist , und irgendwann werden Sie auf dieses PDF stoßen . Was sicher eine gute Lektüre ist, aber für den Fall, dass Sie bereits alle mathematischen Dinge vergessen haben, die Sie in der Schule gelernt haben (oder Sie diese mathematischen Symbole einfach nicht mögen), ist es ziemlich nutzlos.
Was jetzt? Dann gehen Sie doch mal zu Google (Lies: 6 Stunden) und Sie finden endlich einen tollen Artikel zum Thema (inklusive schöner Bilder! ^ _ ^ "):
Http://www.planetclegg.com/projects/WarpingTextToSplines.html
Den eigentlichen Code machen
Falls Sie einfach nicht widerstehen konnten, diese PDFs herunterzuladen, obwohl Sie bereits vor langer, langer Zeit Ihr mathematisches Wissen verloren hatten (und es Ihnen gelungen ist, den großartigen Artikel-Link zu überspringen ), könnten Sie jetzt denken: "Gott, das wird dauern Hunderte von Codezeilen und Tonnen von CPU "
Nein es wird nicht. Weil wir das tun, was alle Programmierer tun, wenn es um Mathe geht:
Wir betrügen einfach.
Lichtbogenlängenparametrierung auf die faule Art und Weise
Seien wir ehrlich, wir brauchen keine endlose Präzision in unserem Spiel, oder? Wenn Sie also nicht bei der Nasa arbeiten und vorhaben, Menschen auf den Mars zu schicken, brauchen Sie keine
0.000001 pixel
perfekte Lösung.So wie bilden wir
T
auft
? Es ist einfach und besteht nur aus 3 Schritten:Berechnen Sie die
N
Punkte auf der Kurve mitt
und speichern Sie diearc-length
(auch bekannt als die Länge der Kurve) an dieser Position in einem ArrayZum
T
Abbildent
multiplizieren Sie zuerstT
mit der Gesamtlänge der Kurve, umu
das Längenfeld nach dem Index des größten Werts zu durchsuchen, der kleiner als istu
Wenn wir einen genauen Treffer hatten, geben Sie den Array-Wert an diesem Index geteilt durch zurück
N
. Wenn Sie nicht ein bisschen zwischen dem gefundenen und dem nächsten Punkt interpolieren, teilen Sie das Ding erneut durchN
und geben Sie zurück.Das ist alles! Schauen wir uns nun den kompletten Code an:
Dies initialisiert unsere neue Kurve und berechnet die
arg-lenghts
, es speichert auch die letzte der Längen als dietotal length
der Kurve, wobei der Schlüsselfaktor hier ist,this.len
welcher unsere istN
. Je höher, desto präziser das Mapping, denn eine Kurve der Größe im obigen Bild100 points
scheint ausreichend zu sein. Wenn Sie nur eine gute Längenschätzung benötigen, erledigt so etwas25
bereits die Aufgabe , wenn Sie nur 1 Pixel von unserem entfernt sind Beispiel, aber dann haben Sie eine weniger genaue Zuordnung, die zu einer nicht so gleichmäßigen Verteilung führt,T
wenn sie zugeordnet wirdt
.Den eigentlichen Mapping-Code ermitteln wir zunächst einfach
binary search
anhand unserer gespeicherten Längen, um die größte Länge zu ermitteln, die dann kleiner isttargetLength
. Dann geben wir einfach zurück oder interpolieren und geben zurück.Dies berechnet sich wiederum
t
auf der Kurve.Zeit für Ergebnisse
Indem Sie jetzt verwenden
mx
undmy
Sie erhalten eine gleichmäßigT
auf der Kurve verteilt :)War das nicht schwer? Wieder stellt sich heraus, dass eine einfache (wenn auch nicht perfekte) Lösung für ein Spiel ausreichen wird.
Für den Fall, dass Sie den vollständigen Code sehen möchten, steht eine Liste zur Verfügung:
https://gist.github.com/670236
Schließlich beschleunigen die Schiffe
Jetzt müssen
T
wir nur noch die Schiffe auf ihrem Weg beschleunigen, indem wir die Position abbilden, auf der wir dann diet
auf unserer Kurve finden.Zuerst brauchen wir zwei der Bewegungsgleichungen , nämlich
ut + 1/2at²
und(v - u) / t
Im eigentlichen Code würde das so aussehen:
Dann verkleinern wir das wie
0...1
folgt:Und los geht's, die Schiffe bewegen sich nun reibungslos auf dem Weg.
Falls es nicht funktioniert ...
Wenn Sie dies lesen, funktioniert alles einwandfrei, aber ich hatte anfangs einige Probleme mit dem Beschleunigungsteil, als ich jemandem im Chatroom von Gamedev das Problem erklärte, fand ich den letzten Fehler in meinem Denken.
Falls Sie das Bild in der ursprünglichen Frage noch nicht vergessen haben, wie ich dort erwähne
s
, stellt sich heraus, dasss
es sich um eine Geschwindigkeit in Grad handelt , aber die Schiffe bewegen sich auf dem Pfad in Pixeln und ich hatte diese Tatsache vergessen. In diesem Fall musste ich die Verschiebung in Grad in eine Verschiebung in Pixel umwandeln. Dies ist also recht einfach:Also und das ist alles! Danke fürs Lesen ;)
quelle
Das Problem ist, dass ein Schiff diese Flugbahn natürlich nicht nehmen würde. Selbst wenn es perfekt funktioniert, sieht es immer noch nicht richtig aus.
Wenn Sie den fließenden Übergang zwischen Planeten simulieren möchten, würde ich vorschlagen, ihn tatsächlich zu modellieren. Die Gleichungen sind sehr einfach, da Sie nur zwei wesentliche Kräfte haben: Schwerkraft und Schubkraft.
Sie müssen nur Ihre Konstanten einstellen: Masse von P1, P2, Schiff
Mit jedem Tick (Zeit: t) machst du 3 Dinge
Berechnen Sie die Schwerkraft von p1 auf dem Schiff und p2 auf dem Schiff und addieren Sie die resultierenden Vektoren zum Schubvektor.
Berechnen Sie Ihre neue Geschwindigkeit basierend auf Ihrer neuen Beschleunigung aus Schritt 1
Bewegen Sie das Schiff entsprechend Ihrer neuen Geschwindigkeit
Es mag wie viel Arbeit erscheinen, aber es kann in einem Dutzend Codezeilen erledigt werden und sieht sehr natürlich aus.
Wenn Sie Hilfe in der Physik brauchen, lassen Sie es mich wissen.
quelle
t
Anspruch nimmt :)Ich habe einen ausgezeichneten Artikel gefunden , in dem eine mögliche Lösung für dieses Problem anhand eines in Javascript geschriebenen Codebeispiels erläutert wird. Es funktioniert, indem der t-Wert in die richtige Richtung bewegt wird.
Diese Frage hat bereits viele coole Antworten, aber ich fand diese Lösung bemerkenswert.
quelle
Vielen Dank für Ihre ausgezeichnete Seite, auf der beschrieben wird, wie Sie dieses Problem gelöst haben. Ich habe in einem Detail etwas anderes gemacht als Sie, da mir der Speicher sehr schwerfiel: Ich erstelle kein Array oder muss es mit einer binären Suche nach dem richtigen "Segment" durchsuchen. Das liegt daran, dass ich immer weiß, dass ich mich von einem Ende meiner Bézier-Kurve zum anderen bewege. Deshalb erinnere ich mich einfach an das „aktuelle“ Segment und wenn ich sehe, dass ich die Grenzen dieses Segments überschreite, um mein nächstes zu berechnen Position berechne ich das nächste (oder vorherige) Segment (basierend auf der Fahrtrichtung). Dies funktioniert für meine Anwendung recht gut. Die einzige Panne, die ich herausfinden musste, war, dass die Segmentgröße in einigen Kurven so gering war, dass mein nächster zu zeigender Plot - in seltenen Fällen - mehr als ein Segment vor dem aktuellen war, anstatt einfach loszulegen zum '
Ich weiß nicht, ob das wirklich Sinn macht, aber das hat mir sicherlich geholfen.
quelle
Diese Art der Modellierung ist seltsam und kann zu seltsamen unlogischen Ergebnissen führen. Vor allem, wenn die Geschwindigkeit der Startplaneten sehr gering ist.
Modellieren Sie die Schiffe mit einer Schubkraft.
Wenn sich die Schiffe auf ihrer letzten Umlaufbahn auf dem Startplaneten befinden, beschleunigen Sie mit vollem Schub.
Wenn sich das Schiff einer bestimmten Entfernung nähert, bremsen Sie es mit dem Rückwärtsschub auf die Umlaufgeschwindigkeit des Zielplaneten ab.
Bearbeiten: Führen Sie die gesamte Simulation auf einmal durch, wenn ein Knoten die Umlaufbahn verlassen soll. Senden Sie entweder alle Daten oder senden Sie nur einige Bewegungsvektoren in Intervallen und interpolieren Sie zwischen ihnen.
quelle
Wenn ich es richtig verstehe, ist Ihr Problem überfordert.
Ich glaube, Sie möchten, dass das Raumschiff in einer bestimmten Zeit t auf einem bestimmten Weg zwischen den Umlaufbahnen fährt , und Sie möchten, dass es in derselben Zeit t von Geschwindigkeit s1 auf Geschwindigkeit s2 beschleunigt . Leider kann man (im Allgemeinen) keine Beschleunigung finden, die beide Bedingungen gleichzeitig erfüllt.
Sie müssen Ihr Problem ein wenig lösen, um es lösbar zu machen.
quelle
Ich bin auf diese Antwort gestoßen, weil ich Punkte auf einem SVG-Pfad, der eine Bezier-Kurve verwendet, gleichmäßig verteilen möchte.
Obwohl MDN sagt, dass es veraltet ist, können Sie das verwenden
path.getPointAtLength
, um das richtige Ergebnis zu erhalten. https://developer.mozilla.org/en-US/docs/Web/API/SVGPathElement/getPointAtLengthEs funktioniert derzeit in Chrome / Safari / Firefox und sollte auch in IE / Edge funktionieren, aber ich habe diese 2 nicht verifiziert.
quelle
Das Problem mit der akzeptierten Lösung
Da Bezier eine Exponentialfunktion ist, erwarten wir unterschiedliche Vorschubgeschwindigkeiten in verschiedenen Bereichen der Kurve.
Da Ivos Lösung linear zwischen diesen anfänglichen Exponentialabtastwerten interpoliert , werden Ungenauigkeiten in Richtung der Enden / Mitte der (typischerweise kubischen) Kurve stark vorgespannt, wo diese Deltas am größten sind. Wenn also die Abtastrate nicht
N
stark erhöht wird, wie er vorschlägt, sind Fehler offensichtlich, und bei einer gewissen Zoomstufe sind sie für eine gegebene Situation immer offensichtlichN
, dh, die Verzerrung ist für diesen Algorithmus intrinsisch. Nicht gut für vektorbasierte Grafiken, bei denen der Zoom möglicherweise unbegrenzt ist.Gegenvorspannung durch geführte Probenahme
Eine alternative Lösung besteht darin, eine lineare Neuzuordnung vorzunehmen
distance
,t
nachdem der natürlichen Vorspannung, die die Bezier-Funktion erzeugt, entgegengewirkt wurde.Angenommen, dies ist das, was wir im Idealfall wollen:
aber das bekommen wir von der Bezier-Positionsfunktion:
Durch die bei der Suche
N
Proben genommen, können wir bei denen der Abstand Deltas sind sehen am größten, und resample ( „split“) in der Mitte zwischen den beiden benachbarten Entfernungen, die ErhöhungN
um 1 zum Beispiel Spaltung beit=0.9
(die in der Mitte der größten Delta ist), könnten wir bekommen:Wir wiederholen diesen Vorgang für das nächstgrößere Entfernungsintervall, bis das maximale Delta zwischen zwei beliebigen Entfernungen in der gesamten Menge unter einigen liegt
minDistanceDelta
, genauer gesagt, weniger alsepsilon
von bestimmten Entfernungen entfernt, die wir auf Schritte von abbilden möchtent
. wir können dann unsere gewünschtent
Schritte linear auf entsprechendedistance
s abbilden . Dies erzeugt eine Hash-Tabelle / Map, auf die Sie kostengünstig zugreifen können und zwischen deren Werten Sie zur Laufzeit ohne Verzerrung wechseln können.Wenn das Set wächst
N
, steigen die Kosten für die Wiederholung. Idealerweise tun Sie dies als Vorverarbeitung. Fügen Sie bei jederN
Erhöhung die beiden neuen resultierenden Intervalle zu einerintervals
Sammlung hinzu, während Sie das alte, einzelne Intervall entfernen, das sie ersetzt haben. Dies ist die Struktur, an der Sie arbeiten, um das nächstgrößere Intervall für die Zweiteilung zu finden. Dieintervals
Sortierung nach Entfernung vereinfacht die Arbeit, da Sie einfach das nächste Arbeitselement vom Ende entfernen und es aufteilen können.Am Ende haben wir so etwas wie das, was wir uns im Idealfall gewünscht haben:
Da wir Vermutungen bei jedem Schritt nehmen, werden wir nicht genau die genauen Abstände erhalten
2
,4
usw. Wir wollten, aber durch Wiederholen Sie diese Iteration nahe genug , um den gewünschten Abstand Werte zu erhalten , so dass Sie Ihre Karte könnent
Schritte mit ziemlicher Genauigkeit, Bias eliminiert durch zu nahezu äquidistanten Abtastungen.Sie können dann z. B.
t=0.5
wie Ivo in seiner Antwort abrufen , indem Sie zwischen den beiden nächstgelegenen Werten (3.9998132
und6.00703
) interpolieren .Fazit
In den meisten Fällen funktioniert die Lösung von Ivo gut. In Fällen, in denen eine Verzerrung unbedingt vermieden werden muss, stellen Sie sicher, dass Ihre
distance
s so gleichmäßig wie möglich verteilt und dann linear zugeordnet sindt
.Beachten Sie, dass die Aufteilung stochastisch erfolgen kann, anstatt jedes Mal in der Mitte aufzuteilen. Beispielsweise haben wir das erste Beispielintervall möglicherweise
t=0.827
eher um als um aufgeteiltt=0.9
.quelle