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);
}
以上为我今天花了三个小时才定位又解决的问题(其中包括扩展计时器和硬造出一个协程管理器),真是耽误我去补庆余年。希望大家看到这个坑,能绕着走。
新年快乐~
上一篇: 刚刚被破格提拔的经理亲戚
下一篇: 倾国倾城的赵飞燕,其实她身上有一个缺点