Get position or dimensions of children of RectTransform with ContentSizeFitter + Layout Group?

This is my object structure:

  • Parent1 - Scroll Rect, Mask

  • Parent 2 - Content Size Fitter (vertical preferred), Vertical Layout Group

  • Children - Content Size Fitter (vertical min), Vertical Layout Group

I am trying to get the RectTransform’s Pos Y or Height variables shown in Inspector so that I can manipulate some stuff in code. However, trying to retrieve RectTransform.rect or anchoredPosition from the Children objects always yield worthless values - either zeroed out or completely wrong.

I have tried using a Coroutine to wait for end of frame, waiting until the next frame for stuff to initialize, calling Canvas.ForceUpdateCanvases but nothing has worked.

Bottom line: How do I get the aforementioned values as they are shown in the inspector while the scene is running?

Please help.

Note that there’s actually a bunch of different values that can be shown in the inspector of a RectTransform depending on how the transform’s anchors are set. In your example hierarchy, I think the anchors are going to be dictated by some of the layout classes, so there is presumably one specific answer for this exact example, but in the more general case getting “the values in the inspector” is actually a bunch of different problems with different answers depending on the anchors. For modifying stuff through script, it’s probably better to focus on what’s conveniently available in script instead of trying to convert everything to and from the inspector values.

I’m moderately sure that RectTransform.rect.size should give you useful size data, though. The position of that rectangle isn’t directly useful for much because it’s in the transform’s own coordinate space (and therefore doesn’t necessarily change even when the object moves).

You could alternately try using RectTransform.sizeDelta, but since that’s the size relative to the anchors it won’t be the same as the actual total size unless all 4 anchors are set to the same point.

To set the size to a specific value, you can use SetSizeWithCurrentAnchors. (Or you can set sizeDelta directly, but again, remember that might not be the “true” size.)

RectTransform.anchoredPosition is the recommended way to modify a RectTransform’s position relative to the parent, and works great if you want to offset the current position by some amount, but figuring out what its absolute value means is…complex.

To get the size and position of one RectTransform relative to another, there’s a couple techniques you could try.

Perhaps the simplest option is to use Transform.TransformPoint to convert points from a given transform’s local space into world space, and then InverseTransformPoint to transform those into the local space of some other transform. (I haven’t personally tried this.)

Another option is to multiply RectTransform.anchorMin/anchorMax by the parent’s size to determine the anchors’ positions in the parent’s coordinate space, then use RectTransform.offsetMin/offsetMax to get the transform’s coordinates relative to those anchors. Here’s a snippet of some code I recently wrote some code that works on this principle:

Vector2 parentSize = parentRT.rect.size;

// Calculate our corners relative to parent
Vector2 ourMinCorner = ourRT.anchorMin.ComponentMultiply(parentSize) + ourRT.offsetMin;
Vector2 ourMaxCorner = ourRT.anchorMax.ComponentMultiply(parentSize) + ourRT.offsetMax;

…where “ComponentMultiply” is just my personal shortcut for multiplying each of the coordinates independently:

public static Vector2 ComponentMultiply(this Vector2 v, Vector2 other)
{
    return new Vector2(v.x * other.x, v.y * other.y);
}

You might also need to multiply/divide by transform.localScale somewhere in there if the objects in your hierarchy aren’t all at scale 1.

2 Likes

Once again @Antistone has laid out a bunch of great material… and all I have to add is that all of the Unity UI classes are available as open-source here:

https://bitbucket.org/Unity-Technologies/ui/src/2019.1/

This could give you more insight into how RectTransform and such coordinates are used.

1 Like

Hi all, thanks for the replies, really appreciate the resources and explanations!

I kind of solved my problem today - albeit in an unfortunately far hack-ier method than I would like. I discovered that @Antistone 's comment about rect.size being useful data doesn’t seem to apply to my scenario, so I thought I should elaborate a bit more about my scene.

What’s happening is that a player presses a button which spawns a window from a prefab via Instantiate.

The window is also a mess of layout groups and content size fitters. When the window is spawned, a function is called on the instantiated object giving it a list of stuff.

It takes this list and instantiates even more prefabs parented to one of its children - the aforementioned Parent2. Each of these children is a prefab, let’s call it ‘WindowDetails’, with a structure that looks like:

  • WindowDetails - VerticalLayoutGroup, ContentSizeFitter

  • HeaderObject - unchanging size

  • ContentObject - Text/Image component, ContentSizeFitter

The ContentObject gets assigned either some text or an image that should change the size of it, which changes the size of WindowDetails, which should change the size of Parent2 as well, and then all the WindowDetails objects should get laid out according to their new sizes based on Parent2’s VerticalLayoutGroup.

In my scene, the values in the RectTransforms of these WindowDetails objects remain at some random, uniform value (usually around but not the same as the prefab values) for a few frames, and then get updated to the correct values, typically after 3-4 update cycles.

After that, checking anchoredPosition returns the correct values.

I suppose this is because Unity’s layout stuff only updates every once in awhile, and not every frame? Although I would expected that calling Canvas.ForceUpdateCanvases() would, well, force the canvases to update.

I ended up solving my issue by iterating through all the children and only doing what I need to do once it seems like the values have been updated (by checking that the difference in anchoredPosition isn’t uniform for every child), which is a pretty crap way of solving it but I can’t think of anything better.

Hm…my guess would be that it updates every frame, but only once per frame, and it takes a couple of iterations for all of your auto-layout components to react to changes in all the other components and reach an equilibrium?

Or maybe you are loading some assets asynchronously, and the content size fitters can’t size to them until they finish loading?

If you get really sick of these issues you can always do ALL of the layout calculations yourself (skipping layout groups and content size fitters and such), but depending on the complexity of your layout that might be a significant amount of work.

I don’t believe any of us are doing async asset loading since it is a fairly simple project. I’ll probably just stick with this hacky method until we see it break, since the project is just a small proof-of-concept prototype.

You might find the printouts interesting, here’s a pastebin: Attempting verification, attempt 0 Child - Object 0 Child position: (218. - Pastebin.com

Each ‘Attempting verification’ line and the subsequent child printouts is called from the Update function, and prints out a few RectTransform details from the children (WindowDetails) objects.

They start printing the frame after all the prefabs are spawned and the various values are assigned. It seems like it does a little bit of the layout calculations each frame, and takes on average 3-5 frames to fully finish calculating.

Pretty weird behaviour.