Search Unity

Getting Rect Transform Width and Height after Aspect Ratio Fitter executes

Discussion in 'Scripting' started by gerardpp, Dec 4, 2018.

  1. gerardpp

    gerardpp

    Joined:
    Dec 4, 2018
    Posts:
    7
    Howdy!

    I started my journey with Unity not so long ago and although many of the concepts are pretty straighforward, there are several details of it that don't look like it.

    I created a TileMap Manager (TMM) for my game, but the screen is not taking the entire canvas space, but just a square in the middle of it. I don't have any trouble with the communication of the TMM with the panel that represent my screen. What I am struggling with is whenever I check for rect.width / height, the return is zero (0).

    I know that at run-time, every instance of the game initializes at the same time, and I am trying to fetch that information while the Aspect Ratio Fitter is doing its thing. But what I am trying to do here is finding an elegant, easy-to-implement and extensible solution to this (because I know I will have this kind of situations along the way).

    Here is what I don't want to do:

    I have seen all kinds hacks and bad practices to go around this. I don't want to use co-routines or yielding the return of the start to fetch this information on frame 2 (In my opinion this is not what I want).

    I have also tried the SceneManager.OnSceneLoaded event listener, but it happens before everything is instantiated and end up getting no results.

    This is what (maybe) I want to achieve:

    I was trying to create an event listener to alert the TMM from within the panel that everything is ready for it to access all its data elements. That approach worked, but using OnPostRender (that is a recurrent event), and for the sake of efficiency, I don't want to create more processes running than the required.

    Is there an event inherited from Monobehavior that tells the game "this element is already redered, resized and initiallized" so all the other members of the hierarchy can access it?

    Or maybe I thought extending AspectRatioFitter to add a trigger event to alert TMM the right moment it can access the Panel info.

    Please help... I am giving this problem more thoughts than it should.
     
    _TheFuture_ likes this.
  2. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    I am afraid there is no good solution for this.
    if you review the implementation of the Aspect Ratio Fitter you can see that
    SetDirty()
    is set with
    delayUpdate
    set to
    true
    inside
    OnEnable()
    . This will trigger the
    UpdateRect()
    method in the next frame.
    Unfortunately the
    UpdateRect()
    method is private and not marked for overriding, so, deriving from that class and raising an event at the right time is not possible.

    So, you have three options:
    1. Do it dirty and simply wait one or two frames
    2. Write a monitoring class which detects when the size is changing and raises an event (as you already where thinking about)
    3. Write your own implementation of Aspect Ratio Fitter (you can copy and modify the class from BitBucket)
     
    _TheFuture_ likes this.
  3. gerardpp

    gerardpp

    Joined:
    Dec 4, 2018
    Posts:
    7
    Thank you very much Hosnkobf for your response. Unfortunately these are not good news for me.

    The concept behind having asynchronous events happening at the same time in something that is supposed to be a system is an easy problem to realize, yet extremely complicated to solve (more than it should be).

    There are tons of Unity beginners banging their heads to go around this, and so many non-so-practical solutions to it.

    I have not dropped the towel, (not yet). I will keep researching about this matter.

    Any other thoughts?
     
  4. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    I am not sure what you want to research further... I gave you a link to the source code, so you can review the actual implementation. I agree that the implementation is not very thought through and it is hacky to wait a frame and also not so cool to track changes every frame.
    But the solution to write your own implementation is something you could do and raise an event there.
     
  5. gerardpp

    gerardpp

    Joined:
    Dec 4, 2018
    Posts:
    7
    Yeah, and that is the most elegant approach. The thing is, that custom implementation of the AspectRatioFitter Class solves perfectly this problem, but it doesn't solve every type of situation on which the progression of the flow of events depends on something to happen or is made available.

    Sorry if I didn't give you enough background to a possible answer. When I said extensible I was thinking on a solution I can implement to solve any kind of these situations.

    Thanks again for your contribution!
     
  6. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    Thank you for your thoughts.
    You have triggered some Ideas how I could improve my asset Better UI. Not in the next release but in the release after the next probably.
    If you know right now already where you would need to solve something like that additionally, I would be glad to put them on my list as well :)
     
  7. claus_interactive

    claus_interactive

    Joined:
    Nov 22, 2018
    Posts:
    10
    Hello! Looking for solving that exact same issue (Aspect Ratio Fitter not executing immediately and thus not giving me the correct size of the RectTransform in the frame I would need it), i found this thread. Thanks for the information and discussion here.

    I ended up taking some code out of the Aspect Ratio Fitter implementation and adding it to my own RectTransformExtensions extension class, so i can use it simply in code. That works best for me (could probably also be used in your own component).

    RectTransformExtensions.cs
    Code (CSharp):
    1. public static class RectTransformExtensions {
    2.  
    3.     // (...)
    4.  
    5.     public static void HeightControlsWidth(this RectTransform rectTransform, float aspectRatio) {
    6.         rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, rectTransform.rect.height * aspectRatio);
    7.     }
    8.  
    9.     public static void WidthControlsHeight(this RectTransform rectTransform, float aspectRatio) {
    10.         rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, rectTransform.rect.width / aspectRatio);
    11.     }
    12.  
    13.     public static void FitInParent(this RectTransform rectTransform, float aspectRatio, bool envelopeParent = false) {
    14.         rectTransform.anchorMin = Vector2.zero;
    15.         rectTransform.anchorMax = Vector2.one;
    16.         rectTransform.anchoredPosition = Vector2.zero;
    17.  
    18.         Vector2 sizeDelta = Vector2.zero;
    19.         Vector2 parentSize = rectTransform.GetParentSize();
    20.         if ((parentSize.y * aspectRatio < parentSize.x) ^ (!envelopeParent)) {
    21.             sizeDelta.y = rectTransform.GetSizeDeltaToProduceSize(parentSize.x / aspectRatio, 1);
    22.         } else {
    23.             sizeDelta.x = rectTransform.GetSizeDeltaToProduceSize(parentSize.y * aspectRatio, 0);
    24.         }
    25.         rectTransform.sizeDelta = sizeDelta;
    26.     }
    27.  
    28.     public static Vector2 GetParentSize(this RectTransform rectTransform) {
    29.         RectTransform parent = rectTransform.parent as RectTransform;
    30.         if (!parent) {
    31.             return Vector2.zero;
    32.         }
    33.         return parent.rect.size;
    34.     }
    35.  
    36.     public static float GetSizeDeltaToProduceSize(this RectTransform rectTransform, float size, int axis) {
    37.         return size - rectTransform.GetParentSize()[axis] * (rectTransform.anchorMax[axis] - rectTransform.anchorMin[axis]);
    38.     }
    39.  
    40. }
    Usage in other code:

    Code (CSharp):
    1. float aspectRatio = 16/9f;
    2. rectTransform.FitInParent(aspectRatio);
    3. Debug.Log(rectTransform.rect.size); // prints the new size correctly
    4.  
     
    Olipool likes this.