Unity knowledge points about the pit of ForceRebuildLayoutImmediate

Mr. Panda Xiao resigned a month ago. In order to fulfill his dream (making a game), he started working hard on his own game in his room every day. (actually playing every day)

Today I will mainly talk about two issues. As we all know, planning is mentally handicapped. (Wait, I'm half a planner now...) ​There are often layouts nested in adaptive layouts, and then the sub-level text and subsequent images are also adaptive, and more adaptive In this situation...
insert image description here
​Ifinsert image description here
you calculate the correct position by yourself, it is obviously unscientific, it takes a lot of brains, the efficiency is very low, and the debugging process will make people crash. Unity officially thought of this situation, so it provides the LayoutRebuilder.ForceRebuildLayoutImmediate(RectTransform) method. It is possible to force a re-layout.
insert image description here

But Unity is a tough guy, some methods are really just enough, just like the editor tool has never been redone after 5.0... Just use it)
This method really just recalculates the position of the child component. Does not trigger a re-layout inside the child component.

Therefore, in order to deal with such self-adaptation in self-adaptation, there are two solutions.
One is to execute a method for each component that needs to be re-layouted. In this case, it is a bit cumbersome.

public void Test()
{
    
    
    LayoutRebuilder.ForceRebuildLayoutImmediate(layout1.GetComponent<RectTransform>());
    LayoutRebuilder.ForceRebuildLayoutImmediate(layout2.GetComponent<RectTransform>());
    LayoutRebuilder.ForceRebuildLayoutImmediate(layout3.GetComponent<RectTransform>());
}

The other is to recursively search for this component, and all components with LayoutGroup perform a re-layout, but this requires a bit of consumption. (After all, the consumption of GetComponent is not very good)

public void Test()
{
    
    
    List<Transform> transList = GetAllChildsByComponent(root.transform);
    foreach (Transform trans in transList)
    {
    
    
        RectTransform rectTrans = root.GetComponent<RectTransform>();
        if (null != rectTrans)
        {
    
    
            LayoutRebuilder.ForceRebuildLayoutImmediate(rectTrans);
        }
    }
}

public static List<Transform> GetAllChildsByComponent(Transform trans, List<Transform> transList = null)
{
    
    
    if (null == transList)
    {
    
    
        transList = new List<Transform>();
    }
    if (null != trans)
    {
    
    
        for (int i = 0; i < trans.childCount; i++)
        {
    
    
            Transform child = trans.GetChild(i);
            if (null != child.GetComponent<LayoutGroup>())
            {
    
    
                transList.Add(child);
            }
            GetAllChildsByComponent(child, transList);
        }
    }
    return transList;
}

When you are done with these, you want to try it, but you still haven't brushed it. (Is the blogger lying, just bury it)
...
This involves the second problem. If you re-layout when the vertices of your components change, it may not work.
The following is a conjecture: the content size fitter of the component is the vertex recalculated in the same frame, and the rebuild that should have been triggered is in the same next frame. At this time, the display is incorrect, but rebuild thinks it is logically correct. (I didn’t find literature or explanations, it’s purely personal conjecture, if anyone knows, I hope you can tell me, so that I can understand this issue...)

The solution to this is simpler. Also two.
The first one, there is a premise that there is a timer manager in the project, just delay it by one frame and then execute it.
This is the timer in my project, functionally it will be executed in the second frame.

public void Test()
{
    
    
    Timer timer = new Timer("Test");
    if (null != timer)
    {
    
    
        timer.SetDelayer(0.01, () =>
        {
    
    
            LayoutRebuilder.ForceRebuildLayoutImmediate(layout.GetComponent<RectTransform>());
        });
    }
}

If not, then use the coroutine WaitForEndOfFrame or in Update to ensure that it is executed in the next frame.
The Update method here is not a panacea, but a solution.

void Update()
 {
    
    
    if (null != layout)
    {
    
    
        if (!isRun)
        {
    
    
            isRun = !isRun;
        }
        else
        {
    
    
            LayoutRebuilder.ForceRebuildLayoutImmediate(layout.GetComponent<RectTransform>());
            isRun = false;
            layout = null;
        }
    }
}

And the solution of the coroutine

public void Test()
{
    
    
    StartCoroutine(TestEnumerator(layout.GetComponent<RectTransform>()));
}

private IEnumerator TestEnumerator(RectTransform rectTrans)
{
    
    
    yield return new WaitForEndOfFrame();
    LayoutRebuilder.ForceRebuildLayoutImmediate(rectTrans);
}

The above is the problem that I spent three hours locating and solving today (including extending the timer and forging a coroutine manager), which really delayed me to celebrate the rest of the year. I hope everyone can see this pit and walk around it.

happy new year~

Guess you like

Origin blog.csdn.net/qql7267/article/details/104026135