我是靠谱客的博主 唠叨白开水,最近开发中收集的这篇文章主要介绍instantiate 卡顿严重_MeshBake——极大减少UI的Instantiate耗时,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

后来发现Mesh不能序列化,转换为向量数组后。然后再调了实机,将Loading的总时间调出来看了,结果如下:正常界面MeshBake

也就是负优化。

Loading.ReadObject变慢的原因是,将Mesh存入Prefab后导致Prefab变大,所以解析Prefab的序列化时间变长了。

Instantiate变慢的原因是。在执行Awake前,它需要先Copy。

最后把生成网格阶段节约下的时间都给浪费了。

不能保存Mesh只能存向量数组多出来的转换时间倒不多,本来速度还有至少2倍的优势的……

不过,Instantiate的Copy部分的消耗倒是可以将网格存成单独的文件类来回避。

所以,我后来又把生成的Mesh都单独放在了另一个自定义Asset文件里(不能把Graphic也传了,否则它指向的不是Instantiate之后的Grpahic),搞的很麻烦。编辑器里能运行,但是打包后显示:A script behaviour (script unknown or not yet loaded) has a different serialization layout when loading.这样的迷之错误,google了也有其他人问但没有任何解答,或者这就是Unity的序列化被人称作“没法用”的原因。

考虑到即使做好了也不会有多少改观,很可能还是负优化,所以就放弃了。

最后的源文件:

虽然这方法失败了,还是给点这个过程中得出的结论吧:

1.UI界面事先用异步的方式后台加载,然后显示时再执行Instantiate,还是可以减少一半的加载卡顿的。而且你还可以通过将asset.SetActive(false) ,再Instantiate的方式,将Instantiate.Produce,Instantiate.Copy预先执行(此时界面没有初始化也没有显示),最后在需要界面的时候再gameObject.SetActive(true),正式显示,就只需要承担Awake时建立网格的代价了。

IEnumerator PreLoading()

{

var request = Resources.LoadAsync("Canvas1");

yield return request;

//异步加载是基本不会影响主线程卡顿的,不管主线程有多繁忙。

//你只需要注意大纹理,但大纹理只有图集,通常早就加载好了。

//避免分界面的大纹理即可。DrawCall没那么重要。

GameObject asset = request.asset as GameObject;

asset.SetActive(false);

GameObject go = GameObject.Instantiate(asset);

//在disable下不完全的Instantiate,界面实际并没有初始化也不会显示。

//但这个过程是在主线程上执行的,会导致主线程卡顿,所以务必多物体分帧执行。

//大预制可分拆多份。

//实在处理不好,就只能延后到正式初始化那里一起卡了。但先LoadAsync还是会好很多。

}

go.SetActive(true);//需要时再正式初始化

这个异步加载,是在你界面空闲时候,预测你可能新打开的界面,提前加载的意思。并不是说打开的时候再执行这个流程,那样只会更慢。

当然你也可以选择在切换场景的等待时间内进行同步的预加载。有这么做的,但负责得讲,体验极差(从战斗回到UI界面的等待时间和进入战斗差不多)

2.将界面分成多预制加载。不需要的组件先disable,然后再逐步激活有一定缓解,但还是逃不掉Instantiate.Copy,彻底的分离还是得多预制。但如果用前者已经足够,也可以不用像后者这么麻烦。

3.文字描边也是个热点,少用,或者用Shader方式的描边,或者改写Text在内部实现描边。下面有写。但是由于前面的负担太重,文字这边的改善占比有限。

4.如果Unity能够提供功能,让Instantiate.Produce,Instantiate.Copy可以在子线程进行,可以大大缓解卡顿压力,实现上是容易的,但是需要改变现在的预制实例化逻辑,以他们的尿性估计没戏。预制复制这里不解决,他们怎么解决UI显示的加载问题都是杯水车薪,就如同我这次做的事一样。

—————————————————————————————————————————

以下原文:

测试场景

Before

After(都没开DeepPofiler,Profile干扰应该不大)

代码下载

原理很简单,在编辑期间获取UI生成出来的网格数据并保持下来,然后在运行期间代替正真的UI直接显示。

我最终没有将多个UI的网格合并,而是单独保存,然后禁用原UI组件,接管UI原本对应的CanvasRenderer。这样带来的以下几个好处:

1.不需要用编辑器脚本处理预制删掉原UI,随时可用,也用不着生成一堆仅用来多材质分批的CanvasRenderer了。

2.工作流简单。在Bake之后依然可以正常编辑界面,似乎Bake这件事并不存在。而且可以随时切换单个UI的Bake状态,也不需要改变原来的UI层级。

3.运行期间,可以随时低消耗地将组件变回原来的样子,恢复之前的一切功能。所以可以直接对整个界面做Bake,然后需要界面变化时,用代码解除相应UI组件的bake状态即可。

4.运行期间,即使不解除bake,也可以控制组件的位置缩放和显隐(显隐需要执行MeshBake组件的Render令其更新,可以指定更新这一个组件),理论上也可以通过扩展给予更多Bake期间的属性变化,比如变色。

5.代码变得很简单,很容易看懂。

核心代码

//利用反射让组件Rebuild一次

var methodInfo = graphic.GetType().GetMethod("UpdateGeometry", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

if (methodInfo != null)

methodInfo.Invoke(graphic, null);

//这时界面网格会临时存在于workerMesh,将它的内容复制走即可。

CopyTo(workerMesh,bake.mesh)

//最后显示的时候直接

renderer.SetMesh(bake.mesh);

用法:

将MeshBake.cs挂在窗体的根节点上。当然你也可以随便挂在其他方便的地方,支持嵌套。

MeshBake本身是个Graphic,所以你挂的节点至少不能有其他UI组件。

然后把里面需要Bake的UI组件enable设置为false。

然后就行了。

虽然界面看上去没变化,照样能编辑,但现在负责绘制的是MeshBake,这个被禁用的组件只是一个数据容器而已。

MeskBake会把Bake过的组件显示出来。另外你也可以点上面的SetAllImage按钮将子级的全部Image设置为disable,这样就都被收纳到Bake的范围里了。是否Bake的依据就是组件是否enable。

所以enable这个属性就不能用了,如果你希望隐藏组件,可以用graphic.canverRender.cull属性,也可以直接将gameObject禁用。

当然,运行期间未Bake的组件enable还是可以随便改的,和以前没区别。

运行期间,如果你将Bake的组件enable设置回true,它就不再受MeshBake的控制了,爱干嘛干嘛。

BatchAll按钮基本没卵用。Batch操作会在界面组件变化时执行,如果你嫌太卡,可以把相应代码注释了,用这个按钮手工更新。

这个做法最大的问题是,当图集变化时,网格当然不会跟着更新。所以你需要在打包前打开一次界面,或者手动执行界面内的MeshBake的BakeAll()。可以用打包脚本解决。

另外,Text当然也可以放在里面,但显然只能是静态文本。

不能处理动态文本挺亏的,因为文本其实才是性能热点。尤其是描边后的文本。可以看我这篇

后面提供的描边Shader和将描边内置到Text内的做法都能成倍提高文字初始化速度,前面那两功能可以无视。

当然,用的是静态文本就可以用这个MeshBake了。静态文本也不是完全不能考虑的吧?多语言改改我的代码,保存多份Mesh也能实现的。

MeshBake的绘制操作有一定消耗,它的Rebuild规则和Graphic一样,改这个组件宽高和enable,换父级的时候执行,平时基本不会激活的,需要的时候可以执行Render激活(也做了只更新选定组件的功能)。这个你们也可以改。

这东西刚做出来而已,没有经历过项目的实际考验。但是原理一看就明白,风险也就小,再考虑到其效能和使用成本……

看看吧。

不过不知道整个合并到一个Mesh会不会效能更高一点,虽然个人觉得没戏。还是说直接不用Canvas?

最后

以上就是唠叨白开水为你收集整理的instantiate 卡顿严重_MeshBake——极大减少UI的Instantiate耗时的全部内容,希望文章能够帮你解决instantiate 卡顿严重_MeshBake——极大减少UI的Instantiate耗时所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部