爆裂吧现实

不想死就早点睡

0%

Cocos多模块热更新方案

最近由于项目要从c++移植到js,为的就是热更新。c++因为种种原因无法热更新,所以转到脚本方案了。cocos2dx-js中热更新的代码已经帮我们写好了,我们只需要写一下配置文件就行了。
然而官方的配置文件的文档有点落后,这里有一份好的Cocos2dxJS 热更新

公司的项目是一个棋牌休闲游戏,内部有很多模块(大厅模块一个、若干游戏模块)。需求为游戏需要玩才下载,所以要针对不用的模块编写不同的配置文件。这种机械性的工作当然是交给计算机做啦,写一个脚本完事。

cocos在开始热更新的时候会先读取本地指定的project.manifest文件,从文件中解析出远程version.manifest的地址,然后下载之。下载完后比较本地的版本号和远端的版本号(此版本非彼坂本),如果不一致就开始更新,更新的时候先下载远端的project.manifest然后读取里面的文件列表,和本地的project.manifest进行比较,如果文件有新增或者相同文件但是md5的数值记录不一样,这时候就会从服务器下载该文件,如果有文件在本地的project.manifest有但是服务器版的project.manifest中没有。cocos就会删除之。

现在最新的代码和资源已经下到了,现在要解决的问题就是怎样让游戏正确的加载下载的资源。这时候就需要添加搜索路径了。

添加游戏搜索路径,这个是jsb才支持的方法。为了方便起见,游戏的默认搜索路径就是根目录。根目录下面有ressrc等。所以只需要在下载的时候,目录结构保持和游戏默认的目录结构一样,然后把下载的根目录添加到搜索路径里面,并且优先级比默认的高。这样的话游戏就能正确读取更新的代码和资源了,添加搜索路径必须在cc.game.run();之前。

1
2
3
4
5
6
if(jsb){
//添加搜索路径,true表示添加到搜索路径前方,高优先权
jsb.fileUtils.addSearchPath(
(jsb.fileUtils.getWritablePath() + "update"), true
);
}

cocos2dx-JS中有一个project.json,里面主要保存的是需要加载的js代码。前面有说到,把游戏分成多个模块来加载,启动大厅模块是游戏启动加载的,里面主要有:

  1. 登录
  2. 大厅
  3. 银行
  4. 支付
  5. 设置

游戏代码是在进入游戏的时候去加载的,这样可以除去不必要的js代码加载。综上所述,当大厅更新的时候,需要重启这个游戏 (调用cc.game.restart();即可)。当游戏更新的时候,只需要加载游戏部分的代码就行。

怎么加载制定的js代码呢?别忘了cocos是开源的,我们在cc.game.run();跳进去看看,发现里面有个this.prepare(cc.game.onStart && cc.game.onStart.bind(cc.game),再跳进去看看就发现了this._loadConfig();这个方法,这个看名字就知道是去加载project.json了,往下看就会发现jsList的加载代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
prepare: function (cb) {
...
this._loadConfig();
...
if (jsList) {
cc.loader.loadJsWithImg(jsList, function (err) {
if (err) throw new Error(err);
self._prepared = true;
if (cb) cb();
});
}
else {
if (cb) cb();
}
...
}

发现其实就是调用了cc.loader.loadJsWithImg(jsList, callback)这个方法来加载js的。这就好办了,我们只需要把每个游戏模块的js代码文件写到一个属于当前模块的project.json中,然后当游戏下载完之后,解析这个project.json文件,读取里面的jsList加载之就可以。ps (同一份js代码加载两次会出现崩溃,目前不知道原因。如果出现了必须重新加载同一份代码的情况,请重启游戏)

下载游戏

当你的游戏是空的时候,肯定要下载整个游戏。如果下载的全部是碎片文件的话,这样对服务器压力很大。这时候就可以考虑下载zip压缩包。然而这里会有几个问题。

  1. cocos在下载的时候会把version.manifestproject.manifest放到当前下载的设定的根目录,也就是说我们不同模块的根目录必须不一样,如果你设置src/GameModule/LKPY/为李逵劈鱼下载的根目录的话,李逵劈鱼的美术资源就在res/Games/LKPY/下面,导致美术资源文件和project.manifest文件的最近公共祖先不是project.manifest所在的目录。然而解决方案很简单,在project.manifest中填写美术资源的相对路径就行了。(ps:iOS平台不支持相对路径的下载操作,必须修改c++源码 url处理链接c++样例)
  2. 如果zip压缩包内有文件夹,而且下载缓存目录中并没有这个文件夹路径的话解压会错误。这个问题我是通过修改引擎解决的(AssetsManagerEx.cpp中的decompress方法在359// Create a file to store current file.后面添加一行fileUtils->createDirectory(basename(fullPath));)。反正cocosbug也比较多

当游戏为空的时候需要去下载zip资源,游戏不为空的时候,增量更新。但是cocos只会去下载在project.manifest中指定的文件。所以我需要3种类型的project.manifestempty.manifest, compres.manifest, project.manifest

最初只有empty.manifest,里面的版本号为0.0.0,代表没有游戏的情况,地址指向相应服务器的compress.manifest配置文件。
之后cocos就会去下载compress.manifest中的压缩包资源,解压到对应的目录。其中compress.manifest文件中的地址指向的是服务器上的project.manifest文件,之后就可以差量更新了。

编写用于生成配置文件的脚本

介于上述要求,有大量的配置文件需要编写,这些工作当然是交给脚本来处理啦。把需要升级的设定成不同的模块,每个模块都有自己的project.manifest。这里打算用一个模块的json配资文件来生成相应模块的project.manifest(PS:会生成project.manifest compress.manifestempty.manifestproject.json)。

Manifest.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"module": [
{
//本地热跟新根目录
"rootPath": "../src/GameModule/LT30S",
//jsList文件目录
"configFilePath": "project.json",
//热跟新远程根目录
"packageUrl": "http://192.168.0.3/src/GameModule/LT30S/",
//远端配置信息
"remoteVersionUrl": "http://192.168.0.3/src/GameModule/LT30S/version.manifest",
//远端工程信息
"remoteManifestUrl": "http://192.168.0.3/src/GameModule/LT30S/project.manifest",
//当前模块版本
"version": "1.0.0",
//是否压缩
"compress": false,
//引擎信息
"engineVersion": "Cocos2d-JS v3.10",
"paths": [
//需要打包的资源
"../../../res/Games/LT30SGame",
""
],
"exIncludePaths": [
//不包括进打包的资源
"GameUpdateScene.js",
"version.manifest",
"project.manifest"
]
}
]
}