Wie andere angemerkt haben, lautet die korrekte Formel für die Verzögerung aufgrund von Trockenreibung
velocity += constant * dt * -dir( velocity )
Dabei wird dir(v)
ein Vektor mit Längeneinheiten zurückgegeben, der in dieselbe Richtung wie zeigt v
. (Für eindimensionale Bewegungen, dir(v) = 1
ob v > 0
und dir(v) = -1
wenn v < 0
.) Eine Möglichkeit, sie zu berechnen, ist as
dir(v) = v / abs(v)
wo abs(v)
gibt die Länge oder Größe des Vektors v
.
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:
friction = constant * dt;
speed = abs( velocity );
if ( friction < speed ) {
delta_v = friction * -( velocity / speed );
} else {
delta_v = -velocity; // the object stops, or was stopped already
}
velocity += delta_v;
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
velocity += delta_v;
position += velocity * dt;
du solltest tun
velocity += delta_v;
position += ( velocity - delta_v / 2 ) * dt;
Wo velocity - delta_v / 2
ist der Durchschnitt der Geschwindigkeiten vor und nach dem Hinzufügen delta_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 Änderungen delta_v
. Bei Spielen mit einem kleinen und konstanten Zeitschritt dt
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 reagieren dt
.
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
delta_v = ( a * speed + b ) * -velocity * dt
wo a
und b
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ährend b
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 erfordert b
. 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, wenn dt
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 anzupassen dt
, 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).