M._.rius
M._.rius

Reputation: 11

How to make UI look Pixel Perfect in Unity?

I am working on an pixel game and now I want to add the UI, and it needs to work at different scales and so on. That's why I used the Unity UI System. However, now UI doesn't look pixel-perfect, the pixels are distorted.

(I am using a script that scales the camera to make the UI look good in every resolution. The script is not mine though)

using UnityEngine;
using UnityEngine.Rendering;
    
namespace GGEZ
{    
    [
     ExecuteInEditMode,                                       // Run this script in edit mode so the preview window looks good
     RequireComponent (typeof(Camera)),                       // Only add this component if there is a camera
     HelpURL ("http://ggez.org/posts/perfect-pixel-camera/"), // Website opened by clicking the book icon on the component
     DisallowMultipleComponent,                               // Only one of these per GameObject
     AddComponentMenu ("GGEZ/Camera/Perfect Pixel Camera")    // Insert into the "Add Component..." menu
    ]
    public class PerfectPixelCamera : MonoBehaviour
    {
    
    // Set this value to the same value as Pixels Per Unit when importing sprites
    [
        Tooltip ("The number of texture pixels that fit in 1.0 world units. Common values are 8, 16, 32 and 64. If you're making a tile-based game, this is your tile size."),
        Range (1, 64)
    ]
    public int TexturePixelsPerWorldUnit = 16;
    
    // Reference to the camera on this same GameObject. Found
    // by the OnEnable function.
    private Camera cameraComponent;
    
    // Set to a value that compensates for the half-pixel offset when rendering
    // with Direct3D. This is automatically handled by Unity 5.5 and later.
    // If that's the case, it is declared as a constant 0 which the compiler
    // can use to optimize calculations in LateUpdate.
    // See: https://docs.unity3d.com/Manual/UpgradeGuide55.html
    #if UNITY_5_5_OR_NEWER
    private const float halfPixelOffsetIfNeededForD3D = 0f;
    #else
    private float halfPixelOffsetIfNeededForD3D;
    #endif
    
    // Objects that you want to be perfectly aligned should have X and Y
    // coordinates that are integer multiples of this value. It is always
    // safe to align to 1.0 / TexturePixelsPerWorldUnit, but this value can
    // be smaller if the camera is zoomed and will make movement more smooth.
    public float SnapSizeWorldUnits { get; private set; }
    
    //---------------------------------------------------------------------------
    // OnEnable - Called by Unity when the component is created or enabled
    //---------------------------------------------------------------------------
    void OnEnable ()
        {
    
        // Grab a reference to the camera
        this.cameraComponent = (Camera)this.GetComponent (typeof(Camera));
    
    #if !UNITY_5_5_OR_NEWER
    
        // Detect whether we are using Direct3D, because D3D rendering has a
        // half-pixel offset from OpenGL rendering.
        bool isD3D =
                SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D9
                || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11
                || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D12;
    
        // 0.4975f and not 0.5f is used because 0.5f is able to be represented
        // as a perfect IEEE float. This means that when added to other
        // floats that are imperfect, the results can sometimes be rounded
        // the wrong way. It can be tricky to reproduce so this isn't part
        // of the main demo.
        this.halfPixelOffsetIfNeededForD3D = isD3D ? 0.4975f : 0f;
    
    #endif
    
        // Run the LateUpdate immediately so that the projection gets set up
        this.LateUpdate ();
    
        }
    
    //---------------------------------------------------------------------------
    // OnDisable - Called by Unity when the component is disabled or destroyed
    // This function cleans up after the PerfectPixelCamera so that the
    // projection matrix isn't left in an altered state by this component.
    //---------------------------------------------------------------------------
    void OnDisable ()
    { 
        if (this.cameraComponent == null)
            {
            return;
            }
        this.cameraComponent.ResetProjectionMatrix ();
        this.cameraComponent = null;
    
        }
    
    //---------------------------------------------------------------------------
    // LateUpdate - Called by Unity after all other functions have run Update.
    // If you have other scripts that use LateUpdate, you might want to use
    // the Script Execution Order project setting to make this script run last.
    //---------------------------------------------------------------------------
    void LateUpdate ()
        {
    
        // Get a local reference
        Camera camera = this.cameraComponent;
    
        // Make sure the camera is in 2D mode
        camera.transparencySortMode = TransparencySortMode.Orthographic;
        camera.orthographic = true;
        camera.transform.rotation = Quaternion.identity;
        camera.orthographicSize = Mathf.Max (camera.orthographicSize, 0.00001f);
    
        // This is the code that computes the parameters needed to perfectly map
        // world-space pixels to screen-space pixels.
        var pixelRect = camera.pixelRect;
        float texturePixelsPerWorldUnit = this.TexturePixelsPerWorldUnit;
        float zoomFactor = Mathf.Max (1f, Mathf.Ceil ((1f * pixelRect.height) / (camera.orthographicSize * 2f * texturePixelsPerWorldUnit)));
        float halfWidth  = (1f * pixelRect.width)  / (zoomFactor * 2f * texturePixelsPerWorldUnit);
        float halfHeight = (1f * pixelRect.height) / (zoomFactor * 2f * texturePixelsPerWorldUnit);
        float snapSizeWorldUnits = 1f / (zoomFactor * texturePixelsPerWorldUnit);
        float halfPixelOffsetInWorldUnits = halfPixelOffsetIfNeededForD3D * snapSizeWorldUnits;
        float pixelPerfectXOffset = halfPixelOffsetInWorldUnits - Mathf.Repeat (snapSizeWorldUnits + Mathf.Repeat (camera.transform.position.x, snapSizeWorldUnits), snapSizeWorldUnits);
        float pixelPerfectYOffset = halfPixelOffsetInWorldUnits - Mathf.Repeat (snapSizeWorldUnits + Mathf.Repeat (camera.transform.position.y, snapSizeWorldUnits), snapSizeWorldUnits);
    
        // Save the snap size so other scripts can use it
        this.SnapSizeWorldUnits = snapSizeWorldUnits;
    
        // Build a manual projection matrix that fixes the camera!
        camera.projectionMatrix = Matrix4x4.Ortho (
                -halfWidth + pixelPerfectXOffset,
                 halfWidth + pixelPerfectXOffset,
                -halfHeight + pixelPerfectYOffset,
                 halfHeight + pixelPerfectYOffset,
                camera.nearClipPlane,
                camera.farClipPlane
                );
    
        }
    }
}

Pixel Problem Inspector of Canvas

So, how can I make the UI Pixel Perfect?

Upvotes: 0

Views: 5243

Answers (1)

FilipToth
FilipToth

Reputation: 157

Just set the PixelPerfect property to true on your canvas. Unity automatically takes care of what you're trying to do (although it's always nice to have something custom built so, you have more control over it)

Upvotes: 2

Related Questions