Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting "giggly" effect when slowly moving a sprite

Tags:

c#

unity3d

sprite

I cooked up a quick animation to demonstrate what's happening here:

Animation demonstrating source of ripple

The grid represents the output pixels of your display. I've overlaid on top of it the sliding sprite we want to sample, if we could render it with unlimited sub-pixel resolution.

The dots in the center of each grid cell represent their sampling point. Because we're using Nearest-Nieghbour/Point filtering, that's the only point in the texture they pay attention to. When the edge of a new colour crosses that sampling point, the whole pixel changes colour at once.

The trouble arises when the source texel grid doesn't line up with our output pixels. In the example above, the sprite is 16x16 texels, but I've scaled it to occupy 17x17 pixels on the display. That means, somewhere in every frame, some texels must get repeated. Where this happens changes as we move the sprite around.

Because each texel is rendered slightly larger than a pixel, there's a moment where it completely bridges the sampling points of two adjacent pixels. Both sampling points land within the same enlarged texel, so both pixels see that texel as the nearest one to sample from, and the texel gets output to the screen in two places.

In this case, since there's only a 1/16th scale difference, each texel is only in this weird situation for a frame or two, then it shifts to its neighbour, creating a ripple of doubled pixels that appears to slide across the image.

(One could view this as a type of moiré pattern resulting from the interaction of the texel grid and the sampling grid when they're dissimilar)

The fix is to ensure that you scale your pixel art so each texel is displayed at the size of an integer multiple of pixels.

Either 1:1

Animation showing ripple-free rendering at 1:1 scale

Or 2:1, 3:1...

Animation showing ripple-free rendering at 3:1 scale

Using a higher multiple lets the sprite move in increments shorter than its own texel size, without localized stretching that impacts the intended appearance of the art.

So: pay close attention to the resolution of your output and the scaling applied to your assets, to ensure you keep an integer multiple relationship between them. The blog post that CAD97 links has practical steps you can take to achieve this.

Edit: To demonstrate this in the Unity project you've uploaded, I modified the camera settings to match your pixels to units setting, and laid out the following test. The Mario at the top has a slightly non-integer texel-to-pixel ratio (1.01:1), while the Mario at the bottom has 1:1. You can see only the top Mario exhibits rippling artifacts:

Two Marios, one exhibiting artifacts


You might be interested in this blog post about making "pixel-perfect" 2D games in Unity.

Some relevant excerpts:

If you start your pixel game with all the default settings in Unity, it will look terrible!

The secret to making your pixelated game look nice is to ensure that your sprite is rendered on a nice pixel boundary. In other words, ensure that each pixel of your sprite is rendered on one screen pixel.

These other settings are essential to make things as crisp as possible.

On the sprite:

  • Ensure your sprites are using lossless compression e.g. True Color
  • Turn off mipmapping
  • Use Point sampling

In Render Quality Settings:

  • Turn off anisotropic filtering
  • Turn off anti aliasing

Turn on pixel snapping in the sprite shader by creating a custom material that uses the Sprite/Default shader and attaching it to the SpriteRenderer.

Also, I'd just like to point out that Unless you are applying Physics, Never Use FixedUpdate. Also, if your sprite has a Collider and is moving, it should have a Kinematic RigidBody attached even if you're never going to use physics, to tell the engine that the Collider is going to move.