Search Unity

Setting up Unity for pixel-perfect 2d project

Discussion in '2D' started by TheCodeJunkie, Sep 16, 2014.

  1. TheCodeJunkie

    TheCodeJunkie

    Joined:
    Apr 4, 2014
    Posts:
    5
    Hi,

    I recently started using Unity3d, more specifically the 2D stuff, to get into game development and find it really easy to get productive, especially when I already have a strong background in C# programming.

    Recently I started reading up on how to properly set up my game environment so that it gives me the best results. I was hoping that someone could validate (or tell me what I should do differently) what I have concluded.

    Background
    I want to do a 2D, pixel art style, game and I'd like for it to run on multiple devices, where the desktop PC (Windows, OSX) is my primary target atm.

    Conclusions
    1. I should be using an Orthogonal camera
    2. The camera's orthographic size should be set to (Screen.height / pixelPerUnits / 2f)
    3. When slicing up sprite sheets, I should set the filter mode to Point
    4. Make sure the Max Size (of the sprite sheet) if at least the dimensions of the sprite sheet
    Also, one thing that I am a bit perplexed about is what I should choose as my Pixels To Units value? If I use 32x32 tiles, would it be a good idea or not (why?) to set it to 32? That would leave me with 1 UV coord would be the length of my tiles, making it easy to divide it up in 10ths

    Are there any other things that I should take into consideration when setting up my Unity environment for this kind of project? I'd rather do the ground work now, rather than figuring out these things later and having to go back and redo a lot of work because the environment setup requirements changed

    Thanks!
     
  2. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    Welcome!

    First, will your camera be moving while your game is being played?

    Unity has no options itself to make it's camera pixel perfect. I've read all the articles, and all the conclusions that you've gotten. However, that will just not make Unity's camera pixel perfect. Which is really frustrating. And everyone on this forum would tell you that it will do the trick, because those are the only things that you can do in the Unity editor. ( Because that's how much they know :cynical: )

    The problem comes if your camera is moving! So brace yourself.

    Unity's camera moves in floating point coordinates. That means that no matter how big or small you set the Pixel To Units value, Unity's camera will find a way to f*ck you up! :D And this is because it will move in sub-pixel levels. Which will create some pixel-level distortions. (It might not bother you, but it bothered me)

    In my research I've found a temporary solution which took me ages to find, because I guess no one here makes games actually to know about this issue.. -.- (pardon my frustration with this issue and the lack of information out there)

    We add a script to the camera which will be used for the camera to follow the player.

    So we use a function called RoundToNearestPixel
    Code (CSharp):
    1.     public static float RoundToNearestPixel(float unityUnits, Camera viewingCamera)
    2.     {
    3.         float valueInPixels = (Screen.height / (viewingCamera.orthographicSize * 2)) * unityUnits;
    4.         valueInPixels = Mathf.Round(valueInPixels);
    5.         float adjustedUnityUnits = valueInPixels / (Screen.height / (viewingCamera.orthographicSize * 2));
    6.         return adjustedUnityUnits;
    7.     }
    And then we use that function to round the cameras x and y coordiantes to the nearest coordinates which would be in pixel levels. (and not in sub pixel levels)

    This would be in the Update function of course.
    Code (CSharp):
    1. Vector3 roundPos = new Vector3(RoundToNearestPixel(newPos.x, camera),RoundToNearestPixel(newPos.y, camera),newPos.z);
    2. transform.position = roundPos;                      
    Where newPos is a Vector3 of the next position the camera would move to, to follow the player. I don't know how your script would be like.

    But I hope this helps in any way.

    Cheers
     
  3. TheCodeJunkie

    TheCodeJunkie

    Joined:
    Apr 4, 2014
    Posts:
    5
    Shadeless,

    Thank you for the welcome!

    Yes, the camera would be tracking my character as it makes its way across the level. I see what you are saying about the camera moving in sub-pixels and understand the purpose of your script. Thank you.

    I will apply this to my camera in the morning evaluate the results (which I expect to be positive) :)

    Would you say that the Pixels To Units value becomes irrelevant with this setup (especially when forcing the camera to align with the pixel grid) and only serves as a way to define your grid, so to day


    PS. In your second code snippet - where does newPos come from? Is that simply the position of the camera at the moment that Update is called? So camera.transform.position ?

    Thanks again for the suggestion and feedback
     
  4. TheCodeJunkie

    TheCodeJunkie

    Joined:
    Apr 4, 2014
    Posts:
    5
    Oh, I guess that all game objects that moves around (player, enemies etc) should also have their movements aligned with the pixel-grid to avoid the sub-pixel issues ?
     
  5. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    Glad you find it helpful.

    1. I've tested with different (large and small) Pixel To Units values, and indeed they are irrelevant to the setup. The way I'd go is to set it up to the size of your tiles in your spritesheet. So if I'm using 32x32 tiles, i'll set it to 32. so that way 1 Unity Unity is 32 pixels. Makes it convenient for tiling. I've decided to go with larger tiles though, so I have more pixels in 1 Unity Units ( I really don't know if that helps, I've tested but not definitive results ). You can do whatever you want in that regard I think.

    2. The Vector3 newPos is the next position the camera would go to. For example if we are following the character without any camera dampening, so it just sticks to the player position. Then the newPos I guess would be the player position of the next frame. (Something like that, you can do whatever you want with your camera following)

    3. That's an interesting point, if I wasn't so busy solving tiling issues and camera issues and pixel distortion issues. I might have gotten to test that. If you do, do post an update on your results. I would think that you wouldn't need to do it for the player, since we're following the player and the camera rounds those values. For the enemies I don't know really. Maybe if they would be high quality enough it wouldn't be noticeable. I really don't know. Unity's camera right? lol

    Note: I am starting to use more detailed sprites with more pixels like 128x128, so I have more pixels in every Unity Unit. This is just theoretical though, I haven't really seen a huge difference in using bigger or smaller tiles.

    Cheers
     
    Redden44 likes this.
  6. crandellbr

    crandellbr

    Joined:
    Apr 3, 2013
    Posts:
    137
    This was very helpful, thank you Shadeless! I'm amazed I haven't seen this anywhere else...surely others have encountered the same thing? Sub-pixel camera positioning seems like such a fundamental issue that it would come up more often. I think I recall camera movement in the 4.3 Tower demo, but then again, those sprites probably weren't detailed enough to notice.
     
    Last edited: Jan 11, 2024
  7. TomasJ

    TomasJ

    Joined:
    Sep 26, 2010
    Posts:
    256
    Hey,
    I'm working on making Unity solve most of these problems automatically. Please send me any and all repro projects demonstrating your troubles.
     
    der_r and GarBenjamin like this.
  8. Ultroman

    Ultroman

    Joined:
    Mar 10, 2014
    Posts:
    110
    Any news on this? It doesn't seem to be on the roadmap, but it is the 2D-issue with the most votes by far.
     
  9. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
  10. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    If you want true pixel-perfection then you will have to consider the resolution of the display. Setting the ortho camera to half its height will give you perfect 1:1 mapping but will also mean other resolutions make your sprites take up more/less of the screen, which may not be what you want. Also make sure you render to integer coordinates (or in the middle of pixels?). And use the pixel snapping tool in the sprite shader to help with that (but beware it snaps pixels using completely separate float values for each vertex so can actually make your sprite grow/shrink by a pixel due to floating point errors).
     
  11. Ultroman

    Ultroman

    Joined:
    Mar 10, 2014
    Posts:
    110
    @Marrt: Yes, but only if you ensure that the pixels match all your projected resolutions exactly, in some multiplication. There is no helping hand for making things pixel perfect in weird resolutions, like many phones use, as well as retina screens.

    A lot can be done with a bunch of different asset-bundles, but a lot of it could be controlled directly in the engine. As the engine knows the current resolution, it could draw sprites larger or smaller to fit the resolution, instead of us having to make a 32ppu set for 720p and a 48ppu set for 1080p.

    I have a game in which I need a fixed width (40 tiles) on any screen. The height doesn't matter. Many games may have it the other way around i.e. fixed height and variable width. I see no way to do this, without a separate set of sprites for each resolution (or do I mean aspect ratio?), which is completely bonkers for indie-development.

    I could make it so there's some leeway on the sides (essentially dead space) for aspect ratios wider than my projected aspect ratio, but I just don't feel like that should be the only option. I don't see any other solutions, though, but this is making me want to drop pixel perfect art entirely.

    And don't even get me started on zooming...
     
  12. Ultroman

    Ultroman

    Joined:
    Mar 10, 2014
    Posts:
    110
    Yeah, I've tried all of that, and it just doesn't cut it. The shader is vertex-based, so it is trying to hammer in a nail with an octopus. Not ideal.

    Thanks for the answer, though. I'm glad I'm not the only one trying.

    The problem is all these wacky resolutions. If all we had were ones with ratios like 4:3, 16:9 and maybe 16:10, we'd be fine, but we have 10-15 semi-popular resolutions, with ratios all over the map, an Apple are sure to invent new ones every year.

    Some brilliant person at Unity must know something that can help, aside from the small guide posted in July 2015, which basically suggests the solutions mentioned in this thread, although the execution and pointers on weird resolutions are hastily skipped over.
     
    Last edited: Dec 29, 2015
  13. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    You have two choices. Either you set your ortho camera to half the resolution height or you keep it fixed. With the first, your Sprite pixels map 1:1 to screen pixels provided your geometry and texture coords are correct. With the latter you will almost always experience scaling and sub-pixel access in all but one resolution. It also depends on the device, like with iOS you have fewer known resolutions to worry about than on desktop.
     
  14. Ultroman

    Ultroman

    Joined:
    Mar 10, 2014
    Posts:
    110
    Thanks again for the reply.

    Setting orthosize to half the screen height, will mess up the width on any device that isn't using the same ratio as your reference resolution. This is where padding the width of the screen with extra border, or letting the player see too much in either side, comes in. Unless you make separate sprite-sets.

    Setting orthosize fixed is not an option because of the problems you've mentioned.

    So we can build to any platform, but to have the game look the same, we have to make a separate sprite-set for each resolution that cannot use a size-multiplication of another sprite-set.

    Those are terrible truthes to accept...
     
    Last edited: Dec 30, 2015
  15. Douvantzis

    Douvantzis

    Joined:
    Mar 21, 2016
    Posts:
    79
    No @Ultroman you don't need to create different sprite-sets.

    In order to achieve pixel-perfect rendering and render everything in the appropriate scale regardless of the screen resolution, you just need to set the camera size taking the following into account:
    • the screen resolution
    • the pixels per unit of the assets
    • a desired orthographic camera size
    For a more in-depth explanation read a blog post I wrote.

    I have created a simple camera script (which is free) that takes all of this into account. You can set the desired camera height (or width) and the script will find the closest pixel-perfect camera size. It works for every resolution, be it on mobile or even the arbitrary resolutions used in the editor. See it in action in this HTML5 demo.

    Also, in contrast to what @Shadeles states, I don't think that you need to snap your camera to pixels to avoid artifacts (at least in most cases). You see artifacts if you are using sprites in an atlas for tiling, but this can be solved by extruding the sprite's border pixels by most sprite generators like Texture Packer.
     
    Wichenstaden likes this.
  16. trialforce

    trialforce

    Joined:
    Apr 15, 2012
    Posts:
    22
    @Shadeless Thank you. I use custom matrix to make a 2.5d game, so Pixel Perfect camera does not work very well, but your script helped me to avoid flickering.
     
  17. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Custom Matrix? You mean non-orthographic camera (why would you need a custom camera matrix otherwise)? i can't imagine how this would not flicker without bilinear filtering. Can you post a Screenshot?