While first investigating pixel art game creation in Unity, like any game engine it has it’s own caveats. One of Unity’s is that with the new dynamic font rendering system, it can’t perform nearest neighbour filtering on scaled fonts, so when pixel art style fonts are displayed through the regular method, they are scaled using bilinear filtering, resulting in blurry fonts. This Unity post explains how to prepare images and set the Texture Importer settings to correctly render pixel art in Unity3D. So, this post covers how the different Texture Importer settings affects the pixel art textures in 3D models and in the game’s graphical user interface (GUI).
The 2D Pixel Perfect package contains the Pixel Perfect Camera component, which ensures your pixel art remains crisp and clear at different resolutions, and stable in motion.
It is a single component that makes all the calculations Unity needs to scale the viewport with resolution changes, so that you don’t need to do it manually. You can use the component settings to adjust the definition of the rendered pixel art within the camera viewport, and you can use the Run in Edit Mode feature to preview any changes immediately in the Game view.
Attach the Pixel Perfect Camera component to the main Camera GameObject in the Scene, it is represented by two green bounding boxes centered on the Camera gizmo in the Scene view. The solid green bounding box shows the visible area in Game view, while the dotted bounding box shows the Reference Resolution.
The Reference Resolution is the original resolution your Assets are designed for, its effect on the component's functions is detailed further in the documentation.
Before using the component, first ensure your Sprites are prepared correctly for best results with the the following steps.
Preparing Your Sprites
![Art Art](/uploads/1/1/7/9/117943695/395384098.png)
- After importing your textures into the project as Sprites, set all Sprites to the same Pixels Per Unit value.
- In the Sprites' Inspector window, set their Filter Mode to ‘Point’.
- Set their Compression to 'None'.
- Follow the steps below to correctly set the pivot for a Sprite
- Open the Sprite Editor for the selected Sprite.
- If Sprite Mode __is set to ‘Multiple’ and there are multiple __Sprite elements, then you need to set a pivot point for each individual Sprite element.
- Under the Sprite settings, set Pivot to ‘Custom’, then set Pivot Unit Mode to ‘Pixels’. This allows you to set the pivot point's coordinates in pixels, or drag the pivot point around freely in the Sprite Editor and have it automatically snap to pixel corners.
- Repeat for each Sprite element as necessary.
Snap Settings
To ensure the pixelated movement of Sprites are consistent with each other, follow the below steps to set the proper snap settings for your project.
- To open the Snap settings, go to Edit > Snap Settings.
- Set the Move X/Y/Z properties to 1 divided by the Pixel Perfect Camera’s Asset Pixels Per Unit (PPU) value. For example, if the Asset PPU is 100, you should set the Move X/Y/Z properties to 0.01 (1 / 100 = 0.01).
- Unity does not apply Snap settings retroactively. If there are any pre-existing GameObjects in the Scene, select each of them and select Snap All Axes to apply the Snap settings.
Properties
The component's Inspector window
Property | Function |
---|---|
Asset Pixels Per Unit | This is the amount of pixels that make up one unit of the Scene. Match this value to the Pixels Per Unit values of all Sprites in the Scene. |
Reference Resolution | This is the original resolution your Assets are designed for. |
Upscale Render Texture | Enable this property to create a temporary rendered texture of the Scene close-to or at the Reference Resolution, which is then upscaled. |
Pixel Snapping (only available when ‘Upscale Render Texture’ is disabled) | Enable this feature to snap Sprite Renderers to a grid in world space at render-time. The grid size is based on the Assets’ Pixels Per Unit value. |
Crop Frame | Crops the viewport with black bars to match the Reference Resolution along the checked axis. Check X to add horizontal black bars, and Y to add vertical black bars. For more information and a visual example, refer to the Property Details below. |
Stretch Fill (available when both X and Y are checked) | Enable to expand the viewport to fit the screen resolution while maintaining the viewport's aspect ratio. |
Run In Edit Mode | Enable this checkbox to preview Camera setting changes in Edit Mode. This causes constant changes to the Scene while active. |
Current Pixel Ratio (available when ‘Run In Edit Mode’ is enabled) | Shows the size ratio of the rendered Sprites compared to their original size. |
Additional Property Details
Reference Resolution
This is the original resolution your Assets are designed for. Scaling up Scenes and Assets from this resolution preserves your pixel art cleanly at higher resolutions.
Upscale Render Texture
By default, the Scene is rendered at the pixel perfect resolution closest to the full screen resolution.
Enable this option to have the Scene rendered to a temporary texture set as close as possible to the Reference Resolution, while maintaining the full screen aspect ratio. This temporary texture is then upscaled to fit the entire screen.
The result is unaliased and unrotated pixels, which may be a desirable visual style for certain game projects.
Pixel Snapping
Enable this feature to snap Sprite Renderers to a grid in world space at render-time. The grid size is based on the Assets Pixels Per Unit value.
Pixel Snapping prevents subpixel movement and make Sprites appear to move in pixel-by-pixel increments. This does not affect any GameObjects' Transform positions.
Crop Frame
Crops the viewport along the checked axis with black bars to match the Reference Resolution. Black bars are added to make the Game view fit the full screen resolution.
Uncropped | Cropped |
Cinemachine Extension
As both the Pixel Perfect Camera and Cinemachine modify a Camera’s orthographic size, using these two systems together in a single Scene would cause them to fight for control over the Camera and likely produce unwanted results. To solve this incompatibility, add the Cinemachine Pixel Perfect extension to your virtual cameras. You can find more information about the extension’s features in the Cinemachine documentation.
Introduction
If you are an indie developer making a retro style game using pixel art, you definitely want to maintain pixel-perfect graphics across a wide range of resolutions. There are already a lot of tutorials on the topic out there so why bother writing another one? The matter of fact is, there is no silver bullet to solve this issue. During my searches I came across many forum posts coming from people confused about PPU (Pixel per unit), reference resolutions, frame cropping and the list goes on. In a nutshell, even if you follow the strategies outlined by others you still may end up with poor results on different resolutions. In this tutorial I’ll share my experience and present my solution to this problem based on a real case scenario.
Table of contents
- Setting up the principles for pixel-perfect graphics
- The sprite settings inside Unity
- Setting the camera for pixel-perfect graphics
- Achieving pixel-perfect GUI graphics
1. Setting up the principles for pixel-perfect graphics
A little disclaimer before you continue on reading. I don’t claim that what I present here will work for you as you may have to experiment a bit with your artwork. Also, for the purpose of this tutorial I don’t consider having any physics engine. That being said, I will present you my particular case scenario and workflow that worked best for me. What I wanted to achieve for my latest project is the crisp, pixel-perfect graphics on a whole spectrum of devices with different resolutions. There are several characteristics of the game that I had to take into consideration:
Unity Pixel Snap
- The game itself is based on a grid
- Each cell is 32 x 32 pixel in size (of course you can go with different sizes but the good rule of thumb is to stick to sizes that are to the power of 2)
- The game will have pixel art GUI that needs to be in line with the rest of the graphics presented in the game
Real case specification approach
Unity Pixel Art Lighting
The first thing you need to do is to establish your target (reference) resolution and it is super important that you stick to it during development. Secondly, you need to define target aspect ratio of the screen. I’ll make a new canvas in Aseprite of the size of my target resolution. After that, I’ll start drawing my levels and sprites within its boundaries. By doing so, I am able to maintain the size of individual sprites in reference to the world.
In case on my latest game that is 320 x 192 pixels, which gives me 5 : 3 aspect ratio. Since I’m planning my game to be based on a grid, each size of the cell is going to be 32 pixels. That tells me that I can fit 10 sprites horizontally and 6 sprites vertically within my target resolution. The process ensures that when I export my sprites to Unity I will know how to exactly set them without losing their quality.
2. The sprite settings inside Unity
When you import your spritesheets to Unity, there are few fundamental settings you have to make.
- In the Sprites “Inspector” panel, set Pixels Per Unit (PPU) to ‘100‘. This means that 100 pixels of your original spritesheet will fit inside 1 Unity world unit (1 meter), which is exactly what we want. PPU is pixel-to-unit ratio that needs to correspond to your artwork. Of course, I could set that to 32 (since each grid cell is 32 pixels in size), but I realized that this causes issues when scaling graphics to different resolutions. However, if you intend to utilize the physics engine in a fast-paced game (such as platformer) you better set it to original size of 32. The reason for that is the fact that colliders apply default contact offset while detecting collisions, which means that the scale heavily affects contact points.
- In the sprites Inspector panel, set Filter Mode to ‘Point‘.
- In the same panel, set Compression to ‘None‘.
- If your sprite sheet is bigger than 2048 pixels then set Max Size to ‘4096‘ or higher to prevent blurriness. This may happen if, for example, your spritesheet contains lengthy animations spanning multiple frames.
Pixel-perfect pivot point
Now that we have the basic settings made, we now have to set the pivot point. It will depend on the nature of your sprites and the point around which you wish to conduct visual transformations (translation, rotation etc.).
- Open the Sprite Editor
- Whatever your sprite mode is (Single or Multiple), make sure you set the pivot point while Pivot Unit Mode is set to ‘Pixels‘. This will ensure the proper placement of pivot point inside your sprite for pixel-perfect graphics.
This concludes the basic setup. Now we can move on to more advanced settings!
The grid settings
Since the game is based on a grid where each cell occupies 32 pixels, we need to make changes to how Unity handles it.
- Go to Edit > Grid and Snap Settings… from the top bar menu.
- Under Increment Snap in the Move fields for each individual component put 1 divided by the size of your sprite’s PPU. In our case it’s going to be 1/100. Please note that you can simply put 0.01 here.
- Create a new Tilemap in your level. This will automatically create a Grid as its parent.
- In the Inspector panel of Grid, set the cell size corresponding to a cell size you defined in the very beginning. In our case that is going to be 0.32 (32 x 32 cell size). This will ensure proper translation of the pixels from Aseprite to Unity. As I already mentioned, if your game heavily depends on physics engine it is best to stick to regular scale. In such case set your grid with 1×1 cell size.
- Create a new Tile Pallete out of your sprites (Window > 2D > Tile Pallete). Simply Create New Pallete from the dropdown menu inside the panel, select all your sprites and drag & drop them inside the grid section.
![Perfect Perfect](https://blogs.unity3d.com/wp-content/uploads/2019/03/image27.png)
You can now start creating your levels with brushes provided by tile pallete. After that we have to set up one more thing before we can enjoy our pixel perfect graphics!
3. Setting the camera for pixel-perfect graphics
We still need to set up the camera for proper rendering of pixel-perfect graphics. Unity Technologies came up with a brilliant add-on called 2D Pixel Perfect that you can attach to your main camera. It will dynamically calculate the proper pixel size for the current resolution based on the provided values. In the early days of development it was necessary to build it from source and import it to the project manually. However, now you can install it directly from package manager. Go to Window > Package manager, search for 2D Pixel Perfect package and install it for your project. After that add it to your camera as a new component.
- Set Assets Pixels Per Unit to your PPU. In our case it’s going to be 100. From multiple experiments I concluded that the best option is to make sure that this value always stays 100. Again, if you intend to heavily rely on physics engine, use the original size (in my case it’s 32).
- Set the Reference Resolution to the Target Resolution we have established in the very beginning. In our case it’s going to be 320 x 192. It’s very important that this size corresponds to the size of the canvas made in Aseprite.
- Tick the Upscale Render Texture if you wish to maintain the unaliased and unrotated pixels during the transformations. I always leave this on but it depends on what kind of style you’re going for.
- If you want to achieve smooth movement of your sprites during gameplay then leave Pixel Snapping unticked. In my case I always leave this option unticked to get the best possible movement .
- Tick the appropriate boxes if you wish to crop the viewport along the checked axis in Crop Frame section. With few experiments I concluded that the best results can be achieved by ticking the Y axis, but this is entirely up to you.
- If you select both axes in Crop Frame section then you will have an option to Stretch Fill it. I usually don’t tick it but it really depends what kind of style you’re going for.
In the end my settings look like this in the Inspector panel.
Testing and validating the setup
When you now run the game in the play mode, you will see crisp, pixel-perfect graphics in all resolutions! At this point what I like to do is to make a transparent sprite of my target resolution size (320 x 192) with 32 x 32 pixels squares in each corner and put it on top of my scene. This allows me to measure if my settings are correct.
If you enable cropping on both, X and Y axes, you will notice that no matter what resolution you will set your game to in play mode, it will always show pixel-perfect graphics with red corners within the boundaries of the viewport. Below are just few examples. Please make sure you have Maximize On Play enabled to more accurately assess the results. You can find it in the top bar of the Game viewport window.
Solving the clashing issue with Cinemachine add on (Optional)
Today, many projects made in Unity take the advantage of Cinemachine plugin. It is totally understandable as it comes with a bunch of neat features that you would otherwise had to write from scratch. However, the way this plugin works inside your projects is that it modifies the orthographic size settings of your camera on the fly. This causes the infamous clash between the 2D Pixel Perfect add on. Luckily, there is an easy solution to this problem. Simply select CinemachinePixelPerfect extension from the dropdown menu in the Inspector panel of your virtual camera.
4. Achieving pixel-perfect GUI graphics
If you are developing a game with pixel art there is a high chance you will also prepare graphics in the same style for your GUI. In order to achieve the highest quality of aesthetic quality standard you most probably would like to make it in line with your game assets. In other words, you’d like pixels occupied by GUI graphics take the same amount of space on your screen as your other sprites utilized in the game.
The secret lies in using the second camera for rendering the canvas containing all GUI elements that need to be pixel-perfect. However, there are few things that we need to be careful about if we already have one camera for rendering the game.
- The camera responsible for rendering sprites in the viewport cannot have the UI mask included in the Culling Mask list.
- On the other hand, the camera dealing with the UI display needs to have only the UI mask enabled. Furthermore, it is necessary to set Depth Only clear flag here since we don’t want to occlude anything being rendered by main camera.
- The 2D Pixel Perfect script component needs to be attached to both cameras and have exactly the same settings. In case of any discrepancies the viewport can be unpredictable.
- In the Inspector panel of the canvas set the Render Mode to Render Space – Camera and select your “UI” camera from the project assets.
- For the Canvas Scaler component, set UI Scale Mode to Constant Pixel Size, Scale Factor to 1 and Reference Pixels Per Unit to 100. If you use physics engine in your game, set this value to your original size (32 in my case).
Adjusting the scaling factor for different resolutions
This concludes the main setup of the pixel-perfect GUI. However, since we have set Canvas Scaler’s scaling mode so that it maintains constant pixel size we need to adjust the scaling factor whenever the resolution changes. This can be easily sorted with a simple script that sets current pixel ratio as our scaling factor. Luckily for us, we don’t even have to calculate it ourselves since the 2D Pixel Perfect module already provides us with this value.
You may have noticed that I’ve placed the execution of the adjusting logic inside the
LateUpdate()
. The reason for this is that some elements of the GUI may track objects that are constantly being updated inside the Update()
function. It may happen for example in case of minimap that updates the location of the pointer representing a character on the map. By doing so we ensure the integrity and minimize the risk of running into glitches. To demonstrate this in action, I’ve placed a simple button with up arrow in the canvas we just created. The button itself was created with Aseprite and is meant to have a pixel look. If you look closely you’ll see that its pixels perfectly align with game assets.
You can fork a minimal sample project at my github page.
Conclusion
In this tutorial I presented my solution to maintaining pixel-perfect graphics across wide range of different resolutions. I’ve showed you my workflow and mindset that I have while creating a retro style games. The real case scenario, which was used to demonstrate it is based on a grid with cells of fixed size. After that, I’ve installed 2D Pixel Perfect plugin that is available for free via Unity package manager. It controls the correct size of screen pixels and orthographic size according to desired reference resolution. In addition I showed you how to resolve a clash with Cinemachine plugin. I concluded the tutorial with the implementation of pixel-perfect GUI based on camera render mode. The setup ensured perfect alignment of pixels rendered in the viewport during play mode.