Unity 知识点 关于ForceRebuildLayoutImmediate的坑

萧熊猫先生于一个月前辞职了,为了完成心中的梦想(做个游戏),开始每天在房间里努力做着自己的游戏。(其实每天都在玩)

今天主要讲两个问题。众所周知,策划都是智障。(等等,我现在也算半个策划了……)​经常会有在自适应的layout里嵌套layout,然后在子级的文本和后面的图还要自适应,以及更多的自适应的情况……
在这里插入图片描述
在这里插入图片描述
如果是自己算正确的位置,显然不科学 ,费脑子不说,效率很低,debug过程会让人崩溃。unity官方想到了这个情况,所以提供了LayoutRebuilder.ForceRebuildLayoutImmediate(RectTransform)方法。可以让强制重新布局。
在这里插入图片描述

不过unity是个铁憨憨啊,有的方法真的只是够用就行,就像5.0之后就再也没有重搞过编辑器工具一样……(美其名曰,重做这玩意有啥用,够用就行了)
这个方法真的就只是重新计算子组件的位置。并不会触发子组件内部的重新布局。

所以,要想对付这样的自适应里的自适​应,两种解决办法。
一个是每个需要重新布局的组件都执行一下方法,这样的话,有点繁琐了。

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

另一个是对这个组件递归查找,所有带有LayoutGroup的组件都执行一遍重新布局,不过这就对消耗有点要求了。(毕竟GetComponent的消耗也不怎么好)

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;
}

当你搞完了这些,想试一下,发现还是没刷。(博主是不是骗人,直接埋了吧)​
……
这就涉及到第二个问题了,如果在你的组件的顶点有变化时就重新布局,可能不会起效。
以下为猜想:组件的content size fitter是在同一帧重新计算的顶点,而​本来应该触发的rebuild是在同样的下一帧。这时候显示不正确,但是rebuild认为逻辑上已经正确了。(没有找到文献或讲解,纯属个人猜想,如果有知情人,希望能告诉我,让我在这个问题上死个明白……)

这个的解决方法更加简单。也是两个。
第一个,有个前提,项目中有计时器管理器,延迟个一帧再执行就好了。
这个是我项目里的计时器,功能上是第二帧才会执行。

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

如果没有,那就用协程WaitForEndOfFrame或者在Update里​,确保在再下一帧执行就好了。
Update的方法在这里,不是万能,只是一种解决思路。

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

以及协程的解决思路

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

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

以上为我今天花了三个小时才定位又解决的问题(其中包括扩展计时器和硬造出一个协程管理器),真是耽误我去补庆余年。希望大家看到这个坑,能绕着走。

新年快乐~

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值