优化uGUI中的SetParent的调用开销

概述

当前项目是一个类似scratch那种积木式的编程工具,编辑器用Unity的uGUI实现,但是对于大型的的工程(600多个block,每个block大概有5、6个GameObject),原有的实现在加载工程时很慢,profiler中查看,发现有很大一部分时间花在了RectTransform.SetParent调用上,这几天花了点时间,优化了这部分调用。

原因

RectTransform.SetParent调用,会触发许多其他的调动,比如Graphic.OnBeforeTranformParentChange,由于工程比较大,场景中有大量的GameObjects,许多SetParent调用导致了加载性能的降低。

原有的工程加载步骤为

  1. 实例化所有GameObjects
  2. Load保存的数据
  3. 恢复GameObjects之间的引用,此时会调用SetParent重新恢复父子关系

由于最初序列化数据结构设计的问题,无法在实例化时知道parent是谁,所以导致Instantiate调用时不能传入parent参数来减少不必要的开销。

解决方法

想到的一个方法是不依赖于Transform的父子关系,而是手动计算所有的坐标,这样我们在实例化后就不需要调用SetParent,就不会有UI的开销了。

为了保持当前代码基本结构不变,即使用本地坐标的地方可以依然指定本地坐标,引入了一个逻辑坐标系的概念(LogicTransform)。逻辑坐标系的层级结构同原来使用unity的Transform建立的层级结构,主要的区别在与我们自己计算整个层级中的坐标,而不是unity代劳。加上我们的ui不需要旋转,只涉及缩放和平移,实际计算代码很简单。下面是一个GameObjects的层级,其中黑色箭头组成的逻辑层级,红色箭头组成的unity的Transform的层级。

LogicHierarchy and UnityHierarchy

每个LogicTransform有一个对应的Transform,两者之间满足如下关系

  1. LogicTransform.localScale == Transform.localScale
  2. Transform在逻辑层级中本地坐标(非localPositon),等于LogicTransform.worldPosition

采用这种实现后,工程加载的时间从最初的30s降低到了15s(依然很慢)。

优化ReapplyDrivenProperties

Profiler中还发现,RectTransform.reapplyDrivenProperties也很费时,查看了uGUI的源码发现,这个部分开销来源于LayoutRebuilder在该事件处理中,调用了MarkForLayoutRebuild所致。由于我们的block布局完全是手动的,所以这部分开销也可以省掉。但是LayoutRebuilder的事件处理是私有的,所幸可以通过反射将事件处理函数替换掉。替换后的逻辑如下

static void ReapplyDrivenProperties(RectTransform driven)
{
	if (driven != null && ((1 << driven.gameObject.layer) & layersToIgnore != 0))
	{
		return;
	}
	else
	{
		// call the original event handler in LayoutRebuilder
		originalHandler(driven)
	}
}

这里将不需要自动layout的game objects分配到特殊的layer上避免开销。这步优化带来的提升不是很明显,大概节省了1.5s左右的时间。

优化SetActive调用

有些元素的显示和隐藏通过SetActive来完成。由于每次调用会触发OnEnable/OnDisable,在game objects相对较多的情况下,开销也不小,所以后来使用嵌套Canvas的方法来解决。改动后,加载速度有所提升。

结论

  1. 大量调用RectTransform.SetParent对性能有负面影响
  2. 避免不必要的SetActive,使用嵌套Canvas也可以提升部分性能
  3. 设计良好的Save数据结构应该可以解决重新SetParent带来的开销
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值