Oft möchte ich einen Geschwindigkeitswert wie 2,5 verwenden, um meinen Charakter in einem pixelbasierten Spiel zu bewegen. Die Kollisionserkennung wird in der Regel schwieriger, wenn ich das mache. Also mache ich am Ende so etwas:
moveX(2);
if (ticks % 2 == 0) { // or if (moveTime % 2 == 0)
moveX(1);
}
Ich schaudere jedes Mal, wenn ich das schreiben muss, nach innen. Gibt es eine sauberere Möglichkeit, ein Zeichen mit nicht ganzzahligen Geschwindigkeitswerten zu verschieben, oder bleibe ich dabei für immer stecken?
c++
2d
movement
floating-point
Akkumulator
quelle
quelle
Antworten:
Bresenham
In alten Zeiten, als die Leute noch ihre eigenen grundlegenden Videoroutinen zum Zeichnen von Linien und Kreisen schrieben, war es nicht ungewöhnlich, dafür den Bresenham-Linienalgorithmus zu verwenden.
Bresenham löst dieses Problem: Sie möchten eine Linie auf dem Bildschirm zeichnen, die
dx
Pixel in horizontaler Richtung verschiebt und gleichzeitigdy
Pixel in vertikaler Richtung überspannt . Zeilen haben ein inhärentes "fließendes" Zeichen. Selbst wenn Sie ganzzahlige Pixel haben, haben Sie rationale Neigungen.Der Algorithmus muss jedoch schnell sein, was bedeutet, dass er nur Ganzzahlarithmetik verwenden kann. und es kommt auch ohne Multiplikation oder Division davon, nur Addition und Subtraktion.
Sie können das für Ihren Fall anpassen:
"x / y" ist hier nicht die Position auf dem Bildschirm, sondern der Wert einer Ihrer Dimensionen in der Zeit. Wenn Ihr Sprite in einer beliebigen Richtung über den Bildschirm läuft, werden natürlich mehrere Bresenhams separat ausgeführt, 2 für 2D, 3 für 3D.
Beispiel
Angenommen, Sie möchten Ihren Charakter in einer einfachen Bewegung von 0 bis 25 entlang einer Ihrer Achsen bewegen. Da es sich mit Geschwindigkeit 2.5 bewegt, wird es dort bei Frame 10 ankommen.
Dies ist dasselbe wie "Zeichnen einer Linie" von (0,0) bis (10,25). Schnappen Sie sich Bresenhams Linienalgorithmus und lassen Sie ihn laufen. Wenn Sie es richtig machen (und wenn Sie es studieren, wird es sehr schnell klar, wie Sie es richtig machen), dann werden 11 "Punkte" für Sie generiert (0,0), (1,2), (2, 5), (3,7), (4,10) ... (10,25).
Hinweise zur Anpassung
Wenn Sie diesen Algorithmus googeln und Code finden (Wikipedia hat einen ziemlich großen Vertrag), gibt es einige Dinge, auf die Sie achten müssen:
dx
unddy
. Sie interessieren sich jedoch für einen bestimmten Fall (das heißt, Sie werden ihn niemals habendx=0
).dx
und obdy
positiv, negativ und auch obabs(dx)>abs(dy)
oder nicht. Natürlich wählen Sie hier auch aus, was Sie brauchen. Sie müssen besonders darauf achten, dass die Richtung, die mit1
jedem Tick erhöht wird, immer Ihre "Uhr" -Richtung ist.Wenn Sie diese Vereinfachungen anwenden, wird das Ergebnis in der Tat sehr einfach sein und alle Realitäten vollständig beseitigen.
quelle
Es gibt eine großartige Möglichkeit, genau das zu tun, was Sie wollen.
Zusätzlich zu einer
float
Geschwindigkeit benötigen Sie eine zweitefloat
Variable, die eine Differenz zwischen der reellen Geschwindigkeit und der gerundeten Geschwindigkeit enthält und akkumuliert . Diese Differenz wird dann mit der Geschwindigkeit selbst kombiniert.Ausgabe:
quelle
Verwenden Sie Floating-Werte für die Bewegung und Integer-Werte für Kollision und Rendering.
Hier ist ein Beispiel:
Wenn Sie sich bewegen, verwenden Sie,
move()
die die Teilpositionen akkumuliert. Kollision und Rendering können jedoch mithilfe dergetPosition()
Funktion integrale Positionen verarbeiten .quelle