Les pixels sont activés ou désactivés. La quantité minimale que vous pouvez déplacer un sprite est d'un seul pixel. Alors, comment faire pour que le sprite se déplace plus lentement que 1 pixel par image?
La façon dont je l'ai fait était d'ajouter la vitesse à une variable et de tester si elle avait atteint 1 (ou -1). Si c'était le cas, je déplacerais le sprite et redéfinirais la variable à 0, comme ceci:
update(dt):
temp_dx += speed * dt
temp_dy += speed * dt
if (temp_dx > 1)
move sprite
reset temp_dx to 0
if (tempy_dy > 1)
move sprite
reset temp_dy to 0
J'ai détesté cette approche car elle semble idiote et le mouvement du sprite semble très saccadé. Alors, comment implémenteriez-vous le mouvement sous-pixel?
Réponses:
Il y a plusieurs options:
Faites comme vous. Vous avez déjà dit que cela n'avait pas l'air fluide. Il y a cependant quelques défauts avec votre méthode actuelle. Pour
x
, vous pouvez utiliser les éléments suivants:cela devrait être mieux. J'ai changé les instructions if pour utiliser 0,5, car lorsque vous passez 0,5, vous êtes plus proche de la valeur suivante que de la précédente. J'ai utilisé des boucles while pour permettre le mouvement de plus de 1 pixel par pas de temps (ce n'est pas la meilleure façon de le faire, mais cela rend le code compact et lisible). Pour les objets se déplaçant lentement, cela restera cependant nerveux, car nous n'avons pas traité le problème fondamental de l'alignement des pixels.
Ayez plusieurs graphiques pour votre sprite et utilisez-en un différent en fonction du décalage de sous-pixel. Pour effectuer un lissage de sous-pixels uniquement en x, par exemple, vous pouvez créer un graphique pour votre image-objet à
x+0.5
, et si la position est entrex+0.25
etx+0.75
, utilisez cette image-objet au lieu de votre original. Si vous voulez un positionnement plus fin, créez simplement plus de graphiques en sous-pixels. Si vous faites cela dansx
et quey
votre nombre de rendus peut rapidement monter en flèche, car le nombre évolue avec le carré du nombre d'intervalles: un espacement de0.5
nécessiterait 4 rendus,0.25
nécessiterait 16.Suréchantillon. Il s'agit d'une façon paresseuse (et potentiellement très coûteuse) de créer une image avec une résolution sous-pixel. Essentiellement, doublez (ou plus) la résolution à laquelle vous effectuez le rendu de votre scène, puis réduisez-la au moment de l'exécution. Je recommanderais des soins ici, car les performances peuvent chuter rapidement. Une façon moins agressive de faire cela serait simplement de suréchantillonner votre sprite et de le réduire à l'exécution.
Comme suggéré par Zehelvion , il se peut que la plate-forme que vous utilisez le prenne déjà en charge. Si vous êtes autorisé à spécifier des coordonnées x et y non entières, il peut y avoir des options pour modifier le filtrage de texture. Le plus proche voisin est souvent la valeur par défaut, ce qui entraînera un mouvement "saccadé". Un autre filtrage (linéaire / cubique) entraînerait un effet beaucoup plus fluide.
Le choix entre 2. 3. et 4. dépend de la façon dont vos graphiques sont implémentés. Un style graphique bitmap convient beaucoup mieux au pré-rendu, tandis qu'un style vectoriel peut convenir au suréchantillonnage de sprites. Si votre système le prend en charge, 4. pourrait bien être le chemin à parcourir.
la source
Une façon dont de nombreux jeux old-skool ont résolu (ou caché) ce problème était d'animer le sprite.
Autrement dit, si votre image-objet devait se déplacer de moins d'un pixel par image (ou, surtout, si le rapport pixels / image devait être quelque chose d'étrange comme 2 pixels en 3 images), vous pourriez masquer la secousse en faisant un n boucle d'animation d'image qui, sur ces n images, a fini par déplacer le sprite de quelques k < n pixels.
Le fait est que, tant que l'image-objet se déplace toujours d' une manière ou d' une autre sur chaque image, il n'y aura jamais une seule image où l'image-image entière "se branlerait" soudainement en avant.
Je n'ai pas pu trouver un véritable sprite d'un ancien jeu vidéo pour illustrer cela (bien que je pense par exemple que certaines des animations de fouille de Lemmings étaient comme ça), mais il s'avère que le motif "planeur" du jeu de la vie de Conway fait un très belle illustration:
Animation par Kieff / Wikimedia Commons , utilisée sous la licence CC-By-SA 3.0 .
Ici, les petits blocs de pixels noirs rampant rampant vers le bas et à droite sont les planeurs. Si vous regardez attentivement, vous remarquerez qu'il faut quatre images d'animation pour parcourir un pixel en diagonale, mais comme elles se déplacent d' une manière ou d' une autre sur chacune de ces images, le mouvement n'a pas l'air si saccadé (enfin, du moins plus) saccadé que quoi que ce soit regarde cette fréquence d'images, de toute façon).
la source
La position de l'image-objet doit être conservée sous forme de quantité à virgule flottante et arrondie à un entier juste avant l'affichage.
Vous pouvez également maintenir l'image-objet à une super résolution et sous-échantillonner l'image-objet avant l'affichage. Si vous mainteniez l'image-objet à une résolution d'affichage de 3x, vous auriez 9 images-objets réelles différentes selon la position des sous-pixels de l'image-objet.
la source
La seule vraie solution ici est d'utiliser le filtrage bilinéaire. L'idée est de laisser le GPU calculer la valeur de chaque pixel en fonction des quatre pixels d'image-objet qui se chevauchent avec lui. C'est une technique courante et efficace. Vous avez simplement besoin de placer le sprite sur une plaine 2D (un panneau d'affichage) comme texture; puis utilisez le GPU pour rendre ces plaines. Cela fonctionne bien, mais attendez-vous à obtenir des résultats quelque peu flous et à perdre l'aspect 8 ou 16 bits si vous le visiez.
avantages:
Solution matérielle déjà implémentée, très rapide.
les inconvénients:
Perte de fidélité 8 bits / 16 bits.
la source
Le point flottant est la façon normale de le faire, surtout si vous effectuez finalement un rendu sur une cible GL où le matériel est assez satisfait d'un polygone ombré avec des coordonnées flottantes.
Cependant, il existe une autre façon de faire ce que vous faites actuellement, mais un peu moins saccadée: le point fixe. Une valeur de position de 32 bits peut représenter des valeurs de 0 à 4 294 967 295, même si votre écran a presque certainement moins de 4 000 pixels le long de l'un ou l'autre axe. Donc, mettez un "point binaire" fixe (par analogie avec le point décimal) au milieu et vous pouvez représenter les positions de pixels de 0 à 65536 avec encore 16 bits de résolution sous-pixel. Vous pouvez ensuite ajouter des nombres comme d'habitude, il vous suffit de vous rappeler de convertir en décalant vers la droite de 16 bits chaque fois que vous convertissez en espace d'écran ou lorsque vous multipliez deux nombres ensemble.
(J'ai écrit un moteur 3D en virgule fixe 16 bits à l'époque où j'avais un Intel 386 sans unité à virgule flottante. Il atteignait la vitesse aveuglante de 200 polygones ombragés Phong par image.)
la source
En plus des autres réponses ici, vous pouvez utiliser le tramage dans une certaine mesure. Le tramage est l'endroit où le bord des pixels d'un objet est de couleur plus claire / plus foncée pour correspondre à l'arrière-plan, ce qui donne un bord plus doux. Par exemple, disons que vous aviez un carré de 4 pixels avec lequel je vais approximativement:
Si vous deviez déplacer ce 1/2 pixel vers la droite, rien ne bougerait vraiment. Mais avec quelques tramages, vous pourriez faire un peu illusion de mouvement. Considérez O comme noir et o comme gris, vous pourriez donc faire:
Dans un cadre et
Ensuite. Le "carré" réel avec les bords grisés prendrait en fait 3 pixels de diamètre, mais comme ils sont gris plus clair que le noir, ils apparaissent plus petits. Cela peut être difficile à coder, cependant, et je ne suis pas vraiment au courant de quoi que ce soit qui le fasse actuellement. À peu près au moment où ils ont commencé à utiliser le tramage pour les choses, les résolutions sont rapidement devenues meilleures et il n'y avait pas autant de besoins que dans des choses telles que la manipulation d'images.
la source