Souvent, je veux utiliser une valeur de vitesse telle que 2,5 pour déplacer mon personnage dans un jeu basé sur des pixels. La détection des collisions sera généralement plus difficile si je le fais, cependant. Je finis donc par faire quelque chose comme ça:
moveX(2);
if (ticks % 2 == 0) { // or if (moveTime % 2 == 0)
moveX(1);
}
Je grince des dents à l'intérieur chaque fois que je dois écrire cela, y a-t-il un moyen plus propre de déplacer un personnage avec des valeurs de vitesse non entières ou vais-je rester bloqué pour toujours?
c++
2d
movement
floating-point
Accumulateur
la source
la source
Réponses:
Bresenham
Autrefois, lorsque les gens écrivaient encore leurs propres routines vidéo de base pour dessiner des lignes et des cercles, il n'était pas rare d'utiliser l'algorithme de ligne de Bresenham pour cela.
Bresenham résout ce problème: vous voulez tracer une ligne sur l'écran qui déplace les
dx
pixels dans le sens horizontal tout en couvrant lesdy
pixels dans le sens vertical. Il y a un caractère "flottant" inhérent aux lignes; même si vous avez des pixels entiers, vous vous retrouvez avec des inclinations rationnelles.Cependant, l'algorithme doit être rapide, ce qui signifie qu'il ne peut utiliser que l'arithmétique entière; et il s'en sort aussi sans multiplication ni division, seulement addition et soustraction.
Vous pouvez l'adapter à votre cas:
"x / y" ici ne sont pas l'emplacement sur l'écran, mais la valeur d'une de vos dimensions dans le temps. Évidemment, si votre sprite s'exécute dans une direction arbitraire à travers l'écran, vous aurez plusieurs Bresenhams fonctionnant séparément, 2 pour 2D, 3 pour 3D.
Exemple
Supposons que vous souhaitiez déplacer votre personnage d'un simple mouvement de 0 à 25 le long de l'un de vos axes. Comme il se déplace à la vitesse 2,5, il y arrivera à l'image 10.
Cela revient à "tracer une ligne" de (0,0) à (10,25). Prenez l'algorithme de ligne de Bresenham et laissez-le fonctionner. Si vous le faites correctement (et lorsque vous l’étudiez, il deviendra très rapidement clair comment vous le faites correctement), alors il générera 11 "points" pour vous (0,0), (1,2), (2, 5), (3,7), (4,10) ... (10,25).
Conseils sur l'adaptation
Si vous recherchez cet algorithme sur Google et que vous trouvez du code (Wikipédia a un traité assez large à ce sujet), vous devez faire attention à certaines choses:
dx
etdy
. Cependant, vous êtes intéressé par un cas spécifique (c'est-à-dire que vous ne l'aurez jamaisdx=0
).dx
etdy
sont positifs, négatifs, et aussi siabs(dx)>abs(dy)
ou non. Vous choisissez bien sûr également ce dont vous avez besoin ici. Vous devez vous assurer particulièrement que la direction qui est augmentée à1
chaque tick est toujours la direction de votre "horloge".Si vous appliquez ces simplifications, le résultat sera en effet très simple et éliminera complètement tous les réels.
la source
Il existe un excellent moyen de faire exactement ce que vous voulez.
En plus d'une
float
vitesse, vous aurez besoin d'avoir une deuxièmefloat
variable qui contiendra et accumulera une différence entre la vitesse réelle et la vitesse arrondie . Cette différence est ensuite combinée avec la vitesse elle-même.Sortie:
la source
Utilisez des valeurs flottantes pour le mouvement et des valeurs entières pour la collision et le rendu.
Voici un exemple:
Lorsque vous vous déplacez, vous utilisez
move()
ce qui accumule les positions fractionnaires. Mais la collision et le rendu peuvent traiter des positions intégrales en utilisant lagetPosition()
fonction.la source