使Creator3.x引擎启动提速60%的亿点点总结|征稿活动V5

背景

一个采用 Cocos Creator 3.4.2 引擎开发的3D游戏,除了主游戏外,还有一个精简版游戏。精简版运行在小游戏平台,功能非常简单,所以要求启动速度一定要快。

首先在Creator项目设置中仅留下 WebGL 1.0基础3D功能基础2D功能用户界面运行状态统计 这几个模块,其它的全部去掉。此时 Reelase 构建后,引擎自身文件cc.js是 2M 大小,在iPhone7手机上,引擎启动耗时是 0.5秒,这对于一个超轻量的小游戏来说,还是不够轻快。 因此,对Creator引擎源码进行了全面的裁剪和优化,改造后的引擎包体大小是 1.1M,启动耗时为 0.2秒,基本达到了预期。

这里说的引擎启动耗时,是从小游戏入口 game.js 的第一行代码开始,到调用 application.js 中的 cc.director.loadScene(launchScene, ...) 为止。因为此时引擎已准备就绪,开始加载首场景了,而加载场景的耗时主要看资源量,如果场景复杂则耗时久,和引擎自身没多大关系。所以本文把 loadScene(launchScene) 定义为引擎启动结束。

要对Creator引擎裁剪优化,首先是自定义引擎。

一 自定义引擎

以 Mac 平台为例,从Creator右上角点击“编辑器”,即可进入引擎目录,进入 resources/3d,里面有 engine 和 engine-native 两个目录,因为我们是小游戏项目,则只需修改 engine 即可。把整个 engine 文件夹复制到另一个目录,然后在 Creator偏好设置 -> 引擎管理器 中把 自定义ts引擎路径 指向自己刚才复制的那个目录,把 使用内置ts引擎 的勾取消。另外还可以把文字前面那个黄色齿轮点亮,代表这个设置存储在项目内,而不是全局。

如下图所示。

提示:

  • 如果只修改引擎ts代码,无需自己编译,只要重启Creator编辑器,就会自动编译;

现在可以任意修改引擎源码,并在项目中生效了。

二 分析启动耗时

刚才说了,原始版本引擎的启动耗时约 500ms,那这 500ms 的时间都花在什么上面呢?先看看引擎启动过程中做了哪些事情,主要流程列出如下。

在每个关键处打印出时间,发现标黄色的这几处:import(‘cc.js’)initBuiltinRes_initMaterialsloadAssetBundle 耗时较大,其中加载引擎自身文件cc.js,耗时就超过 200ms ,看来引擎包体是影响启动速度的第一要事。所以首先要减小引擎包体,把不必要模块都删掉。

三 裁剪引擎源码

3.1 先粗删一遍

做这步之前,需要对自己项目的功能非常熟悉,以及对引擎的基本结构有个大致了解。之后操作就比较简单粗旷了,就是到引擎源码目录,把每个子目录和类大概看一遍,按类名搜索,觉得项目用不上的,就把该类和相关引用删掉。有些是整个文件可删除,有些是删除文件中的一部分,配合代码管理神器git的助力,删除出现问题也可随时回退。

这样一遍过后,引擎包体从 2M 降到了 1.3M。需要注意的是,每一个源码目录下都有个 index.ts 作为索引,删除一个模块后通常还要在 index.ts 中删除相应引用,否则会报错。

值得一提的是,3.x引擎构建时会同时包含glsl1glsl3两个模块,分别对应WebGL 1.0WebGL 2.0,这两模块每个都有 200K 大小,因为我的项目确定只需要WebGL 1.0就足够,所以直接把glsl3.ts及相关引用代码删除,这一个操作就减小了 200K。

其它方面,像动态合图、压缩纹理、延迟渲染、部分灯光、平面阴影、模型合批、graphics、bmfont、letterFont、scroll-view、slider、mouse、keyboard等模块都用不上,所以都是整个相关模块删除。

3.2 根据逻辑细删

粗删一遍后,接下来是根据耗时热点,分析代码逻辑进行细删。观察 initBuiltinRes_initMaterials 耗时较久,调试源码发现,引擎默认创建的有些 texture 和 shaer 是用不上的,例如:billboard、graphics、pariticle-gpu等等,所以继续到 effect.tsglsl1.ts 中删除它们的定义。

需要注意,effect.tsglsl1.ts 分别定义了 effect 数组和 shader 数组,这俩兄弟数组是一一对应的关系,所以删除时要在两个ts文件中同时删。这一个操作又减小了 200K,现在引擎包体大小是 1.1M。

3.3 精简wx适配库

最后把wx adapter中不用的模块也去掉,例如:xmldom、video、audio、EditBox等;

最终裁剪掉的模块和相关代码非常多,不再一一列举,所有的如下图所示。

对本次裁剪优化影响较大的几处已标出绿色显示。

四 优化启动逻辑

现在能删的代码都已经删除了,加载 cc.js 已经很快了,但 loadBundle 中加载 mainresources 两个 bundle 仍然耗时较长,原因是加载 JSON 文件 比较慢。

4.1 优化JSON读取速度

因为引擎默认采用了异步方式读取 .json 文件,其实 bundle 相关的 .json 很小,只需 1ms 就能读取完成,但因为用了异步,只能被动等待下一个周期返回,这样就至少需要 10~16ms。因此改成了同步方式,这一改动使整个启动至少加快了 50ms。

修改 platforms/minigame/platforms/wechat/wrapper/fs-utils.js 中的 readJson 代码如下:

readJson (filePath, onComplete) {
    // 在引擎启动过程中使用同步方式读取JSON,以加快启动速度。启动完成后恢复异步
    if (!window.stAfterScene) {
        Promise.resolve().then(function() {
            var result = fsUtils.readJsonSync(filePath);
            if (!(result instanceof Error)) {
                onComplete && onComplete(null, result);
            }
        });
    } else {
        fsUtils.readFile(filePath, 'utf8', function (err, text) {
            var out = null;
            if (!err) {
                try {
                    out = JSON.parse(text);
                }
                catch (e) {
                    console.warn(`Read json failed: path: ${filePath} message: ${e.message}`);
                    err = new Error(e.message);
                }
            }
            onComplete && onComplete(err, out);
        });
    }
}

如上所示,考虑到运行中加载的JSON文件不确定,也许会很大,所以仅针对启动过程做了提速,启动完成后恢复默认的异步。

其中 window.stAfterScene 是我自己定义的变量,在 application.jsonGameStarted 方法中进行定义,并没有太大作用,只是标记引擎启动是否结束。示例代码如下:

window.stBeforeScene = new Date().getTime();
cc.director.loadScene(launchScene, null, function () {
    window.stAfterScene = new Date().getTime();
    ....
}

4.2 优化图片读取速度

在引擎读取图片时,会先去检测文件是否存在,然后才是真正读取。因为这两个操作都是异步,所以读取一张图片至少需消耗两个异步时间。

为提升启动速度,我在这里去掉检测操作,改为直接读取,这样读取图片的耗时减少了50%。

修改 platforms/minigame/common/engine/AssetManager.js 中的 doNothing 方法,如下:

function doNothing(content, options, onComplete) {
    try {
        onComplete(null, content);
    } catch (err) {
        onComplete(new Error(`file ${content} does not exist!`));
    }
    // exists(content, (existence) => {
    //     if (existence) {
    //         onComplete(null, content);
    //     } else {
    //         onComplete(new Error(`file ${content} does not exist!`));
    //     }
    // });
}

五 总结

总结本次优化,是先跟踪启动流程,分析耗时热点,然后做引擎包体裁剪,再针对耗时原因做专门优化。对Cocos Creator 3.4.2引擎裁剪优化的最终成果,是引擎代码量从 2M 减小到 1.1M,iPhone7真机上,游戏引擎启动耗时从 0.5秒 降到 0.2秒,其中,加载引擎自身耗时约 0.15秒

对小项目来说,对引擎启动速度影响较大的是引擎自身的包体,毕竟Creator3.x是一个3D引擎,功能越强大代码量也越大。但是,游戏启动慢并不等同于引擎启动慢,这需要结合实际项目做具体分析。

其次是异步文件加载,在确信文件资源很小的情况下,改用同步会大大加快启动速度。

以上是对Creator引擎裁剪优化的一些总结,欢迎探讨。

更多文章请访问个人主页:https://www.chuyouxiang.com/

22赞

:grinning: 6666

1赞

感谢大佬的分享, 不知可否分享下您删改后的引擎的代码呢 ?

哈哈,+1

1赞

哈哈哈哈,极限操作

本来是可以,但因为引擎中加了一点上层业务相关的代码,我需要把这部分剔除掉才行,只能等后续看时间情况了 :joy:

如果实践中碰到问题可以随时找我一起探讨~

1赞

好的,感谢大佬的解答

+1 继续蹲

1赞

:+1: :+1: :+1:

既然不分享干货, 还逼逼

省流版阅读,裁剪引擎,自己优化耗时的地方

100天后,终于兑现了承诺,放出了源码,感兴趣的朋友请移步:分享:《如何让CocosCreator3.x引擎启动提速60%》源码

引擎启动失败啊

是怎么失败?方便的话请提供更详细的信息。

3.6.2的引擎,然后使用自定义路径为下载楼主的引擎。 重启的时候直接提示引擎启动失败

你的用法有两个问题:
1、裁剪版引擎 只适用于 3.4.2,不能用于3.6.2;
2、裁剪版引擎 只是ts部分,不包含原生模块;

好吧.那我再研究研究别的方法.不敢贸然降版本.

请问如果是web版,应该修改引擎的哪个目录?