Pixel Perfect in Unity
Unity wasn’t built to make pixel perfect 2D games, but with a few simple tricks you can use it just fine. I am using Unity 2021.2.8f and URP, but most of this should be transferable to other versions.
Import settings for images
The Pixel Per Unit parameter in the import settings of the image assets should be the resolution of you tiles for example. I’ve got 16x16 pixels for each asset so I have set 16.
The Filter Mode is the algorithm used by Unity to display a scaled or rotated image. Here we don’t anything so we choose “Point (no filter”), otherwise the image would get blurred.
For those who want to know what Filter Mode is, image you had a 10x10 image scaled at 150%. Unity would need to show a 15x15 image from a 10x10 source, and the algorithm determines which color each pixel of the 15x15 image would be based on the color of the pixels of the 10x10 image.
You should also disable the Compression (set it to “None”) to avoid compression artefacts (and you probably don’t need compression if you have small resolution pixel art).
You can save all these settings as the default settings which can be pretty handy. First set the settings on an existing image asset, then click the “preset” icon at the top of the inspector window, then “Save current to preset”. You can now select this preset as the default preset inside the “Project Settings” under “Preset Manager”.
Pixel perfect camera
First of all you should disable anti-aliasing on your camera.
Unity has created a pixel perfect component for the camera. You can add it manually to an existing camera or create a preconfigured camera gameobject.
This creates a Pixel Perfect Camera component on the camera wich looks like this :
My game uses 16x16 tiles and I want 1 tile per unit (Unity unit of distance). So I set the Assets Pixels per Unit to 16. You need to put the same number as the “Pixel per Unit” in the import settings of your sprites.
The reference resolution is the resolution you want your game to be. It will be displayed at a higher resolution, but this is how many pixels of your assets should be displayed on screen. I have 16x16 tiles in my game, so with a resolution of 180x96 pixels my camera will show 11.5x6 tiles.
Crop frame
Now Unity knows I want to display 180x96 pixels, but nobody will have a screen resolution of exactly 180x96 (it would be very weird if anyone did). You can set the Crop Frame to “Stretch fill” to simply stretch the 180x96 image to fill the full screen (with black bars if the aspect ratio isn’t correct). This works, and it fills the screen, but the image may be blurry because the image is stretched.
Otherwise Unity will display the highest multiple of the camera resolution that fits the screen, so that each pixel of the camera image takes exactly n pixels of the screen. This way the image is clean and crispy, so this is what I recommend. No you can choose “Letterbox” to have a fixed height, add black bars at the top and bottom, and widen the camera to fit the screen. “Pillarbox” is the same but vertically, and “Windowbox” will add a black frame all around the camera, with a fixed aspect ratio.
Grid snapping
So your camera image is the size you want, scaled the way you want it to be, but what happens if you put a sprite that is not aligned with the grid (for example at (0.1, 0, 0)) ? What happens if you put a high resolution image ? This is solved by Gid Snapping.
Set it to “Upscale render texture” and the camera will render to a low resolution texture (the resolution set in Reference Solution), then upscale this texture to the target resolution. That way everything is always at the reference resolution. You can scale/rotate images, have particle effects, display text, everything will still be at the reference resolution. But you cannot show higher resolution images or text (or at least not with the same camera).
Set it to “Pixel Snapping” and it will only “snap” every image to the grid. So if you use a 32x32 image on a 16x16 grid, it will be aligned with the other sprite but still displayed at double the resolution. If you rotate an image (any rotation other than multiples of 90°), the squares of each pixels will be rotated. So if you only use 16x16 unrotated images the result is the same as “Upscale render texture”, but you could still have a higher resolution text for example, and it would be displayed at a high resolution.
Set it to “None” and nothing will be done by the camera. So a 16x16 image displayed at (0, 0, 0.1) would not be aligned with the pixel grid. This is probably not what you want, unless you manually make sure that every image of your game is aligned with the pixel grid.
World space Canvas
When you create a world space canvas in Unity by default it will be huge compared to the rest of your scene. This is simply because the canvas is created at the scale 1 canvas unit (pixel) = 1 Unity unit. It you have some text in your canvas with a font size of 24, the text will be 24 units high in your scene, which is pretty huge.
What you probably want is to set the scale of the canvas to the size of a pixel in units. So if your Pixel per unit is 16, it means that a pixel is 1/16 unit, and 1 / 16 = 0.0625 so you should set the scale of this canvas object to 0.0625. That way a 16x16 image in the canvas now takes 1 unit in the scene. And if you have a text with a pixel font and a font size of 16, the pixels of the text will be aligned with the pixels of the grid. Perfecto !