Comment éviter «l'effet d'escalier» dans le mouvement pixel art?

21

Je rend les sprites aux coordonnées de pixels exactes pour éviter l'effet de flou provoqué par l'anticrénelage (les sprites sont du pixel-art et auraient l'air horribles s'ils étaient filtrés). Cependant, comme le mouvement des objets implique une vitesse, une gravité et des interactions physiques variables, la trajectoire est calculée avec une précision sous-pixel.

À des vitesses d'espace d'écran suffisamment grandes (vΔt supérieur à 2 ou 3 pixels), cela fonctionne très bien. Cependant, lorsque la vitesse est faible, un effet d'escalier notable peut apparaître, en particulier le long des lignes diagonales. Ce n'est plus un problème à des vitesses d'espace d'écran très lentes (v << 1 pixel par seconde) donc je ne cherche qu'une solution pour des valeurs de vitesse intermédiaires.

A gauche se trouve la trajectoire tracée pour une grande vitesse, obtenue par simple arrondi des coordonnées de l'objet. Au milieu, vous pouvez voir ce qui se passe lorsque la vitesse diminue, et l'effet d'escalier dont je parle. A droite, le lieu de la trajectoire que j'aimerais obtenir.

coordonnées en pixels pour la trajectoire de l'objet

Je m'intéresse aux idées d'algorithmes pour filtrer la trajectoire afin de minimiser l'aliasing, tout en conservant le comportement d'origine à grandes et petites vitesses. J'ai accès à Δt, à la position et à la vitesse instantanées, ainsi qu'à un nombre arbitraire de valeurs précédentes, mais comme il s'agit d'une simulation en temps réel, je ne connais pas les valeurs futures (bien que si nécessaire, une estimation pourrait être extrapolée sous certaines hypothèses) . Notez qu'en raison de la simulation physique, des changements de direction soudains peuvent également se produire.

sam hocevar
la source

Réponses:

18

Voici un aperçu rapide, du haut de ma tête, d'un algorithme qui devrait fonctionner assez bien.

  1. Tout d'abord, calculez la direction dans laquelle l'objet se déplace et vérifiez s'il est plus proche de l'horizontale ou de la verticale.
  2. Si la direction est plus proche de la verticale (horizontale), ajustez la position de l'objet le long du vecteur de direction au centre de la rangée (colonne) de pixels la plus proche.
  3. Arrondissez la position au centre du pixel le plus proche.

En pseudocode:

if ( abs(velocity.x) > abs(velocity.y) ) {
    x = round(position.x);
    y = round(position.y + (x - position.x) * velocity.y / velocity.x);
} else {
    y = round(position.y);
    x = round(position.x + (y - position.y) * velocity.x / velocity.y);
}

Edit: Oui, testé, fonctionne très bien.

Ilmari Karonen
la source
+1, cela fonctionne étonnamment bien! Je remarque des sauts en arrière étranges avec un mouvement circulaire à des vitesses lentes, car le réglage peut être effectué dans la direction opposée au vecteur de vitesse (ce qui est généralement OK, mais pas avec de petites courbures de trajectoire). Cela peut être résolu en multipliant velocity.y / velocity.xpar un facteur de correction proportionnel à la vitesse.
sam hocevar
@Sam: Vous voulez dire un petit rayon de braquage (= courbure élevée), non? Cela pourrait en effet poser des problèmes d'extrapolation linéaire à basse vitesse. (Fondamentalement, cela fonctionne tant que la vitesse au carré par accélération est beaucoup plus grande que 1 pixel.) Une solution (klugey) pourrait être de se souvenir de la dernière position arrondie et de la réutiliser si elle est plus proche de la position réelle que celle nouvellement calculée. (On pourrait aussi essayer une extrapolation d'ordre supérieur, mais les formules deviennent plutôt laides.)
Ilmari Karonen
En effet, je voulais dire petit rayon. Ma faute. Et merci pour les conseils supplémentaires; la performance n'est pas critique là-bas, donc je peux me permettre d'améliorer la qualité.
sam hocevar
3

Il n'y a pas grand-chose que vous puissiez vraiment faire à ce sujet pour un monde basé sur la physique générale. Si tous vos objets se déplaçaient le long de lignes ou de cercles spécifiques, vous pourriez faire quelque chose. Mais vous opérez sous la physique réelle. L'objet est là où la physique le met; vous dessinez simplement une approximation en pixels de cet emplacement.

C'est généralement quelque chose que vous devez accepter si vous voulez vous en tenir aux coordonnées en pixels. Cela ne devrait pas être trop perceptible à moins que vous n'affichiez à une résolution incroyablement petite (inférieure à 640x480, bien que cela dépende de la résolution et de la taille natives de l'écran).

Nicol Bolas
la source
Même à des résolutions élevées, le rendu est mis à l'échelle (voisin le plus proche) pour améliorer l'apparence oldschool. Il s'agit d'une décision de direction artistique.
sam hocevar
@SamHocevar: Si vous voulez une "apparence oldschool", pourquoi ne voulez-vous pas une "apparence oldschool" complète ? Pourquoi l'escalier, que n'importe quel jeu "oldschool" aurait eu, ne fait-il pas partie de l'effet global que vous souhaitez obtenir?
Nicol Bolas
Je ne pense pas qu'un jeu oldschool décent aurait implémenté un mouvement diagonal qui a cet effet d'escalier, car il aurait ressemblé à de la merde. Ne pas ressembler à de la merde est une partie importante de l'effet oldschool que je souhaite atteindre :-)
sam hocevar
@SamHocevar: La plupart des jeux de la vieille école sont des jeux d'action, et ne se déplacent donc pas assez lentement pour s'en rendre compte. Ils ont également tendance à ne pas se déplacer le long des courbes. Le jeu en particulier auquel je pensais était Solar Jetman, qui a beaucoup cet effet en se déplaçant lentement. Certes, la caméra est toujours centrée sur vous, donc vous la remarquez dans le mouvement mondial, mais elle est bien là.
Nicol Bolas
3

Lorsque le mouvement en attente est perpendiculaire au dernier mouvement (dans l'espace écran), ignorez-le et utilisez les dernières coordonnées d'écran. Si cela conduit à un bégaiement aussi mauvais que l'escalier, vous pouvez essayer de déplacer la somme du dernier mouvement en attente.

Je pense que le problème réside dans v <sqrt (2). v> sqrt (2) doit toujours se déplacer au moins sur une diagonale complète, en évitant l'effet d'escalier. Peut-être utile pour l'élagage qui nécessite des comparaisons de mouvements antérieures.

mghicks
la source
+1 pour indiquer une limite supérieure pour v. La suggestion d'Ilmari est plus détaillée mais vous fournissez des informations utiles.
sam hocevar