A menudo querré usar un valor de velocidad como 2.5 para mover a mi personaje en un juego basado en píxeles. Sin embargo, la detección de colisión generalmente será más difícil si hago eso. Así que termino haciendo algo como esto:
moveX(2);
if (ticks % 2 == 0) { // or if (moveTime % 2 == 0)
moveX(1);
}
Me estremezco por dentro cada vez que tengo que escribir eso, ¿hay una forma más limpia de mover un personaje con valores de velocidad no enteros o me quedaré atrapado haciendo esto para siempre?
c++
2d
movement
floating-point
Acumulador
fuente
fuente
Respuestas:
Bresenham
En los viejos tiempos, cuando la gente todavía escribía sus propias rutinas de video básicas para dibujar líneas y círculos, no era extraño usar el algoritmo de línea de Bresenham para eso.
Bresenham resuelve este problema: desea dibujar una línea en la pantalla que mueva los
dx
píxeles en la dirección horizontal y al mismo tiempo abarque losdy
píxeles en la dirección vertical. Hay un carácter "flotante" inherente a las líneas; incluso si tienes píxeles enteros, terminas con inclinaciones racionales.Sin embargo, el algoritmo debe ser rápido, lo que significa que solo puede usar aritmética de enteros; y también se escapa sin multiplicación o división, solo suma y resta.
Puede adaptar eso para su caso:
"x / y" aquí no es la ubicación en la pantalla, sino el valor de una de sus dimensiones en el tiempo. Obviamente, si su sprite se ejecuta en una dirección arbitraria a través de la pantalla, tendrá varios Bresenhams ejecutándose por separado, 2 para 2D, 3 para 3D.
Ejemplo
Digamos que quieres mover a tu personaje en un movimiento simple de 0 a 25 a lo largo de uno de tus ejes. Como se mueve con la velocidad 2.5, llegará allí en el cuadro 10.
Esto es lo mismo que "dibujar una línea" de (0,0) a (10,25). Toma el algoritmo de línea de Bresenham y déjalo correr. Si lo haces bien (y cuando lo estudias, rápidamente se hará claro cómo hacerlo bien), entonces generará 11 "puntos" para ti (0,0), (1,2), (2, 5), (3,7), (4,10) ... (10,25).
Consejos sobre la adaptación
Si buscas en Google ese algoritmo y encuentras algún código (Wikipedia tiene un tratado bastante extenso), hay algunas cosas que debes tener en cuenta:
dx
ydy
. Sin embargo, está interesado en un caso específico (es decir, nunca lo tendrádx=0
).dx
ydy
son positivas, negativas, y también siabs(dx)>abs(dy)
o no. Por supuesto, también elige lo que necesita aquí. Debe asegurarse especialmente de que la dirección que aumenta con1
cada tic sea siempre la dirección de su "reloj".Si aplica estas simplificaciones, el resultado será muy simple y eliminará por completo cualquier real.
fuente
Hay una excelente manera de hacer exactamente lo que quieres.
Además de una
float
velocidad, necesitará tener una segundafloat
variable que contendrá y acumulará una diferencia entre la velocidad real y la velocidad redondeada . Esta diferencia se combina con la velocidad misma.Salida:
fuente
Utilice valores flotantes para movimiento y valores enteros para colisión y renderizado.
Aquí hay un ejemplo:
Cuando te mueves, usas lo
move()
que acumula las posiciones fraccionarias. Pero la colisión y la representación pueden ocuparse de posiciones integrales mediante el uso de lagetPosition()
función.fuente