欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

解决 cocos2dx UIWidget 加载速度慢造成的游戏卡顿

程序员文章站 2022-06-17 22:00:46
...

我们游戏项目中的 ui 实现方式,采用的是用 cocostudio 编辑ui ,导出 exportjson 和大图的方式实现的. 这样实现一直都存在一个问题: 当一个页面用到较多的 ui模板时, 加载速度会非常慢. 比如点击一个 按钮,展示一个 由 7, 8 个ui模板组成的 页面时,要花费一

我们游戏项目中的 ui 实现方式,采用的是用 cocostudio 编辑ui ,导出 exportjson 和大图的方式实现的.

这样实现一直都存在一个问题: 当一个页面用到较多的 ui模板时, 加载速度会非常慢. 比如点击一个 按钮,展示一个 由 7, 8 个ui模板组成的 页面时,要花费一两秒的时间,体验非常不好.

导致问题的根本原因是 widgetFromJsonFile() 这个函数非常慢。

起初我怀疑是 io 的问题, 也就是说认为是这个函数里面调用 CCFileUtils::sharedFileUtils()->getFileData() 这个函数慢,但后来验证发现这个函数花费的时间非常少,

影响性能的是这个函数里面调用的 widgetFromJsonDictionary(widgetTree); 函数非常慢。这个函数是在根据 json 字符串解析出各个 ui控件。


为了解决卡顿的问题,我尝试了以下几个方法:

1. 相同 ui模板交替位置,实现无限循环。

比如我们有个页面,需要展示 几十个 相同 ui模板组成的 list view.这时可以计算 当前页面最多显示 n 个,在此基础上 +2,也就是一共调用 n+2 次 UIHelper::instance()->createWidgetFromJsonFile() 函数。

利用这个 n+2 个控件,当向下移动时,顶端控件挪下来,向上移动时下端控件挪上去,来实现几十个 ui模板的滚动。

这样的方法虽然节省了不少时间,但是即使仅读取 n+2 次 ui 模板,还是要花费1 ~ 2秒,效果不理想


2. ui 控件不从 json file 解析,而从内存中拷贝

我们项目的 cocos2dx 版本比较老, UIWidget 没有 clone() 函数。

起初我就根据 UIWidget* CCSGUIReader::widgetFromJsonDictionary(const rapidjson::Value& data) 函数里调用的各个子函数

setPropsForButtonFromJsonDictionary() setPropsForCheckBoxFromJsonDictionary() 等等函数,来按照这些函数写一些 属性赋值函数。但是写得很辛苦,而且复制效果也不好,许多不同的 UIWidget 类需要 不同的参数设置,很难保证正确性。

后来下载了 cocos2dx 2.2.5 版本, UIWidget 以及它的各个子类都实现了 clone() 函数。我把这些 clone() 函数移植到了我们项目里面。

但结果非常悲剧 : 用 UIWidget 的 clone() 竟然 比 每次读取 json file 的方法更慢! 并且在 2.2.5 版本上试验一番,也证明了 UIWidget 类的 clone() 函数效率低下。

所以从内存中拷贝这样的方法行不通。


3. 给游戏增加 loading 界面,在进入游戏前缓存足够的 UIWidget

为此写了一个 singleton 的类 UIPool ,在游戏开始时加载足够多的 所需要的 UIWidget 控件,修改 UIHelper::instance()->createWidgetFromJsonFile() ,当 UIPool里面有相应的空闲控件时,直接拿来用,否则再去 调用 read from jsonfile 相关函数。

起初以为这个UIPool 实现起来, 对缓存的 UIWidget 的内存控制会比较麻烦,但是写了代码后发现,采用 CCDictionary 里面存储 n个 CCArray ,每个 CCArray 存储控件实例的方式,实现起来非常简单。并且 CCDictionary 和 CCArray 都可以使用 cocos2dx 的内存管理机制,实现起来很方便。


这样,使用方法3 和 方法1 相结合, 就可以消灭大部分游戏中由于 UIWidget 读取导致的的严重卡顿了。