Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Unity 5 - 2D and Pixel Perfection

Discussion in '2D' started by MapMan, Mar 12, 2015.

  1. MapMan

    MapMan

    Joined:
    Oct 27, 2013
    Posts:
    38
    Hi! I am currently working on a 2D 'pixel perfect' game. I did a lot of research on the subject and consideration what pixel perfect is and what it should achieve. Additionally, when developing a pixel perfect game certain decisions must be made and some compromises taken. Even after my research I still have some doubts and questions, especially now that Unity 5 was released to public. Let me start of by describing what I want to achieve.

    1. Each pixel of any sprite in a scene must be represented exactly by one pixel on the target device and resolution. This means that if I create a sprite in a graphic editor it will be displayed exactly the same way in the game engine after rendering.
    2. Zooming in and out must be possible. To retain that crisp pixel art look a technique similar to nearest-neighbor algorithm must be used.
    3. All sprites must be aligned perfectly to each other. A pixel is the minimal distance any sprite can be moved, it can be thought of as the base unit. There must not be any sub-pixel movement of sprites.
    4. There must be no antialiasing or other resampling of sprites (i.e. bicubic or bilinear) that can cause pixels to disappear, appear distorted, stretched, blurred or produce other visual artifacts.
    5. There must be no sub-pixels at the borders of the screen. This is best illustrated by an example: - This is actually impossible if you want to support different resolutions.

    I managed to achieve a working solution that supports the given criteria in Unity 4. Here's how I do it:

    1. Imported sprites must:
      1. Use TrueColor format
      2. Filter Mode must be set to Point
      3. Pixels Per Unit should remain at default value of 100. Using a 1:1 ratio can lead to floating point precision problems when dealing with large scenes (ref: http://www.davenewson.com/dev/unity-notes-on-rendering-the-big-and-the-small).
    2. Sprites must be aligned to what I call Pixel Grid. Using the default Pixels Per Unit value (100) that means that all sprites can be moved by a minimal distance of 0.01 unit. This assures that no pixel stretching and artifacts appear. Movement by values lower than 0.01 will place the sprite in a subpixel location and will lead to artifacts.
    3. The main camera must be orthographic and aligned to the pixel grid as well. Failing to align either the sprites or the camera will lead to artifacts.
    4. Zooming can be easily achieved by manipulating the camera size. We need to retain the pixel perfect look, so a single in game pixel can be represented only by a 1x1 on screen pixel (the native size, 1 pixel in game corresponds exactly to a 1 pixel on the screen) , 2x2 on screen pixel, 3x3 on screen pixel and so on. This can be achieved with this very simple formula:

    float unitsPerPixel = 1f / (Managers.BasicGameStateManager.pixelsPerUnit * Managers.BasicGameStateManager.zoomMultiplier);
    Camera.main.orthographicSize = height / 2f * unitsPerPixel; // orthographic size is half the screen height

    Zoom multiplier must be an integral value, i.e. 1, 2, 3 and so on.

    And now come my questions:
    1. How does all this relate to sprites moving, collisions and generally anything that isn't static scenery? How do I implement, for example, NPC characters that move around but are still aligned properly so we have the pixel perfect look? I can see a lot of potential errors by simply rounding their transform locations to pixel grid in LateUpdate().
    2. What is the PixelSnap propery of sprite shader? I've heard it tossed around here and there but I still have to know how it works. Can someone elaborate?
    3. Does the whole Pixel Perfect game thing change in Unity 5? What are the new or now obsolete capabilities that should be taken into account?
    Aditionally, here are the script's I use to achieve the above. Keep in mind that those are simply prototypes and should only be used for experimenting.
    Another matter is supporting multiple resolutions and aspect ratios while still retaining the pixel perfection. This is best illustrated on an example. Let's say I set up a scene in a graphic editor that is 1280x720 pixels and the players camera size is 640x360, allowing the player to pan around. With that in mind, these are the goals I set out to achieve:

    1. No stretching of pixels/scene since we need that pixel perfect look.
    2. Allow running the game natively in 640x360 in full screen or windowed mode.
    3. Allow running the game in a higher resolution than 640x360 full screen or windowed mode but with a letterbox, that is a black border around the 640x360 view.
    4. Allow running the game in a higher resolution than 640x360 in full screen or windowed mode but expand the view (players camera) rather than adding a letterbox. This criteria is only valid for games where showing more of the scene doesn't affect gameplay and it can cause violation of the last criteria about sub-pixels at the border of the screen.
    And again, questions:
    1. Since we can zoom (enlarge the scene and the players camera), should we scale the content if the player can support at least twice the resolution than the games native? To illustrate: if the player can support 1280x720 resolution, we can scale the scene up to 2560x1440, the camera to display 1280x720 and we still retain the pixel perfect look. Does it have any pros over running the game natively in 640x360? The game will look exactly the same. The only scenario I see that this could find use is when a player would want to run the game in a window on a screen that supports huge resolutions (full HD or 4k).
    2. Does Unity 5 change anything in this matter?
    If I made a mistake somewhere or simply didn't mention something do not hesitate to point it out! I hope this write-up will not only ignite discussion but also help me and others to better understand how to achieve Pixel Perfect 2D games in Unity.
     
    Kuan and theANMATOR2b like this.
  2. Osteel

    Osteel

    Joined:
    Jan 17, 2014
    Posts:
    59
    Thanks for posting your findings.

    I'm working on a 2D puzzle game (very small) and am running into the question about handling the resolution as well. What the artist is doing is exporting the assets in Sketch which will create the different sizes of each asset to meet the different size requirements for Android/iOS.

    (http://www.coreprogrammers.com/list-of-screen-resolutions-for-all-android-based-phones-and-tablets/)

    From there, I plan on sticking them by group in the resource asset folder of Unity and essentially loading them dynamically based on device's resolution.

    But that's sort of where I'm stuck: do I look at the resolution of the device (and how?) or the aspect ratio (and how?). Then, do I just sort of hardcode the requirements such as 1920x1080 (or 9:16) needing to use the xxhdmi Android assets?

    It's really confusing. >.<
     
    theANMATOR2b likes this.
  3. Woppix

    Woppix

    Joined:
    May 11, 2013
    Posts:
    5
    I'm also in the same boat. Upon the release of Unity 5 I decided to try converting my game. It would be amazing to get some of these questions answered. The multi resolution of pixel art on mobile devices ones are specifically important to me for my current game. Would be nice to see this thread build up and have some of these things answered for us :)
     
  4. BlaXun

    BlaXun

    Joined:
    Jan 8, 2015
    Posts:
    52
    I am interested in this too!
     
  5. Kuan

    Kuan

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    87
    Wonderful findings. Thank you for sharing!

    I would contribute my part to answer Unity current capability related questions,

    Pixel Snap will make the sprite shader snap all the post-transformed vertices to a screen pixels. Caveat is, it's snapping vertices of the sprite mesh rather than each pixel of the sprite.

    There are nothing between Unity 4.x and Unity 5.x that will affect your endeavor to achieve this.

    Good luck and have fun!
     
  6. Ultroman

    Ultroman

    Joined:
    Mar 10, 2014
    Posts:
    105
    I believe this, and many other problems pertaining to the moving of sprites, can be solved quite easily. I have solved this, by creating an entity-model for everything that has to be visible in my game, but it is not showing anything on the screen. It is just code, with my own Update-function which my maingamescript updates every update, for each entity in the scene (I have lists of them). This model has all my data for the entity, like position, status, current animation (a string), inventory etc., but it also controls the visibility of its screen-entity; a prefab with just a spriterenderer, colliders, an animator and an entity-script for changing animations on it and such. This means that the position of my entity-model is decoupled from the position of my entity-prefab (which has the sprite), so I change the position of my entity-model, and update the position of the prefab from there. This means I can have the correct position in my model, and then update the position of the actual sprite (prefab), using a method in its script that sets its position using the given position and then corrects its position to match the pixel-grid afterwards. This way the model is still working with the actual position. Your collisions will obviously still be positioned where the sprite is, because they're on the same prefab, but you could decouple the colliders and put them in a separate prefab. As long as your model has references to those two prefabs, you can control them from there; like, when they're active or removed from the world entirely, as well as their positions (offsets maybe?) depending on which animation is running (remember, the model knows the currentAnimation, and when changed it passes this information to the sprite-prefab which is running the actual animation).

    This also enables me to remove my prefab from the world when it is off-screen, and instantiate a new one when it is about to enter the screenspace again. I use objectpooling for this, obviously. When reintroduced to the world, my model knows all the information about which state the prefab should be in, and uses it when initializing the prefab.