Frequentemente, desejarei usar um valor de velocidade como 2,5 para mover meu personagem em um jogo baseado em pixels. A detecção de colisão geralmente será mais difícil se eu fizer isso. Então, acabo fazendo algo assim:
moveX(2);
if (ticks % 2 == 0) { // or if (moveTime % 2 == 0)
moveX(1);
}
Eu me encolho por dentro toda vez que tenho que escrever isso, existe uma maneira mais limpa de mover um personagem com valores de velocidade não inteiros ou continuarei preso a fazê-lo para sempre?
c++
2d
movement
floating-point
Acumulador
fonte
fonte
Respostas:
Bresenham
Antigamente, quando as pessoas ainda escreviam suas próprias rotinas básicas de vídeo para desenhar linhas e círculos, não era incomum usar o algoritmo de linha de Bresenham para isso.
Bresenham resolve esse problema: você deseja desenhar uma linha na tela que mova os
dx
pixels na direção horizontal e ao mesmo tempo estenda osdy
pixels na direção vertical. Existe um caractere "flutuante" inerente às linhas; mesmo se você tiver pixels inteiros, você acaba com inclinações racionais.O algoritmo precisa ser rápido, o que significa que ele pode usar apenas aritmética inteira; e também foge sem multiplicação ou divisão, apenas adição e subtração.
Você pode adaptar isso para o seu caso:
"x / y" aqui não é o local na tela, mas o valor de uma de suas dimensões no tempo. Obviamente, se o seu sprite estiver sendo executado em uma direção arbitrária na tela, você terá vários Bresenhams executando separadamente, 2 para 2D, 3 para 3D.
Exemplo
Digamos que você queira mover seu personagem em um movimento simples de 0 a 25 ao longo de um dos seus eixos. Como está se movendo com a velocidade 2.5, ele chegará lá no quadro 10.
É o mesmo que "desenhar uma linha" de (0,0) a (10,25). Pegue o algoritmo de linha de Bresenham e deixe correr. Se você fizer o que é certo (e quando estudá-lo, rapidamente ficará claro como o faz), gerará 11 "pontos" para você (0,0), (1,2), (2, 5), (3,7), (4,10) ... (10,25).
Dicas sobre adaptação
Se você pesquisar esse algoritmo no Google e encontrar algum código (a Wikipedia possui um tratado bastante grande), há algumas coisas que você precisa observar:
dx
edy
. Você está interessado em um caso específico (por exemplo, você nunca terádx=0
).dx
edy
são positivos, negativos, e também seabs(dx)>abs(dy)
ou não. É claro que você também escolhe o que precisa aqui. Você deve ter certeza de que a direção que é aumentada a1
cada escala é sempre a direção do "relógio".Se você aplicar essas simplificações, o resultado será muito simples e se livrará completamente de quaisquer reais.
fonte
Existe uma ótima maneira de fazer exatamente o que você deseja.
Além de uma
float
velocidade, você precisará ter uma segundafloat
variável que conterá e acumulará uma diferença entre a velocidade real e a velocidade arredondada . Essa diferença é então combinada com a própria velocidade.Saída:
fonte
Use valores flutuantes para movimento e valores inteiros para colisão e renderização.
Aqui está um exemplo:
Quando você se move, você usa o
move()
que acumula as posições fracionárias. Mas a colisão e a renderização podem lidar com posições integrais usando agetPosition()
funçãofonte