小游戏中加载远程资源时的一次优化

问题:

小游戏的诸多限制对于大型项目或者一些原本未针对小游戏进行优化的中大型项目来说,由于资源及文件基本只能放cdn,第一次进入一个需要加载大量资源或者提前加载大量资源的场景,需要进行大量的下载,消耗极长的时间(我们原本的项目在未优化的情况下,第一次进入一个核心界面耗时1min以上)

解决问题思路:

  • 压缩资源(效果还挺好)

  • 优化项目,将需要加载的资源尽量的减少:

    a.使用延迟加载资源(效果明显,但非常影响体验)

    b.将场景中用到的资源,打一个图集(如果本身在其他图集中的,扣出来,也打到这个图集中),重新拖拽资源(对我们项目效果不错,就是麻烦)

    c.将项目中原本需要提前加载的资源改成不提前加载,需要使用的时候再加载(对我们项目效果不错,就是麻烦,变成异步的写法)

进一步的优化思路:

问题的核心的就是资源下载耗时,上一步中我们通过减少第一次的下载文件数量和文件大小来达到目的;现在我尝试将这些资源打包为一个zip文件,下载完成后再进行通过修改加载路径来进行本地加载;查看源码后在cocos2d/core/asset-manager/downloader中找到一个下载资源的统一入口download (id, url, type, options, onComplete);修改这里的代码达到修改文件加载路径的目的

  • 找到要加载的文件(这里的改动是临时的,仅仅为了我方便的获取所有资源url)

在提前加载资源或加载场景之前定义wx.xxx = [];

在上述的download的方法中(或者构建之后的引擎文件中的相对应的地方)加入:

            if(wx.xxx && url.indexOf('http://xxx.xxx.xxx/') == 0) {

                wx.xxx.push(url.replace('http://xxx.xxx.xxx/', ''));

            }

在所有加载完毕或场景的加载完成回调中就可以打印这个信息了;

保存为localAssets.js

module.exports = []

  • 将这些文件从远程包中查找出来,压缩为一个zip,放到远程目录下

我写了一个简易的node脚本执行此操作

const fs = require('fs');

const localAssets = require('./localAssets');

let root = __dirname.replace(/\\/g, '/') + '/remote';

let func = (dir)=>{

    let stat = fs.statSync(dir);

    if(stat.isDirectory()) {

        let d = fs.readdirSync(dir);

        d.forEach(element => {

            func(dir + '/' + element)

        });

    }

    else {

        let d = dir.replace(__dirname.replace(/\\/g, '/') + '/', '');

        if(localAssets.indexOf(d) != -1) {

            copy(dir, dir.replace('/remote/', '/localAssets/remote/'));

        }

    }

}

let n=0;

let copy = (filepath, newpath)=>{

    let arr = newpath.replace(__dirname.replace(/\\/g, '/') + '/', '').split('/');

    let gen = __dirname;

    for(let i=0;i<arr.length-1;i++) {

        gen += '/' + arr[i];

        if(!fs.existsSync(gen)) {

            fs.mkdirSync(gen);

        }

    } 

    fs.copyFileSync(filepath, newpath);

    n++;

    console.log(n);

}

let package = __dirname.replace(/\\/g, '/') + '/localAssets';

if(!fs.existsSync(package)) {

    fs.mkdirSync(package);

}

func(root);
  • 修改源码中的download方法,添加如下代码

try {
if(wx && wx.localAssets && wx.remoteUrl && url.indexOf(wx.remoteUrl) == 0) {

            for(let i=0;i<wx.localAssets.length;i++) {

                  if(url.indexOf(wx.localAssets[i]) != -1) {

                    url = `${wx.env.USER_DATA_PATH}/` + 'localAssets/' + wx.localAssets[i]; 

                    break;

                  }

                }

              }

            } catch (error) {}
  • 在小游戏的game.js文件中添加一个下载操作

wx.localAssets = require("./localAssets");

wx.remoteUrl = 'http:/xxx.xxx.xxx'; 

wx.localAssetsFinish = false; //标记下载完成

let func = ()=>{

    let ver = '20210626';

    setTimeout(()=>{

        wx.getStorage({

            key: 'LocalAssets_Ver',

            success(res) {

                const fs = wx.getFileSystemManager();

                if(res.data != ver) {

                    try{ 

                        fs.rmdirSync(`${wx.env.USER_DATA_PATH}/localAssets`, false); 

                        wx.removeStorageSync('LocalAssets_Ver');

                    } 

                    catch{};

                }

                fs.access({

                    path: `${wx.env.USER_DATA_PATH}/localAssets`,

                    success(){

                        wx.localAssetsFinish = true;

                    },

                    fail(){

                        wx.downloadFile({

                            url: wx.remoteUrl + '/localAssets.zip',

                            success(res){

                                if(res.statusCode === 200){

                                    fs.unzip({

                                        zipFilePath: res.tempFilePath,

                                        targetPath: `${wx.env.USER_DATA_PATH}/localAssets`,

                                        success() {

                                            console.log('unzipXXXXXXXXXXXXXXXXX');

                                            wx.localAssetsFinish = true;

                                        },

                                        fail(err) {

                                            console.log(err);

                                            func();

                                        }

                                    })

                                }

                            },

                            fail(err) {

                                console.log(err);

                                func();

                            }

                        })

                    }

                })

            },

            fail(err){

                console.log(err);

                wx.setStorage({

                    key:'LocalAssets_Ver',

                    data: '000000',

                    success(){

                        func();

                    },

                    fail(err){

                        console.log(err);

                        func();

                    }

                });

            }

        })

    }, 100);

}

func();
  • 修改项目代码,在提前加载资源或加载场景之前确保

wx.localAssetsFinish = true

总结:

通过以上方案结合使用,将我们项目中的这个场景的加载时间缩短到了3-4s;如果觉得想简单一点,我觉得使用最后的优化方案应该基本可以解决问题

1赞

你好,你这个是针对cocos creator多少版本的?

我是2.4.3,应该2.4版本都能用

mark!

你是把build的小游戏下面的remote文件夹打包吗?
按照你上面的步骤就就能实现remote打包zip包,解压后给cocos解析?

是的~~~