Lately, we’ve been working on our upcoming pixel-art 2D game, The Last Priestess. It’s actually an old project that we started building 3 years ago on Cocos 2D. At that time we were targeting iPhone 4/4S which meant that there would be a single resolution on which the game would run. Now, we are using Unity and we are targeting both iOS and Android. This means thousands of possible resolutions. “Why is this a problem?”, you may ask…well, let me introduce you to our little friend!

Texture filtering

In non-pixel art 2D games, a sprite can be rendered in any screen resolution using bilinear filtering. When the sprite is rendered on screen, the screen pixels get their color values by interpolating between the actual texture color values. This produces a smooth result.

If bilinear filtering is used in a pixel-art game, the result gets quite blurry. I will pause here to mention that retro games that used pixel art back in the day were meant to be viewed blurry! The artists took advantage of the gaussian blur that the CRT phosphors offered for free to reduce the ‘blockiness’ of the sprites (take a look here). In order to view those retro games the way they are suppoded to, CRT-shaders have been developed, but this a story for another post. Left: How pixelart games are rendered today Right: How they use to look back in 90s

Nowadays, we want our new pixel-art games to look really sharp and blocky! This is why, nearest neighbor filtering (aka point filtering) is used to render sprites. With this technique, when a texture is sampled, the color value of the nearest texture pixel is returned. Another way to think about that is that each texture pixel gets rendered to multiple screen pixels.

The problem

But how may screen pixels is a texture pixel going to be rendered into? This is where the problem begins to unfold!

Let’s imagine that we have a 4x4 pixels sprite that is being rendered on an 32x32 screen area. Each sprite pixel will render to an 8x8 region. In a less ideal scenario, the screen region could be 38X38, resulting in a 9.5x9.5 screen area (because 38/4 = 9.5). Since we render on non integer pixel lengths, this actually means that the region will sometimes be 9x9 or 10x10. Thus, some texture pixels will appear slightly bigger than others. Even worse, when a sprite moves (translates) the same sprite pixel could change between 10 and 9, creating a shimmering artifact.

The solution

We want the screen resolution to be a multiple of our texture’s size. However, we can’t control the resolution because each device has its own native resolution. What we can do is scale up or down our sprite so that each texture pixel renders to an integer number of screen pixels. Another way to think about this is that we will zoom in or out of our 2D world a bit, in order to match a pixel-perfect ratio.

The perfect candidate to apply this solution in Unity would be the orthographic camera. So, we created a simple script that adjusts the camera’s size in order to achieve a pixel perfect result.

Pixel Perfect Camera script

Using our Pixel Perfect Camera script, you can set the ideal camera width or height in units (Yep, you can set the camera width if you want!). If you enable the pixel perfect mode, the script will set the camera’s size to the closest pixel perfect size to the one you have set.

HTML5 demo

Below you can take a look at a live demo, that makes use of some of our upcoming game’s assets at your browser. In the demo you can toggle the pixel perfect mode of the script and adjust the camera size. When pixel perfect is disabled you can notice the following artifacts:

Shimmering artifacts in the falling word

Distorted geometry of the pixelated grid

Small details in the background, like windows, appear to change size

When pixel perfect mode is enabled, the camera size snaps in specific values. The artifacts disapper in these pixel-perfect sizes.

Get the add-on

Even though some other solutions exist in the Unity Asset store, our approach is simpler and has a few more features that were important for us. This is why we would like to share it with you with no charge!

UPDATE: In order to continue the support and development of Pixel Perfect Camera further, we decided to put a price tag on the addon.

You can download our script from the Unity Asset store. We hope you find it useful!