Search Unity

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

Discussion in 'UGUI & TextMesh Pro' started by mrCharli3, Jan 10, 2018.

  1. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    976
    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?
     
  2. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    976
    ada7ke and quangdilinh like this.
  3. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    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:
    Code (CSharp):
    1. private float GetScale(int width, int height, Vector2 scalerReferenceResolution, float scalerMatchWidthOrHeight)
    2.         {
    3.             return Mathf.Pow(width/scalerReferenceResolution.x, 1f - scalerMatchWidthOrHeight)*
    4.                    Mathf.Pow(height/scalerReferenceResolution.y, scalerMatchWidthOrHeight);
    5.         }
    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.
     
    Last edited: Jan 11, 2018
  4. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    976
    Great information, thank you! Im doing PC so Ill do 1920x1080 with 0.5. :)
     
  5. y0u553ef

    y0u553ef

    Joined:
    May 2, 2017
    Posts:
    27
    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
     
  6. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    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:
    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.
     
    y0u553ef likes this.
  7. y0u553ef

    y0u553ef

    Joined:
    May 2, 2017
    Posts:
    27
    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
    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
     
  8. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    I think your formula should be
    Code (CSharp):
    1. (ScreenResolution.x /ScaleFactor) * (anchor.x) - image.width * (Pivot.x)
    This puts the absolute screen resolution into the scaled space of the rect transform first :)
     
    y0u553ef likes this.
  9. y0u553ef

    y0u553ef

    Joined:
    May 2, 2017
    Posts:
    27
    did try it still didn't work
    upload_2018-3-29_18-32-42.png
     
  10. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    If you want me to help, post your code. With that screenshot I can't do anything.
     
    y0u553ef likes this.
  11. y0u553ef

    y0u553ef

    Joined:
    May 2, 2017
    Posts:
    27
    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
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine.UI;
    4. using UnityEngine;
    5.  
    6. public class MoveMobile : MonoBehaviour
    7. {
    8.  
    9.     // Use this for initialization
    10.     private Vector2 start;
    11.     RectTransform rt;
    12.  
    13.     private float scaler;
    14.     private int width;
    15.     private int height;
    16.  
    17.     private float GetScale(int width, int height, Vector2 scalerReferenceResolution, float scalerMatchWidthOrHeight)
    18.     {
    19.         return Mathf.Pow(width / scalerReferenceResolution.x, 1f - scalerMatchWidthOrHeight) *
    20.                Mathf.Pow(height / scalerReferenceResolution.y, scalerMatchWidthOrHeight);
    21.     }
    22.  
    23.     void Start()
    24.     {
    25.         height = Screen.height;
    26.         width = Screen.width;
    27.      
    28.         scaler = GetScale(height, width,new Vector2 (800, 600), 0.25f);
    29.         Debug.Log(scaler);
    30.         rt = GetComponent<RectTransform>();
    31.      
    32.  
    33.     }
    34.  
    35.     //ScreenResolution/2-taille*0.5GetScale
    36.     private void Update()
    37.     {
    38.         if (Input.touchCount > 0) // if we have finger on screen
    39.         {
    40.             // get the first touch
    41.             Touch first = Input.GetTouch(0);
    42.  
    43.             if (first.phase == TouchPhase.Began)
    44.             {
    45.                 start = first.position;
    46.             }
    47.             else if (first.phase == TouchPhase.Moved)
    48.             {
    49.                 Vector2 NewPosition =  (first.position- start);
    50.                 start = first.position;
    51.                
    52.                 rt.anchoredPosition += NewPosition;
    53.             }
    54.         }
    55.     }
    56.    
    57. }
     
  12. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    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: https://forum.unity.com/threads/can...ng-ui-objects-to-stretch.512005/#post-3442785
    - 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.
     
  13. y0u553ef

    y0u553ef

    Joined:
    May 2, 2017
    Posts:
    27
    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
     
  14. djkpA

    djkpA

    Joined:
    Mar 28, 2016
    Posts:
    14
  15. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    @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:360*640, 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!
     
  16. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826

    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:

    Code (CSharp):
    1.  
    2. private float GetScale(int width, int height, CanvasScaler canvasScaler)
    3. {
    4.    var scalerReferenceResolution = canvasScaler.referenceResolution;
    5.    var widthScale = width / scalerReferenceResolution.x;
    6.    var heightScale = height / scalerReferenceResolution.y;
    7.    
    8.    switch (canvasScaler.screenMatchMode)
    9.    {
    10.       case CanvasScaler.ScreenMatchMode.MatchWidthOrHeight:
    11.          var matchWidthOrHeight = canvasScaler.matchWidthOrHeight;
    12.  
    13.          return Mathf.Pow(widthScale, 1f - matchWidthOrHeight)*
    14.                Mathf.Pow(heightScale, matchWidthOrHeight);
    15.      
    16.       case CanvasScaler.ScreenMatchMode.Expand:
    17.          return Mathf.Min(widthScale, heightScale);
    18.      
    19.       case CanvasScaler.ScreenMatchMode.Shrink:
    20.          return Mathf.Max(widthScale, heightScale);
    21.      
    22.       default:
    23.          throw new ArgumentOutOfRangeException();
    24.    }
    25.    
    26. }
    27.  
    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?
     
  17. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Thanks for the reply a lot! Seems that it's wrong formula. It's just a little better than original without any dpi calculations.
     
  18. jags_tech

    jags_tech

    Joined:
    Dec 4, 2013
    Posts:
    1
    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.
     
  19. Giammy47

    Giammy47

    Joined:
    Aug 1, 2017
    Posts:
    8
    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 :)
     
  20. MaximumSpice

    MaximumSpice

    Joined:
    Oct 25, 2018
    Posts:
    22
    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!