10

NGUI研究院之为什么打开界面太慢(十三)

 3 years ago
source link: https://www.xuanyusong.com/archives/2799
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

NGUI研究院之为什么打开界面太慢(十三)

NGUI打开界面太慢了,起初一直以为是unity的问题,最近经过我的全面测试我发现这和unity没有关系。一般一个比较复杂的界面大概需要150个GameObject  或者 UISprite 。我用NGUI直接载入发现竟然需要250多毫秒,仅仅只是两张小图。同样的GameObject 我用unity2d的Sprite载入只需要70多毫米,可见Unity2d的效率要比NGUI高多少。。我可能说的不完全对,因为U3D是闭源的,我只能猜测。

在普及一下基础知识。

GameObject go = GameObject.Instantiate(Resources.Load(“prefabName”)) as GameObject;
go.AddComponent<Test>();

 我想上面这一行代码,大家应该再也熟悉不过了。实例化一个Prefab,然后给它身上帮一条脚本。如下图所示,我用的是 红米作为测试机器。实例化对象我们可以拆成4部分。我用NGUI加载了150个UISpirte = 41 + 4 + 30 + 194 = 269毫秒,一般打开界面超过500毫秒的话用户就会明显感觉卡顿了。而我这个仅仅是一张图而已,事实证明NGUI加载太慢了,到底为什么这么慢?

NGUI研究院之为什么打开界面太慢(十三) - 雨松MOMO程序研究院 - 1

1.Resources.Load

Resources.Load是一个”同步”耗时操作,Unity内部维护了资源的内存池,但是调用Load的时候Unity会自动的把Prefab上所引用的资源在加入内存池,它不会重复加载资源。也就是说当你加载相同的UIAtlas的时候,只会第一次比较卡。你可以试试一些线上的unity游戏,一般第一次打开某界面的时候要比以后打开此界面时间长一些。

2.GameObject.Instantiate

很多人认为加载慢的原因罪魁祸首是Instantiate()。其实我告诉你它的时间反而是最快的,上面的截图我相信就是最好的证明。第一次Instantiate要比以后执行Instantiate要慢一些,可能Unity在做一些特殊处理吧。

3.第一次添加脚本。

添加脚本一般会有两种形式,第一种是通过AddComponent<Script>的形式把脚本添加给游戏对象,还有一种是你的Prefab天生就带着这个脚本。无论哪种加载时间都是一样的。第一次加载脚本要比以后加载慢,我觉得应该是和Resources缓存池的原理一样吧。

4.第二次以后的GameObject.Instantiate 和 AddComponent<Script>

GameObject.Instantiate 就不用说了,它载入很快,这里要详细的说说Script。

对!导致于你界面打开慢的原因就是prefab上绑的脚本,罪魁祸首就是脚本。

AddComponent<Script> 以后 或者  Prefab上预先绑定的脚本。当你GameObject.Instantiate()同步方法执行的时候,并不是把脚本挂上去就完了,而它要等脚本里面的一些方法执行完毕才算结束。

脚本中有两个很典型的方法 Awake 和 OnEnable。当Prefab 用Instantiate()方法载入的时候,它的脚本必须执行完Awake和OnEnable两个方法以后才算完整载入。那么如果你的脚本这里面有一些耗时操作,那么必然载入会慢了。。

void Awake()
for(int i =0 ;i <1000; i++)
void OnEnable()
for(int i =0 ;i <1000; i++)

如果你在Awake() 或者 OnEnable()方法里面继续去实例化对象,继续绑定脚本,那么依然还需要把新实例化对象的 Awake()和 OnEnable()方法执行完毕才会结束。。。

这里并没有完,还有一个地方也会引起打开界面慢。代码中用Pubilc 声明的对象,然后是在编辑器拖拽赋值。

public GameObject go;

拖拽赋值,如果是资源很大的话unity需要load ,然而load就是一个同步耗时操作,那么它也会影响打开界面的时间。

如下图所示,NGUI里面 UISprite UITexture UILabe 这三个脚本上面都有 public 绑定的对象。 NGUI打开界面慢的罪魁祸首就在这里,我尝试把public 绑定的代码全部取消, 发现 20几毫秒 就载入完成了。。。 知道原因了,但是我们也没办法,因为不能随便乱改它的代码。。 

一定要把一个界面的所有GameObject做成一个Prefab,有些人不想用unity的Prefab,想通过一种规则程序运行时利用GameObject.Instantiate()  和 AddComponent<Script>  来生成界面的树状结构。我做过测试如果单纯加载一个Prefab和 代码动态生成对应树状结构 前者要比后者快30%左右。所以如果做UI编辑器的话,一定要先把Prefab生成出来,一定要只加载一个Prefab。

至于Unity 的Sprite载入 为什么要比NGUI的Sprite载入快,那么唯一可以解释的就是Unity可能后台用的是C语言,而NGUI用的是纯C#,从执行效率上C会快很多,所以我们还是早日期待Unity可以自身完美的解决做界面的问题。unity4.6预览版看起来很赞,不过我更期待unity5的到来。

最后我们在说说怎么让NGUI打开界面的速度能快一些。

1.修改界面结构,尽可能让界面上绑定UISprite UITexture UILabe这样的游戏对象少一些。

2.如果界面没法拆开,那么就把界面的prefab拆成多个,比如底框是一个Prefab , 内容是一个Prefab ,列表是一个Prefab ,这样打开界面的时候用协同任务 一个一个打开,这样用户就不会感觉到界面卡顿了。。

3.期待您的补充。。

作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK