How to set up pixel perfect UI for mobile devices?

I am working on a 2D pixel art game for mobile devices and am at an absolute loss as to how I should set up the UI. I am of course using the 2D Pixel Perfect package, but this does not seem to affect UI. I need to have UI elements that follow game objects, so I tried using World Space canvases. However, these cannot be set to pixel perfect, resulting in jittery movement since the game objects are set to pixel perfect. I then tried using a Screen Space canvas with the scaling mode set to Scale With Screen Size, and while this initially works, the UI elements do not scale properly when rotating the device between portrait and landscape. Finally, I tried setting the scaling mode to Constant Pixel Size, but the UI elements don’t have the same ratio of pixels per unit as the game objects, despite matching values in the inspector.

I really don’t know how to proceed from here. I can think of a few custom scripts I could write to fix these issues, just as manually snapping UI elements to a grid or reading the device orientation to change the reference resolution, but I can’t help feeling that these are extra steps and I am overlooking something. I would greatly appreciate any help with this issue.

1 Like

Hi!
I have the same problem. I’ve searched around and looks like there isn’t a proper solution for this. I wanted to achive pixel perfect UI, without distortion. I tried to set the Canvas to Screen Space - Camera, on the camera having the pixel perfect component and the canvas scaler mode to Scale with Screen Size (pixel perfect camera resolution and canvas scaler resolution the same).
Well, it kinda worked. However, this method distorts the images.
If someone found a solution to achive pixel perfect UI, please reply.

2 Likes

I know this is a super late reply, but I wanted to post what I ended up doing for anybody who finds this thread. For snapping word space UI elements to a grid, I created a PixelSnapper script that gets a reference to the Pixel Perfect Camera component and performs the following in LateUpdate():

// Check whether transform has changed
        if (transform.hasChanged)
        {
            // Set target position
            Vector2 targetPosition;
            if (transform.parent != null)
                targetPosition = (Vector2)transform.parent.position + (Vector2)pixelPerfectCamera.RoundToPixel(transform.localPosition);
            else
                targetPosition = transform.position;

            // Snap to pixel grid
            transform.position = pixelPerfectCamera.RoundToPixel(targetPosition);
            transform.hasChanged = false;
        }

Then, to dynamically change the scale based on the orientation, I created a PixelPerfectCanvasScaler script that inherits from UIBehaviour, allowing for OnRectTransformDimensionsChanged to be defined. That script gets a reference to the Pixel Perfect Camera component and the Canvas Scaler component and performs the following:

// OnRectTransformDimensionsChange is called when an associated RectTransform has its dimensions changed.
    protected override void OnRectTransformDimensionsChange()
    {
        if (gameObject.activeInHierarchy)
            StartCoroutine(SetScaleFactorAtEndOfFrame());
    }

    // Call SetScaleFactor at end of frame
    IEnumerator SetScaleFactorAtEndOfFrame()
    {
        yield return new WaitForEndOfFrame();
        SetScaleFactor();
    }

    // Set the canvas scale factor to match the pixel perfect canvas
    void SetScaleFactor()
    {
        canvasScaler.scaleFactor = pixelPerfectCamera.pixelRatio;
    }
1 Like

How to access the UIBehaviour? It doesn’t exist

Also, I tried the code and it doesn’t work (I’m using the ScreenSpace - Camera). The UI images of the Canvas are just thrown away.

Nevermind, now works, but still it vibrates when I move.

You need to use the EventSystems namespace. using UnityEngine.EventSystems;

What vibrates? The camera or the UI?

The UI. Just to test I attached the Camera to the Player GameObject and tested a bit. When moving, the UI vibrates.

Is the UI in screen space or world space? I’ve only tested the PixelSnapper script with world space UI objects.

The UI is Screen Space, so I think it’s beacuse of that.