Często będę chciał użyć wartości prędkości, takiej jak 2,5, do poruszania moją postacią w grze opartej na pikselach. Wykrywanie kolizji będzie jednak ogólnie trudniejsze, jeśli to zrobię. W rezultacie robię coś takiego:
moveX(2);
if (ticks % 2 == 0) { // or if (moveTime % 2 == 0)
moveX(1);
}
Kulę się w środku za każdym razem, gdy muszę to napisać, czy istnieje czystszy sposób na poruszenie postaci o wartościach liczb całkowitych innych niż całkowite, czy też utknę robienie tego na zawsze?
c++
2d
movement
floating-point
Akumulator
źródło
źródło
Odpowiedzi:
Bresenham
W dawnych czasach, kiedy ludzie nadal pisali własne podstawowe procedury wideo do rysowania linii i okręgów, nie było niespotykane stosowanie do tego algorytmu linii Bresenhama.
Bresenham rozwiązuje ten problem: chcesz narysować linię na ekranie, która przesuwa
dx
piksele w kierunku poziomym, jednocześnie rozciągającdy
piksele w kierunku pionowym. W liniach występuje nieodłączny „pływający” charakter; nawet jeśli masz piksele całkowite, skończysz na racjonalnych nachyleniach.Algorytm musi być szybki, co oznacza, że może używać wyłącznie arytmetyki liczb całkowitych; i unika się bez mnożenia lub dzielenia, tylko dodawanie i odejmowanie.
Możesz to dostosować do swojego przypadku:
„x / y” tutaj nie jest położeniem na ekranie, ale wartością jednego z twoich wymiarów w czasie. Oczywiście, jeśli twój duszek biegnie w dowolnym kierunku po ekranie, będziesz mieć wiele Bresenhamów biegnących osobno, 2 dla 2D, 3 dla 3D.
Przykład
Powiedzmy, że chcesz przenieść swoją postać prostym ruchem od 0 do 25 wzdłuż jednej ze swoich osi. Ponieważ porusza się z prędkością 2,5, będzie tam przy klatce 10.
Jest to to samo co „rysowanie linii” od (0,0) do (10,25). Chwyć algorytm linii Bresenhama i pozwól mu działać. Jeśli zrobisz to dobrze (a kiedy ją przestudiujesz, bardzo szybko stanie się jasne, jak to zrobić dobrze), to wygeneruje dla ciebie 11 „punktów” (0,0), (1,2), (2, 5), (3,7), (4,10) ... (10,25).
Wskazówki dotyczące adaptacji
Jeśli przejdziesz do tego algorytmu i znajdziesz jakiś kod (Wikipedia zawiera dość duży traktat), musisz pamiętać o kilku rzeczach:
dx
idy
. Jesteś zainteresowany jednym konkretnym przypadkiem (tzn. Nigdy go nie będziesz miećdx=0
).dx
idy
są pozytywne, negatywne, a także czyabs(dx)>abs(dy)
nie. Tutaj oczywiście również wybierasz to, czego potrzebujesz. Musisz szczególnie upewnić się, że kierunek, który zwiększa się o1
każdy tik, jest zawsze twoim kierunkiem „zegarowym”.Jeśli zastosujesz te uproszczenia, wynik będzie naprawdę bardzo prosty i całkowicie pozbędziesz się reali.
źródło
Jest świetny sposób na robienie dokładnie tego, co chcesz.
Oprócz
float
prędkości musisz mieć drugąfloat
zmienną, która będzie zawierać i kumulować różnicę między prędkością rzeczywistą a prędkością zaokrągloną . Różnica ta jest następnie łączona z samą prędkością.Wydajność:
źródło
Użyj wartości zmiennoprzecinkowych dla wartości ruchu i liczb całkowitych dla kolizji i renderowania.
Oto przykład:
Kiedy się poruszasz, używasz,
move()
który akumuluje pozycje ułamkowe. Ale kolizja i rendering mogą poradzić sobie z integralnymi pozycjami za pomocągetPosition()
funkcji.źródło