Die Verwendung der Deltazeit mit Addition und Subtraktion ist einfach.
player.speed += 100 * dt
Multiplikation und Division erschweren die Dinge jedoch ein wenig. Nehmen wir zum Beispiel an, ich möchte, dass der Spieler seine Geschwindigkeit jede Sekunde verdoppelt.
player.speed = player.speed * 2 * dt
Ich kann das nicht tun, weil es den Spieler verlangsamt (es sei denn, die Delta-Zeit ist wirklich hoch). Die Teilung ist der gleiche Weg, nur dass sie die Dinge beschleunigt.
Wie kann ich mit Multiplikation und Division mit Deltazeit umgehen?
Edit : Es sieht so aus, als hätte meine Frage alle verwirrt. Ich wollte wirklich nur die Verzögerung ohne diese schreckliche Menge an Code implementieren können:
else
if speed > 0 then
speed = speed - 20 * dt
if speed < 0 then
speed = 0
end
end
if speed < 0 then
speed = speed + 20 * dt
if speed > 0 then
speed = 0
end
end
end
Weil das viel größer ist als es sein muss. Bisher scheint eine bessere Lösung zu sein:
speed = speed - speed * whatever_number * dt
Antworten:
Hier möchten Sie etwas mehr Physik verwenden, als nur die Position um einen konstanten Wert zu ändern. Geben Sie Ihrem Spieler eine Geschwindigkeit und eine Beschleunigung. Wenn Sie dann aktualisieren, können Sie Folgendes tun:
Dann ist es einfach, die Beschleunigung so einzustellen
0
, dass eine konstante Geschwindigkeit erhalten bleibt, oder Sie können eine positive Zahl festlegen, um die Geschwindigkeit zu erhöhen, oder eine negative Zahl, um die Geschwindigkeit zu verringern.Wenn Sie nur eine negative Beschleunigung "abbremsen" möchten, ist das alles Reibung. Reibung ist nur eine weitere Kraft wie das Beschleunigen.
quelle
player.x = player.x * 2
), was noch schneller, exponentiell schneller außer Kontrolle geraten würde.In diesen Fällen sollten Sie Exponentialfunktionen anstelle einer normalen Multiplikation verwenden. Bedeutung:
Es ist ziemlich einfach zu erklären, warum: Wenn Sie schreiben
player.velocity.x += CONSTANT * dt
, nehmen Sie an, dass Ihr SpielCONSTANT * 1/60
mit 60 fps läuft, und erhöhen die Geschwindigkeit 60 Mal pro Sekunde, was bedeutet, dass sich Ihre Geschwindigkeit in jeder Sekunde erhöhtCONSTANT * 1/60 * 60 = CONSTANT
.Jetzt möchten Sie, dass die Spielgeschwindigkeit zwei- oder dreimal pro Sekunde erreicht wird. Nach einer Sekunde können Sie
new_velocity = old_velocity * CONSTANT
diese in kleinere Multiplikationen aufteilen und schreibenDies bedeutet, dass Sie in jedem Frame die Geschwindigkeit mit multiplizieren müssen
CONSTANT^dt
BEARBEITEN
Basierend auf den Änderungen, die Sie in Ihrer Frage vorgenommen haben, müssen Sie diese exponentielle Methode nicht wirklich implementieren. Angenommen, Sie simulieren Reibung, können Sie auch eine physikalischere Methode implementieren:
Physikalische Reibung verringert die Geschwindigkeit linear, vorausgesetzt, die Objektmasse ändert sich nicht im Laufe der Zeit. Es übt eine Kraft auf das Objekt aus, genau entgegengesetzt zur aktuellen Geschwindigkeit des Objekts. Nehmen wir zum Beispiel an, Reibung reduziert die Geschwindigkeit immer um zwei Einheiten pro Sekunde. Sie müssen die aktuelle Richtung der Geschwindigkeit ermitteln, sie umkehren und einen Vektor der Länge zwei in dieser Richtung multipliziert mit dt reduzieren. Hier ist ein Code, der Ihnen das Verständnis erleichtert:
Es gibt eine kleine Kleinigkeit, die Sie vielleicht bemerken möchten: Wenn der Code bei diesem Code zu niedrig ist, zittert das Objekt an seiner Position. Das liegt daran, dass
Fricton_Direction * dt
es größer als die Geschwindigkeit selbst ist, was dazu führt, dass sich das Objekt im nächsten Schritt genau in die entgegengesetzte Richtung bewegt, anstatt still zu stehen. Um dies zu beheben, müssen Sie eine if-Anweisung hinzufügen, um zu überprüfen, ob die Geschwindigkeit zu niedrig ist und das Objekt vollständig gestoppt werden soll. Ich mache es so:Beachten Sie, dass dies
Length
eine Funktion ist, die einen Vektor als Eingabe erhält und dessen Länge zurückgibt. Zum Beispiel, wenn Ihr Spiel eine 2D-Welt hatLength (Velocity) = Sqrt(Sqr(Velocity.x) + Sqr(Velocity.y))
quelle
v *= Math.pow ( 2, dt );
die Geschwindigkeit jede Sekunde zu verdoppeln (dt ist Bruchteil einer Sekunde)Wie andere angemerkt haben, lautet die korrekte Formel für die Verzögerung aufgrund von Trockenreibung
Dabei wird
dir(v)
ein Vektor mit Längeneinheiten zurückgegeben, der in dieselbe Richtung wie zeigtv
. (Für eindimensionale Bewegungen,dir(v) = 1
obv > 0
unddir(v) = -1
wennv < 0
.) Eine Möglichkeit, sie zu berechnen, ist aswo
abs(v)
gibt die Länge oder Größe des Vektorsv
.Um zu verhindern, dass Objekte nach dem Anhalten zittern (und um zu vermeiden, dass sie durch Null geteilt werden, wenn wir versuchen, die Richtung eines perfekt stationären Objekts zu berechnen), sollten wir auch sicherstellen, dass die Geschwindigkeitsänderung niemals die ursprüngliche Größe der Geschwindigkeit überschreitet. Das heißt, Reibung sollte niemals dazu führen, dass sich ein Objekt rückwärts bewegt. Alles in allem ist eine vernünftige Implementierung von Reibung in einem Spiel:
Beachten Sie, dass dies auch dann funktioniert, wenn sich das Objekt in mehr als einer Dimension bewegt, sodass es sich
velocity
um einen Vektor handelt. (Natürlich kann der tatsächliche Code dafür mit Vektoren etwas anders aussehen, je nachdem, welche Notation Ihre Sprache für die Vektorarithmetik verwendet.)Sie können vor oder nach diesem Code weitere Geschwindigkeitsänderungen hinzufügen. Wenn Sie sie vorher hinzufügen, können ausreichend kleine Kräfte durch Reibung vollständig aufgehoben werden, was tatsächlich realistisch ist. Für weiteren Realismus möchten Sie möglicherweise statische Reibung implementieren, indem Sie die Konstante in der Reibungsberechnung variieren lassen, je nachdem, ob die Geschwindigkeit des Objekts zu Beginn dieses Zeitschritts ungleich Null war oder nicht.
Wenn Sie wirklich genau sein möchten, sollten Sie bei der Aktualisierung der Objektposition die Beschleunigung während des Zeitschritts berücksichtigen. Das heißt, anstatt nur zu tun
du solltest tun
Wo
velocity - delta_v / 2
ist der Durchschnitt der Geschwindigkeiten vor und nach dem Hinzufügendelta_v
.Diese letztere Annäherung an Newtons Bewegungsgesetze ist tatsächlich genau, solange sie
acceleration = delta_v / dt
konstant ist, und ist auf jeden Fall eine bessere Annäherung als die erstere, selbst für Änderungendelta_v
. Bei Spielen mit einem kleinen und konstanten Zeitschrittdt
ist der Unterschied jedoch im Allgemeinen nicht erkennbar, zumindest ohne Vergleich nebeneinander. Der Hauptvorteil der genaueren Form besteht darin, dass Objekttrajektorien weniger empfindlich auf Änderungen in reagierendt
.Ich sollte auch darauf hinweisen, dass (trockene) Reibung nicht die einzige Kraft ist, die sich bewegende Objekte verlangsamen kann. Beispielsweise erfahren Objekte, die sich durch Wasser oder Luft bewegen, einen Luftwiderstand , der einer Formel folgt, die im Allgemeinen ungefähr so aussieht
wo
a
undb
sind Konstanten, die von vielen Dingen abhängen, wie der Dichte und Viskosität der Flüssigkeit und der Masse, Größe und Form des Objekts, das sich durch sie bewegt. (Siehe den Wikipedia-Link oben für Details;a
oben entspricht Newton Drag, währendb
Stokes Drag entspricht.)Für makroskopische Objekte, die sich durch ziemlich nichtviskose Flüssigkeiten wie Wasser oder Luft bewegen,
b
sollte sie sehr klein oder sogar Null sein, während eine viskose Flüssigkeit wie Lava oder Melasse eine höhere erfordertb
. Spielen Sie für Spielzwecke einfach mit den Werten herum, bis Sie den gewünschten Effekt erzielen.Beachten Sie, dass im Gegensatz zur Trockenreibung die Widerstandskräfte ein sich bewegendes Objekt niemals vollständig zum Stillstand bringen, sodass wir uns im Allgemeinen keine Gedanken über versehentliche Richtungsumkehrungen machen müssen. Die Ausnahme ist if
(a * speed + b) * dt > 1
, was passieren kann, wenndt
es zu groß ist oder wenn das Objekt irgendwie eine ungewöhnlich hohe Geschwindigkeit erreicht; In diesem Fall besteht die Lösung entweder darin, sich dynamisch anzupassendt
, um sich für sich schnell bewegende Objekte zu verkleinern, oder einen Bewegungsintegrator höherer Ordnung zu verwenden (der wirklich über den Rahmen dieses Beitrags hinausgeht).quelle
Ohne mehr in Ihre Frage zu lesen und Ihre Frage so zu beantworten, wie sie ist, ist dies eine Vereinfachung Ihres Codes:
Um jedoch ein realistischeres (dh Newtonsches) Ergebnis zu erzielen, sollten Sie, wie andere angegeben haben, eine "Beschleunigungs" -Variable implementieren und Ihre Entität wie folgt aktualisieren:
Hier wäre die Geschwindigkeit (vermutlich) Ihre "Geschwindigkeits" -Variable. (Wenn ich mich aus der Physik richtig erinnere, ist "Geschwindigkeit" technisch ABS (Geschwindigkeit), sollte daher immer> = 0 sein)
quelle
Um diesen Code zu verkürzen, können Sie Folgendes tun:
Der Operator
(condition ? value_if_true : value_if_false )
wird als ternärer Operator bezeichnet. Mehr dazu lesen Sie hier:http://en.wikipedia.org/wiki/Ternary_conditional_operation
quelle