Canvas Scaler - Scale with screen size, same as setting UI-Objects to stretch?

I’ve been looking at tutorials for UI most of the day and couldnt find a single one talking about the Canvas Scaler attached to Canvas. I found a lot of videos of people manually setting anchors in each corner of the UI-object so that it stretched properly, but to me it seems like setting the Canvas Scaler to “Scale with screen size” does that for you kind of? The anchors don’t actually change but it seems like things are stretching in the same way?

Is there anyone with more experience than me that could tell me what you set you Canvas Scaler to? And how you deal with properly scaling your UI-objects to resolutions using that setting?

Found the answer to most of my questions here: Redirecting to latest version of com.unity.ugui

However I still wonder what I should be using as reference resolution? Im developing for PC.

2 Likes

Hey there,

As you probably have already read, the canvas scaler does help a lot in creating a similar ui experience, but does not do the same thing as setting the anchors in the corners you want the ui to be.
I recommend creating a simple ui setup, moving the game window in its own window and setting the aspect ratio to to free aspect. If you resize the window, you can see rather well what happens.
As for the reference resolution:
With recttransforms at every ui element you can set quite a lot of absolute unit values (e.g. Left, right, top, bottom). Those need to have some kind of reference to be able to be recalculated for larger of smaller screens. That’s were your reference resolution comes in. It defines the resolution your absolute values in the recttransforms are the ones you entered.
Here is some math:
Formula for scaling:

private float GetScale(int width, int height, Vector2 scalerReferenceResolution, float scalerMatchWidthOrHeight)
        {
            return Mathf.Pow(width/scalerReferenceResolution.x, 1f - scalerMatchWidthOrHeight)*
                   Mathf.Pow(height/scalerReferenceResolution.y, scalerMatchWidthOrHeight);
        }

Reference Resolution: 800x600
Screen Match mode: Match Width or Height
Match: 0.5

Screen 1: 800x600
UI Scaling: 1

Screen 2: 1024x768
UI Scaling: Pow(1024/800, 0.5) * Pow(768/600, 0.5) = 1.28

Screen 3: 1920x1080
UI Scaling: Pow(1920/800, 0.5) * Pow(1080/600,0.5) = 2.078

I recommend taking a reference resolution for a screen you will expect (so when developing for PC maybe 1920x1080, for an iPhone I would go with 375x667 because of their design guidelines of using half resolution). For the matching mode think of what screens you want to support and also if it will be landscape and portrait or just one of them. Mostly you’re good to go with Matching of 0.5 Width or Height.

8 Likes

Great information, thank you! Im doing PC so Ill do 1920x1080 with 0.5. :slight_smile:

I did try to use your solution to get the scalefactor value so I can use it to get the real value of the ui element I did multiply the factor within the height and width of th thelement but I didn’t get identical value , kt was close but not the identical value I do need

Just to be sure: The width and height are for the screen resolution not the element size. If you were using the screen resolution, you can easily check if my formula is correct by taking a look at the canvas root component with the scaler:
3442785--272467--upload_2018-3-29_8-55-52.png
You can check if the scale does follow my formula. As for me, I get the exact same values (unity 5.6) and I don’t think they changed anything about the formula.

If everything checks, the next step would be to check if you are using scaling anywhere in the parent components of the component you want to get the width and height for, and if the component you want to get the height for has different min max anchors.

If none of my solutions work for you, please come with screenshots and a better description of the setup.

1 Like

okay u made me little confused , here what I am trying to do , I am trying to move ui element (image) inside the screen (canvas) with my finger touch input (mobile) so when I keep swiping left the position of the image keep moving on the X axes even if that postion is out of the screen canvas , what I am trying to do is to get the maximal and minal value the ui image could get on both x and y axes so it won’t get out of the screen
so I gave the image the following values
3443118--272503--upload_2018-3-29_15-9-12.png
and then I did made this formula the maximum value the image could have on x axes for example is
ScreenResolution.X*(anchor.x)-image.width*(Pivot.X)*ScaleFactor
it seems like this didn’t work , I get very close values but still far enought to make go mad

I think your formula should be (ScreenResolution.x /ScaleFactor) * (anchor.x) - image.width * (Pivot.x)
This puts the absolute screen resolution into the scaled space of the rect transform first :slight_smile:

1 Like

did try it still didn’t work
3443398--272524--upload_2018-3-29_18-32-42.jpg

If you want me to help, post your code. With that screenshot I can’t do anything.

1 Like

here is the code , I didn’t add the screen limit condition yet , I did try you formula just by calculation the position manually and putting them on the inspector

using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;

public class MoveMobile : MonoBehaviour
{

    // Use this for initialization
    private Vector2 start;
    RectTransform rt;

    private float scaler;
    private int width;
    private int height;

    private float GetScale(int width, int height, Vector2 scalerReferenceResolution, float scalerMatchWidthOrHeight)
    {
        return Mathf.Pow(width / scalerReferenceResolution.x, 1f - scalerMatchWidthOrHeight) *
               Mathf.Pow(height / scalerReferenceResolution.y, scalerMatchWidthOrHeight);
    }

    void Start()
    {
        height = Screen.height;
        width = Screen.width;
      
        scaler = GetScale(height, width,new Vector2 (800, 600), 0.25f);
        Debug.Log(scaler);
        rt = GetComponent<RectTransform>();
     

    }

    //ScreenResolution/2-taille*0.5GetScale
    private void Update()
    {
        if (Input.touchCount > 0) // if we have finger on screen
        {
            // get the first touch
            Touch first = Input.GetTouch(0);

            if (first.phase == TouchPhase.Began)
            {
                start = first.position;
            }
            else if (first.phase == TouchPhase.Moved)
            {
                Vector2 NewPosition =  (first.position- start);
                start = first.position;
               
                rt.anchoredPosition += NewPosition;
            }
        }
    }
   
}

Alright, this looks good so far.
So, please check the following:

  • Your canvas scaler has a reference resolutoin of 800x600 and is set to matchwitdhheight 0.25f
  • If that is the case, is the debugged scaler value you are calculating the exact same value as the canvas scaling value. You can check by reading the value from the rect transform as suggested here: Canvas Scaler - Scale with screen size, same as setting UI-Objects to stretch?
  • If that fits as well, check if the parent objects of you moving object have a scale different to 1,1,1. This would of course change the scale you would need.

thank you very much for you effort but I did find another solution getting the size of the canvas directly and setting up the anchor to the middle so the borderds of the screen are the following -canvas.size.x/2 , canvas.size.y/2 , canvassize.x/2 , -canvas.y/2

I have a small doubt when using Canvas Scaler - Scale with screen size, which I asked here. Can someone answer that?

@Johannski Hi there! Could you help me a little bit with a CanvasScaler?
I’am making an android/ios app, which is looks like native material design. But i can’t handle how to reach my goal: make layout adaptive for tablets.
My CanvasScaler settings - Scale With Screen Size, Reference:360640, Match Mode:Expand, Pixels Per Unit:100.
I made app photos on real devices, i think it much better for understanding what i want.
Here is nexus 5 ( 4.95", 1920x1080) and Lenovo TAB 2 A7-20F (7", 1024x600) with default settings(above)


And here is my goal, i changed Reference to 544
640 to reach this effect

How i got 544*640 reference? Nexus dpi is 445, lenovo is 170. I got 445/170 = 2.617. And width difference is 1080-600 = 480. So 480/2.6 = 184. 360(initial width reference) + 184 = 544.
My question: Is this formula correct? Or may be there is another formula without using dpi(cause i can’t get device dpi in editor). Thanks!

Hey there!
So well, depends on what you want to achieve. From what I can tell, I think you want to have the same physical size no matter the device. With that in mind, my answer for "Is there anonther formula without using DPI my answer is no, since the dpi is your only connection between the amount of pixel you have to the actual physical size of a pixel.

As for the formula: Not that easy to wrap the head around, but here are my thoughts:

First without taking the Canvas Scaler in consideration:

  • You have the Resolution and DPI of your nexus which acts as your “ground truth” how large your ui should appear.
  • You have the Canvas Scale for that ui (3)
  • You have the resolution and DPI for your device you want to know the scale
  • You’re searching for the Canvas Scale of that device.

The number of pixels for one inch on your nexus are the DPI = 445
The number of unity ui pixels are DPI / scale factor = 445 / 3 = 148.33

The DPI of your lenovo are 170, so the canvas scale s to get the same physical scale is
170 / s = 148.33
s = 170 / 148.33
s = 1.46

Alright, awesome so that’s the scale we want to get for the canvas. So now we have to calculate the necessary reference resolution to get that scale. Since you’re not using match width or height, I adapted my get scale calculation:

private float GetScale(int width, int height, CanvasScaler canvasScaler)
{
   var scalerReferenceResolution = canvasScaler.referenceResolution;
   var widthScale = width / scalerReferenceResolution.x;
   var heightScale = height / scalerReferenceResolution.y;
   
   switch (canvasScaler.screenMatchMode)
   {
      case CanvasScaler.ScreenMatchMode.MatchWidthOrHeight:
         var matchWidthOrHeight = canvasScaler.matchWidthOrHeight;

         return Mathf.Pow(widthScale, 1f - matchWidthOrHeight)*
               Mathf.Pow(heightScale, matchWidthOrHeight);
      
      case CanvasScaler.ScreenMatchMode.Expand:
         return Mathf.Min(widthScale, heightScale);
      
      case CanvasScaler.ScreenMatchMode.Shrink:
         return Mathf.Max(widthScale, heightScale);
      
      default:
         throw new ArgumentOutOfRangeException();
   }
   
}

We want to get the reference resolution [x,y], so looking at the formula for widthScale we see
s = resultionX / x
x = resolutionX / s;
so for lenovo:
x = 600 / 1.46 = 410.96
y = 1024 / 1.46 = 701.36

So the reference resolution I calculated is 411x701, which is quite different from 544x640.

Does that make sense?

Thanks for the reply a lot! Seems that it’s wrong formula. It’s just a little better than original without any dpi calculations.

Hi Guys,
I am facing different problem here, I am using canvas scaler to scale my UI elements, and also using the local scale to change the size of UI elements, it’s working perfectly on editor but it’s not working on my device ( local scale is not reflecting ) and but the scale values of UI element are correct when I log the values. Any help on this.

For everyone who comes here for a similar problem, after a few hours of headaches I’ve found a simple solution. Just apply a canvas scaler and an aspect ratio fitter components to your main canvas. After that, set canvas scaler UI scale mode to scale with screen size and select match to 1 if your canvas is in porttrait mode, 0 if it’s in landscape mode.
Than go to the aspect ratio fitter and select aspect mode to “height controls width” if in portrait mode, while “width controls height” if in landscape. Hope this will help :slight_smile:

4 Likes

Still having to play around with mine but this gave me hope, simple enough and working better than I had before and I had no idea what to do. Thanks man!