1
\$\begingroup\$

I am trying to set up an environment for a retro style pixel art game in Unity 2D. I aim for a vertical resolution of 240 pixels, and then, I would like the horizontal resolution to be dependent on the target screen's aspect ratio, so, on a 4:3 screen 320x240, on 16:9 around ˜432x240, and so on.

I have read a bunch of tutorials, added pixel perfect to my main camera, set up a target resolution in player preferences, set the pixel in a unit to 8 pixels everywhere, but still cannot make heads or tails of how it actually works.

Here is an image of my scene with my camera selected:

enter image description here

If I am correct, the dashed line of the camera is my target resolution, although it is weird as the green image I used as a background has a native resolution of 216 pixel vertical, yet it fills up the supposedly 240 pixel high camera. I have no idea what the solid green line is, and don't even get me started of the canvas, whose render target is set for camera yet it is obviously many times bigger than that.

Can someone please explain it to me how to properly set up a 2D environment described in my opening, point out my mistakes, point me to a good tutorial, or just give me advice on how to understand Unity's resolution management?

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

The pixel-perfect camera component automatically scales your view so each texel in your sprites maps to an integer number of pixels on the screen. That means it's limited to scaling by whole-number ratios: 1x, 2x, 3x etc.

Within those constraints, it tries its best to fill the current size of the window/screen with the largest texels it can, while still showing everything inside the "reference resolution" box.

Screenshot of pixel perfect camera on black & grey grid

Above, I've pressed the "Run in Edit Mode" button so we can see what it's doing. In the background is an 8 PPU grid, where the large grey squares are 80 px.

You can see the dashed green outline exactly matches the 320 x 240 reference resolution I asked it for. (If you're not observing this, you might not have Run in Edit Mode ticked, or there may be a scale applied to another object in your scene that's throwing it off).

My current game window is larger than this though, so the current rendered bounds are shown by the solid green box outside that. Rendering these bounds lets us fit the current window with a 3x zoom (see the "Current Pixel Ratio 3:1" in the bottom-right).

Now we'll try making the game window narrower in width. We can get as tight as this while keeping our 3:1 zoom factor:

Limit of 3:1

If I make the game window any narrower, it starts to creep into the space we reserved for the reference resolution. So the pixel-perfect camera component drops down a zoom level, from 3:1 to 2:1, so that it can still show the full width of the reference space. Zooming out like that means we can also see more above and below the reference range too.

Now in 2:1

If you want your vertical resolution to remain absolutely capped, you can tick the "Crop Frame Y" checkbox.

This snaps the height of the displayed area to exactly match the reference resolution, letterboxing the display so that pixels above and below aren't drawn, even if the current window size leaves a little extra space at the selected zoom factor. Instead you get black bars above and below.

Crop Frame Y

If you want your 240 vertical texels of scene to exactly fill the height of the window, no matter how tall it is, then you'll have to sacrifice pixel perfection. Some possible screen/window sizes aren't multiples of 240, so the only way to stretch 240 source texels to cover it is to interpolate them, or repeat some texels more than others.

If that's the look you want, it's reasonably easy to set up:

  1. Get rid of your pixel perfect camera component, since we've decided pixel perfection isn't the goal.

  2. Create a new RenderTexture in your assets folder. We'll make it 1024x240 to have lots of room for wide screens. (You can also dynamically create this in script to match the width you actually need)

  3. Set your camera's Target Texture to this texture. Now you'll render exactly 240 vertical texels of the screen into the 240 vertical texels in the texture.

    You can use a script to adjust the Viewport Rect of the camera to match the current display aspect ratio so you're not rendering stuff that will be off-screen.

  4. Place a quad in the scene on a new layer that's not seen by your main camera. We'll scale it so it measures 128 x 30 (1024 x 240 divided by 8 PPU).

  5. Create an unlit material that display your Render Texture, and assign it to the quad.

  6. Create a new orthographic camera that sees only the quad's layer, and give it a size of 15 (15 above + 15 below = 30 units x 8 PPU = 240 texels). Frame it so it looks at the center of the quad.

    By outputting to the display, this camera handles upscaling your 240 texel high render texture to whatever vertical resolution the window/screen happens to be at this moment.

    By default, the Render Texture will be set to use bilinear filtering, which will look smooth, but potentially blurry. You can change that to point filtering to keep the edges crisp, though at the cost that some lines might look wider than others because of the inconsistent zoom factor. You can even apply a custom shader to the quad material to implement your own upscaling algorithm, to get a different compromise between crispness and smoothness/consistency.

\$\endgroup\$
2
  • \$\begingroup\$ Very thorough answer, thank you very much. I will read it tonight, just wanted to say thank you in advance for taking the time. \$\endgroup\$ May 9, 2021 at 18:16
  • \$\begingroup\$ I had to read it twice, but it totally makes sense. Thank you very much, I think I understand how it works. \$\endgroup\$ May 9, 2021 at 19:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .