Cocos Creator 性能调优优化集锦

COCOS

共 6270字,需浏览 13分钟

 ·

2021-04-16 10:06


01

为什么要做性能优化?

性能:是一种优秀的能力。唤醒快、运行持久、稳定。

这种能力在游戏上能让你的用户感觉很爽,表征表现为加载快、手机不发热、运行流畅、不卡顿。

所以,性能优化的终极目标是让你的用户感觉很爽。当然这种爽不能以牺牲自己为代价,要考虑成本和副作用。

我们要优化性能,首先要搞明白哪些因素会影响性能?是体力不行就得加强锻炼,是操劳过度就应该学会休息。

在我们的游戏中,哪些因素会影响性能呢?


02

影响性能的因素有哪些?


如果大家不知道如何分析,那我给大家引导下。

当你玩游戏的时候,首先是不是要将你的游戏加载到内存。前戏太长,你肯定会等得不耐烦吧,不爽,就要优化。

那么第一个问题来了,如何优化游戏加载速度(1)。我们先记录下来,下面逐一讲解。

其次,你好不容易把东西放内存里去了,但是屏幕没任何东西,也不给你发出点声音。这体验不好!也就是说屏幕渲染游戏界面耗时太长、卡顿,需要优化。所以,第二个问题,如何优化渲染速度(2)?

最后游戏运行过程中,运行速度受什么影响?与设备内存、CPU、代码有关。所以我们要做内存优化(3)、CPU占用及性能优化(4)。


03

性能优化从哪些方面着手?


1.如何优化游戏加载速度【加载优化】 问大家一个问题,你一个 8M 的东西和人家一个 1M 的东西,你说谁会先加载进去?肯定是 1M 的,所以首先要想方设法优化包体大小。

【优化包体大小】的方法有哪些?

首先想下你的包体里面有啥?哪些东西占用的内存最大,哪些东西压缩的可能性最大?项目路径下有 res、scenes、scripts,还有游戏引擎等。

简言之,包体的组成分为:资源和代码。

(1)资源:图片、声音、动画等。

避免大尺寸的图片出现:纯色图片或有规律的图片用一像素的图片表示,带圆角的按钮背景图片用九宫格形式展示。

另外有一点也要格外注意,Cocos Creator 所支持的最大图片尺寸为 2048*2048,超过这个尺寸的图片在显示时会有问题,常见于一些 Spine 动画打包出来后没注意资源图片尺寸,导致动画显示异常。

如果是地图资源超过 2048*2048,常见于一些 mmo 项目,这种情况下,需要对地图资源进行切分,切分成小于 2048*2048 的若干图片,在游戏中再拼接在一起。

图片压缩:

图片的压缩格式:背景,jpg 体积要小于 png,背景图片 jpg,很多图片格式,导出美术图后,这些图,仍然可以压缩。百度搜索在线压缩图片,即可找到相应网站直接操作。https://www.yasuotu.com/

图片分辨率:1920x1080 > 960x540,减少分辨率;降低了清晰度的;尽可能用九宫格的图片,来代替一张整图的按钮 200x100, 300x100;大大地降低的图片的分斌率,这样就可以节省资源;



图片资源的模块化:



类似这样,将各个界面的美术资源、帧动画分类并且打包成图集也是比较好的处理方法,将资源进行模块化。这样在加载时,以及游戏运行时,会有以下几点好处:

1.提升加载速度

省去了多次打开 / 关闭文件所带来的时间损耗

2.减少文件的体积

多张图片合并到一起,在包体上面会有一定的优化

3.减少 DrawCall

使用时,由于这些美术资源都是一起配合使用的。因此放在一张图集中,可以减少渲染的 DrawCall 数量,对渲染的性能也有优化的作用。

同时在不需要使用这些资源时,比如说某个界面不需要再显示时,可以将这个界面的资源统一释放,避免占用内存。

另外通用资源可以统一打包在一张图集中,让这些通用的常用资源常驻与内存,方便使用。避免频繁的重复释放和加载。

同样,声音也可以采用相应方法处理。音乐音效资源(压缩格式背景音乐的大小,音乐的数量,声道,采样率https://www.aconvert.com/cn/audio/



如下是压缩后的音乐文件和压缩前的音乐文件大小对比,缩小为压缩前的十分之一了。



字库:


尽可能的不要自己带字库。

(1)特效文字,尽量使用位图字体,几个字母 + 图片,体积远远小于一个完整的字库,性能还要好,数字、界面的文字,例如我们的《极速赛车》中就是采用这种方案!bmpfont  (.png + .fnt 文件);

(2)尽可能的使用系统字库;

(3)字库可以压缩 fontmin;特定的数量固定的文字;

(4)位图字与矢量字,哪个性能更好? ? ? 位图字:速度快,但是,内存大;矢量字:速度慢,但是内存小;

【预制体】:

首先先说一下 prefab 在使用时的步骤:从文件中读取数据 → 反序列化数据 → 还原得到 Prefab 节点树 → 预处理 → 实例化。Prefab 这块的加载优化主要集中在两个地方:一个是 load 加载耗时优化,另一个是实例化耗时优化。

①合理拆分 Prefab

越大的 Prefab 文件在加载过程中的耗时是越长的,而且通常不是等比,而是以类似平方曲线去增加时长。

例如读取一个 100kb 的文件,可能耗时也就 10 毫秒,但对于一个 1M 或者是 2M 的文件,我们在加载时就不是 100 毫秒,可能就是几百毫秒。

类似这样七八百 kb 的 prefab 文件,我们就要去思考一下,是不是里面的节点都必须做成一个 Prefab?是否可以拆分成 2 个以上的 Prefab,通过拼接的方式组合?

一个 Prefab 我们可以将它看作为一个功能模块,而功能模块并不是越大越好,而是功能职责越单一越好。遵循这个原则,我们可以对 Prefab 做更好的拆分。

记住:职责单一原则,逻辑清晰,解耦,便于后期维护。加载快!!

②延迟加载资源

在场景处可以找到。在 Creator 的资源管理器中点击编辑好的 Prefab 资源,在属性检查器中我们可以看到延迟加载资源的选项。勾选这个选项可以减少 Prefab 的加载耗时,但首次显示的耗时会增加。

这是由于勾选后,Prefab 所引用的资源,像图片、音效这些,不会在 load 时加载,而是在 Prefab 第一次显示时再进行资源加载。因此需要根据具体的使用环境进行选择。

③选择优化策略

在 Prefab 的属性检查器中,我们可以看到优化策略这个选项。这个也需要我们根据实际的使用情况进行选择。

当我们选择“优化多次创建性能”这个选项时,Prefab 加载后会进行一个预处理的操作,这个预处理其实就是动态生成一些 Prefab 的实例化代码,并把这些代码交给 jit 去进行优化。这样在实例化时的耗时将会大大减少,相应的,在 load 时的耗时会有所增加。

当我们选择“优化单次创建性能”这个选项时,Prefab 加载后会跳过预处理的步骤,这样在加载时的耗时会减少很多,但实例化时的耗时会增加。例如一些固定 UI 界面,用于方便加载场景或者进行功能划分,通常会做成 Prefab,这种 Prefab 只会加载一次的,就可以选择这个选项,提升加载的性能。

 需要注意的有一点:由于微信小游戏平台禁用了动态加载代码,类似 eval 这些不能使用,因此优化策略这个选项在微信小游戏平台是无效的。

(2)代码:代码体积(引擎+业务逻辑代码大头在引擎。

解决方案:

引擎:非常简单,你只要把不要的模块去掉就可以了,你要知道哪些模块是占体积多的。物理引擎,能不用的模块,就不用。【项目设置】-【模块设置】,只打包必要模块即可。

能不用 tilemap 就不用,因为 Cocos 中有足够优秀的 2D 编辑器,可以替代 tilemap,能用碰撞检测引擎就不用物理引擎。

可以通过项目设置,去掉游戏中没有使用的功能模块,减少包体大小。如下图,将不需要的模块去掉,再打包。

业务逻辑代码一般我们也没法修改,你的业务逻辑差不多,但是,要注意一个 95% 以上的同学,都会忽略的一个事实:src 文件夹内的 settings 文件。

其实 settings 文件大小是可以修改的。首先大家要搞明白它的大小是由谁决定的?如果你不搞清楚这个问题,你就没办法优化,即使别人告诉你这么做,你也会纳闷。

所有需要代码加载的资源放到 resources,否则坚决不放到 resources 文件夹内。因为程序不知道哪些资源需要加载,也不知道你什么时候加载,所以会一股脑在 settings 文件中建立资源的映射。

如下图所示,将资源放在 res 中和将资源放在 resources 文件夹下,settings 文件的大小对比。注意:需要清空 build 再打包。

2.如何优化渲染速度【渲染优化】

渲染方面优化主要集中在如何降低 draw call 上,draw call 越多,渲染的压力也就越大,对应的帧率可能就会下降,正常情况下如果 draw call 超过 100 就有可能带来卡顿,所以要注意这方面的优化。

draw call:游戏场景里面物体,分几次提交给显卡绘制,这个次数就是等于 drawall 次数。100 个物体,100 次提交给 GPU,就是 100 个 draw call。

draw call过高为什么会影响性能?

GPU:每次绘制我们的图像, 一次能吞吐一定数目的三角形的,如果你的 draw call 过高,每次绘制的时候,GPU 本来可以一次吃更多的三角形,但是你没有让我吃饱,GPU 性能没有发挥出来。

CPU:10 个精灵,10 张图片;每次渲染管道里面再绘制的时候,只能带一张纹理, 每个精灵的纹理对象是不一样的, 所以无法合批。到底什么样的能在一个 draw call 里面绘制呢?

mesh 相同 -- 纹理相同,shader 要相同,参数要相同,draw call 合批 绘制; 比如,10 个物体:sprite1, sprite2, sprite3, label, 9 宫格 sprite, sprite; 推荐方法:

【1】合并渲染批次,降低 DrawCall,提升渲染性能

(1)使用自动图集或使用 TexturePacker 对碎图进行打包处理:只有图集内的精灵才有可能在一个 draw call 绘制 → 合批。

这样操作的话,可以让多个 Sprite 渲染的纹理都是同一张图集图片,合并这些 sprite 的渲染批次,就可以减少 DrawCall 以及 CPU 的运算开销。

(2)合批的时候,尽可能不要打乱了合批:例如上面 label 的出现打乱了 sprite 的结构,就不能合批。

(3)为什么 label 会单独做一个 draw call?文字会绘制在纹理中。

(4)资源处理,减少 Mask 组件数量:由于 Mask 组件需要在 stencil 和 content 前后都添加修改 gl 状态的 render command,因此使用 Mask 会打断我们的 DrawCall 批处理。

对于一些特殊的显示,例如圆角的 icon 等,如果条件允许,尽量不要使用 Mask 组件来进行处理,而是通过对资源进行处理达到同样的效果。目前 Mask 组件、Spine 组件、DragonBone 组件都会打断批处理,在节点结构上我们要避免被打断的情况发生。


(5)复用节点,减少节点数:当显示或隐藏这个界面时,大量的节点会带来大量的 enable 和 disable 的开销。比如好友排行,假设有 1000 名好友,没必要设置 1000 个节点,设置一页显示的节点即可,之后更换这些节点的显示内容。



最快的办法就是合并碎图成图集,然后同一图集的按照顺序摆放节点,中间不能插入其他图集的节点。

3.内存优化

静态资源的内存管理:静态资源指的是场景中直接或间接引用到的所有资源(脚本动态加载的资源不算在内)。在场景资源的属性编辑器中可以勾选“自动释放资源”选项,从而在切换场景时,会自动将旧场景使用的静态资源释放掉,从而节省内存的占用。


动态资源的内存管理:

动态资源统一使用 cc.loader 进行资源的加载以及管理。参考:动态加载。

要注意的一点是,Cocos Creator 中通过 cc.loader 去加载资源的所有方法,都是异步的。所以需要在回调中,确认加载完成后才能使用资源。

也可以通过 cc.loader.getRes 这个 API 去同步获取资源,但需要对 get 到的资源进行检查,如果没有加载或者没有加载完成,则需要等待或者通过 cc.loader 进行加载。这样的话整个代码会清晰一些,避免掉入 JS 的回调地狱中。

例如:

  1. _loadRes = function(url, type, callback){

  2. cc.loader.loadRes(url, type, function(){

  3. if(!err){

  4. callback(prefab);

  5. }

  6. });

  7. }

  8. Cc.loader.getRes

  9. _createPrefab(url){

  10. var prefab = cc.loader.getRes(url, cc.Prefab);

  11. If(prefab !== null&& typeof(prefab) !== "undefined"){

  12. return cc.instantiate(prefab);

  13. }

  14. returnnull;

  15. }

通过简单的封装两个方法,在使用时可以保持代码的整洁易读。另外一点需要注意的是,当批量进行加载时,cc.loader 也提供了 onProgress 回调,这个回调中的三个参数中。

totalCount 并不是指的是加载的资源总个数,而是加载这个资源所需要加载的依赖项个数。比如加载一个 SpriteFrame,它的 totalCount 就是 3,这 3 个 item 分别为:json、texture2D 和 SpriteFrame。所以当 totalCount 为 0 时,并不是代表加载资源总个数为 0,而是意味着这些资源已经加载过在内存中了,可以直接使用。

复用一切可复用的对象

最后是内存使用的一个理念:复用一切可复用的对象。复用,并不仅仅是为了节省对象在 alloc 造成的开销,更重要的是避免 GC 时带来的额外开销。

像一些战斗中的掉血数字、敌人的血槽、怪物、子弹、英雄头像等等,都是我们常常回去做复用的地方。对于常见一些复杂对象,我们可以使用对象池 NodePool 进行复用 对于基础的对象我们可以直接进行赋值从而达到复用的目的。

4.CPU 及性能优化

1、绝对避免游戏中出现死循环。

2、控制游戏帧率。

3、H5 游戏,JS 代码级别优化


  1. for(var i=0,len=arr.length; i<len; i++){


  2. }


4.JS 异常捕获


  1. Try_catch

  2. 一单抛出异常,效率就会直线下降。尽量避免在for循环中try


5.全局变量的使用要慎重!!

6、优化节点树,减少节点数量。

7、场景中不要挂载过多的 Prefab,可适当将一些 Prefab 变成动态加载的。

04

性能优化应注意什么?


效率、成本。

不要花百分之九十的时间、成本去尝试获取百分之一的性能提升。很多时候,需要两害相权取其轻。



往期精彩


浏览 113
1点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
1点赞
评论
收藏
分享

手机扫一扫分享

举报