Do you remember the Pythagorean theorem from high school? No worries if you don’t… We also did not think we’d ever use THAT again. Let’s dive in!

Why are we talking about high school trigonometry?

Project Ghost is a multiplayer horror game focusing on lighting and shadows. In this progress update, we will dive into our journey in refactoring our game from 2D to 3D in Unity

The problem

GIF showcasing the giggly effect of non-pixel-art

First, our switch to 3D led to a “giggly” effect on our assets when moving around a level.

Image comparing pixel-perfect sprite and non pixel-perfect sprite

Second, our assets were looking stretched and wonky. We set out to figure out why.

The cause

Simply put, Unity’s camera moves along a grid. If your assets are not perfectly aligned with this grid, you will get rendering issues like the “giggly” effect. In our research to understand this phenomenon, we found a Stack Overflow article with visual aids to illustrate what was happening to the pixels. At this point, we knew that we would need to refactor our assets to align with our grid. More on this later.

GIF showing the slowed-down giggly effect where pixels are stretching over the pixel grid.

Furthermore, we found out that the stretching of the pixels was caused by the 45° angle of our camera. This viewing angle distorted the perfect squares of the pixels into triangular shapes. Therefore, achieving pixel-perfect for our camera would not be as simple as snapping our assets onto a grid. This is where trigonometry will come to our aid.

Diagram showing the 45 degree of the camera

The Pythagorean theorem told us that the camera should compensate for its viewing angle so that the pixels appeared perfectly square. In math terms, we needed to solve for the hypotenuse. This will be our magic number.

Diagram showing the Pythagorean theorem.

The fix

You might be asking yourself, “these fine folks are not the first ones to make a pixel-art game; I’m sure Unity has an easy solution for this….” You are not wrong. Unity has a pixel-perfect camera component. Huzzah! ╰(°▽°)╯

Screenshot of the Unity's pixel-perfect camera error saying that it requires a 2D renderer to work.

However, this component is only compatible with Unity’s 2D renderer, and as you know, our game uses a 3D renderer. Frankly, we did not know where to go from here. Therefore, we sought help from fellow indie game developer Elliot Colp (@FinalNanner) to find a solution. Please go check out his incredible work on Tumble Rush.

Gif of game Tumble rush

With his help, we determined that our pixel grid was actually 1/32 by 1/32. This is entirely project-dependent and is determined by the pixel resolution of your assets. As you may have guessed, our pixel resolution is 32 pixels by 32 pixels.

If we were viewing the level at a perfect 90° angle, we would simply have to set our camera’s snapping for the X and Z axis to 1/32 increments (0.03125). Remember that our Y axis is locked because we are only displaying a 2D plane of our game. However, our grid has to compensate for the camera angle by solving for the hypotenuse, which we will call z. Z is the vertical axis in the 2D render of our game, as shown below.

Diagram displaying the x and z axis of the game

Based on the formula, the solution to find our magic number is:

Diagram displaying the formula:

z = square root of (a squared) + b squared)

In this case, our magic number is 0.04419411. We set the camera’s snapping grid to x = 0.03125 and z = 0.04419411. This means that the camera moves by increments of 0.03125 pixels on the x-axis and 0.04419411 pixels on the z-axis. Again, this is due to the camera being at a 45° angle and the resulting distortion of the pixels on the z-axis.

Screenshot of camera grid

The result

GIF of Charlie from It's Always Sunny in Philadelphia about "Pepe Silvia"

After all this work involving theorems and hippos 🦛, we finally achieved pixel-perfect! Check it out below, and let us know what you think!

GIF of our game in pixel-perfect


You can sign up to receive updates and special announcements directly into your inbox every month! By signing up, you will also be registered to be a playtester for our upcoming community playtests!