Search Unity

Canvas Scaler issues

Discussion in 'UGUI & TextMesh Pro' started by tosiabunio, Nov 3, 2014.

  1. tosiabunio

    tosiabunio

    Joined:
    Jun 29, 2010
    Posts:
    115
    Should the canvas scaler affects positioning as well? I'm using the scaler with 2x, 3x, 4x scales depending on the screen height and it works quite nice. The problem is that all my positionings which references the world coordinates (via Camera.main.WorldToScreenPoint()) need to take Canvas scale into consideration. That is when the scale is 2 for 720p resolution all position coordinates returned by WorldToScreenPoint() need to be divided by 2 in order to fit the screen.

    I'm not sure about that (not enough testing) but this problem affects only those elements which are positioned relative to the canvas. UI elements in nested containers below don't have to know anything about the canvas scale.
     
  2. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    For "Screen Space - Overlay" mode, Camera.main.WorldToScreenPoint doesn't work at all. For other modes, it will convert to position in the screen in pixels. This should work regardless of scale factor in the canvas.

    Of course, if you want to use the returned values for something that's inside the canvas, the screen pixels values are not really what you need. If so, you'll need to convert those screen pixel values into the appropriate parent space before proceeding.
     
  3. tosiabunio

    tosiabunio

    Joined:
    Jun 29, 2010
    Posts:
    115
    Okay, but if the canvas scale is set to 1 Camera.main.WorldToScreenPoint return values that match canvas positioning, right? At least that's what's worked for me till I started to use scaling.

    But I assume that this is intended behavior - positioning is being affected by scaling and I should consider scale when calcuating sizes and positions, right?
     
  4. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    It really depends on what Canvas Render Mode you use, and what you use the positions for. Do you use them to set world positions, local positions, or anchored positions? Do you set positions on direct children of the Canvas or children of children? Do any of your UI elements that are parents of the objects you want to position have scaling or rotation? In some very specific combinations of conditions, screen space pixel coordinates may be able to be used for positioning of UI elements, but in most cases it won't work.

    All objects in the scene are affected by the position, rotation, and scale of all their parents up the parent chain. UI elements are no exception. If the Canvas has a non-(1,1,1) scale, that needs to be taken into account, as does it if any other parent has a non-(1,1,1) scale.

    In order to get the position in the local space of the parent of the element you want to position, you can use:
    http://docs.unity3d.com/460/Documentation/ScriptReference/Transform.InverseTransformPoint.html

    However, note that this takes a world space position as input to start with. No reason to go around screen space pixels at all, really.

    If you use transformations between spaces like this, it will work regardless of Canvas Render Mode and also work regardless of how elements and their parents are scaled and/or rotated.
     
  5. tosiabunio

    tosiabunio

    Joined:
    Jun 29, 2010
    Posts:
    115
    Thank you for the explanation. I understand how hierarchy affects objects below, I just wrongly assumed that Canvas Scaling is somewhat different from that hence my confusion.

    I'm using positions for anchored positions only and I understand that they are always relative to containing element. I have a simple sidescrolling pixel art 2D game and need to position UI elements next to objects in the scene therefore I need some way to translate game object position to screen position (I'm using Camera.main.WorldToScreenPoint for that). Keeping everything in pixel coordinates helps me understand what's going on. I don't rotate and scale UI elements and don't mess with transforms in hierarchy in any other way than changing anchored position. The only exception is the canvas scaling needed to match the camera scaling (integer scaling to get a possible best match to the vertical resolution of the screen).

    I will follow your links and will try to understand how it can help me. Thanks again.
     
  6. myama

    myama

    Joined:
    May 2, 2014
    Posts:
    14
    I was having this same problem. My canvas is Screen Space - Overlay. I am trying to position an object in the canvas over a certain world position. I was using Camera.main.WorldToScreenPoint() to convert to screen point and then using rectTransform.anchoredPosition to position the object. But this does not work when the Canvas scale is set to 2 (which I do for iOS retina devices).

    I could not get Transform.InverseTransformPoint to work. Other people had the same issue here:

    The answer by leni8ec worked for me, with Canvas scale set to 2:
    Vector2 viewportPoint = Camera.main.WorldToViewportPoint(pos); //convert game object position to VievportPoint
    // set MIN and MAX Anchor values(positions) to the same position (ViewportPoint)
    rectTransform.anchorMin = viewportPoint;
    rectTransform.anchorMax = viewportPoint;

    I am concerned that this is a slow operation as ian_sorbello pointed out. I am not animating my object so it's okay for now. There were other solutions that were faster in that answer, but they require specifics about the anchor.

    The question I have is why doesn't Transform.InverseTransformPoint work?

    Thanks
     
  7. nmd

    nmd

    Joined:
    Aug 5, 2014
    Posts:
    3
    I've been trying to determine the width of a Canvas object in relation to world space. As mentioned above it largely depends on Canvas Scaler settings.

    If set to Constant Pixel size, and you convert world > screen or screen > world you will get consistent results i.e. you can simply convert world and screen space objects to your preferred units (e.g. Camera.main.ScreenToWorldPoint) and do a comparison.

    However in Scale With Screen Size mode, the screen > world conversion will vary depending on the Canvas.scaleFactor applied when it renders to a specific screen size. RectTransform units will not change, but their apparent size in world units will change in relation to the scaleFactor.

    Basically, the world size remains consistent, but the RectTransform is scaled to fit the screen size. Therefore you must take the Canvas.scaleFactor into account when converting RectTransforms to world space and vice versa.
     
    Wanderer13 likes this.
  8. karma0413

    karma0413

    Joined:
    Dec 24, 2014
    Posts:
    6
    I finally figured it out.... i was having a simiar problem....

    Quick Description of my problem:
    I was using the Canvas to display a sprite ( a healthbar) which floats over all the characters. The problem however, was when i tried to obtain the WorldToScreenPoint it kept giving a result that was slightly off.... for example: the healthbar looked a little okay when the character was immediately in front of the camera... but as the character walks to the edge of the camera's fulstrum, the screens x,y placement becomes more and more incorrect.

    Days and days of research and trying different combinations finally showed me that maybe there is a scaling issue which pointed me to look at **Canvas / Canvas Scaler / scaling mode: scale with screen**

    Originally, this worked wonderfully when i had only 1 character and his healthbar stayed stuck to the top of the screen like old classic double dragon games. BUT when i made the decision to have many characters and they all need "floating healthbars", i didnt come back to re-evaluate whether this option needed to change.

    **Setting the canvas Scaler to: keep constant pixel size**
    , fixes the problem and i now have the correct WORLDtoSCREENpoint that i needed! And now the healthbar floats beautifully above the characters...

    BUT WAIT, ANOTHER PROBLEM! Now, if the screen resolution is small... the Ui sprite is obsurdly large..... and if the screen resolution is high definition then Ui sprite is way too small!

    QUESTION: So how do i use the "scale with screen size" mode, but yet also still get back a correct WorldToScreenPoint?

    ANSWER: you must take into consideration the overal scaling of the canvas when it is stretched to fit (whatever current resolution that you are using)

    INSTEAD OF:

    RectTransform myRect = GetComponent<RectTransform>();
    Vector2 myPositionOnScreen = Camera.main.WorldToScreenPoint (myOwner);
    myRect.anchoredPosition = myPositionOnScreen;

    YOU CALCULATE THE OVERALL SCALE FACTOR LIKE THIS:

    RectTransform myRect = GetComponent<RectTransform>();
    Vector2 myPositionOnScreen = Camera.main.WorldToScreenPoint (myOwner);

    Canvas copyOfMainCanvas = GameObject.Find ("Canvas").GetComponent <Canvas>();
    float scaleFactor = copyOfMainCanvas.scaleFactor

    Vector2 finalPosition = new Vector2 (myPositionOnScreen.x / scaleFactor , myPositionOnScreen.y / scaleFactor);
    myRect.anchoredPosition = finalPosition;


    If this helped anyone please log in to give me a thumbs up....