官网例子:https://github.com/cocos-creator/tutorial-hot-update
原帖:craetor热更新
之前使用quick-cocos2d-x,做过lua版本的热更新,后来转了creator,也做过creator的热更新,在这里分享一上心得。
lua版本的热更新只使用了cocos的下载功能,其它工作全都是自己实现,有兴趣的同学点击这里可以看看实现代码。这套东西是16年做的,实现了读取配置文件,比对配置文件,下载写入等等功能,对于现在来说代码里的很多API可能已经不适用了,但重在原理和功能,如果是做lua版本热更新,拿过来改改就可以跑起来。
Cocos在热更新上帮我们做了很多工作,这就导致很多同学在做热更新功能的时候只是看着文档来,但是做完了也不知道实现了什么功能,出了问题也是一头雾水。做热更新,先要了解热更新要做什么,把问题拆开来一个一个去解决。这篇文章里不去讲细节实现,而是告诉你Cocos热更新的原理。理解透了这套原理,做热更就是小菜一碟。
如何去真正理解透彻,建议读完这篇文章,找一款root过的安卓手机,安装RootExplore,配合Android Studio,调试,打印功能,再加上官方的Demo,把不明白的地方挨个都扣清楚。
热更新的核心功能
- 读取配置文件
- 从远程下载新的配置文件并和本地进行对比
- 下载更新文件,并写入本地
- 设置搜索路径的优先级
理解了这几个功能,如果可以自己徒手实现,热更新的可定制性将大大提升。如果实现不了,就使用Cocos自带的,基本也能满足我们的需求。
配置文件
记录当前游戏版本的文件,Creator使用的是mainfest文件,看一下里面的内容就可以知道,是当前的所有游戏资源文件的MD5值,在这里,你可以把脚本文件也当成游戏资源文件的一种。这个文件游戏本地放一个,远程服务器放一个,进游戏时下载远程文件和本地进行对比,筛选出MD5值不一样的文件,然后进行下载。下载完成后,把新的mainfest文件写入本地。下次再进入游戏时,读取新的mainfest文件和远程对比。配置文件也可以是其它格式的文件,json,lua,js等等。它就仅仅是记录版本的信息而已,只要你能在游戏中读取到这个文件的内容,什么格式都ok。配置文件的生成可以直接使用官方的脚本文件,也可以自己用python写,目的都是生成配置信息而已。
从远程下载新的配置文件并和本地进行对比
下载功能creator已经帮我们做了,读取配置文件也帮我们做了,我们要做的仅仅是拿着两个文件的数据进行比对,告诉creator要不要更新,更新什么,下载文件之后放到哪个目录。
下载更新文件,并写入本地
逐个下载上一步中比对出来的要下载的文件,并放入到预先设置好的目录。
设置搜索路径优先级
搜索路径优先级是说当去加载一个资源的时候,会先去优先级高的路径寻找,如果没有,再逐级向下寻找。Creator中的搜索路径是以Array存储的。比如一张头像图的路径是UI/HeadImg/1.png
,搜索路径的优先级是['/Path_a/','/Path_b/']
,那么会先去Path_a
文件夹下寻找有没有UI/HeadImg/1.png
,如果没有,再去Path_b
寻找。
以下是官方文档中在main.js文件中添加的代码,也就是搜索路径的优先级设置。单看这里的设置,根本看不出来什么,HotUpdateSearchPaths
这个数据是什么?是从哪里存储进去的?
if (cc.sys.isNative) {
var hotUpdateSearchPaths = cc.sys.localStorage.getItem('HotUpdateSearchPaths');
if (hotUpdateSearchPaths) {
jsb.fileUtils.setSearchPaths(JSON.parse(hotUpdateSearchPaths));
}
}
我们去看官方的Demo工程中的HotUpdate.js文件中的一些代码:
this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'blackjack-remote-asset');
this._storagePath在脚本中多次用到,这个变量就是我们自己设置的文件下载目录的路径,blackjack-remote-asset可以更改成任何你喜欢的目录名称,最好是数字+字母或者纯字母。jsb.fileUtils.getWritablePath()获取到的是可写目录,这个目录的路径是什么?安卓使用root过的手机下载RootExplore查看/data/data/游戏包名/files/blackjack-remote-asset
,这就是我们存放下载的资源的目录,也是我们设置的搜索路径优先级最高的目录。更新完成之后,新版本的mainfest文件同样也会被放入这里。
为什么要放入这里?我们打出apk包安装到手机之后,是无法更改apk里的内容的,getWirtablePath
获取到一个可写目录,这是原生Android给开发者开放的接口。你可以在这里存放一些需要修改的文件,IOS同理。
再看下面一段代码
if (needRestart) {
this._am.setEventCallback(null);
this._updateListener = null;
// Prepend the manifest's search path
var searchPaths = jsb.fileUtils.getSearchPaths();
var newPaths = this._am.getLocalManifest().getSearchPaths();
console.log(JSON.stringify(newPaths));
Array.prototype.unshift.apply(searchPaths, newPaths);
// This value will be retrieved and appended to the default search path during game startup,
// please refer to samples/js-tests/main.js for detailed usage.
// !!! Re-add the search paths in main.js is very important, otherwise, new scripts won't take effect.
cc.sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
jsb.fileUtils.setSearchPaths(searchPaths);
cc.audioEngine.stopAll();
cc.game.restart();
}
从以上代码中可以看到,是在更新完毕,准备重启的时候存入HotUpdateSearchPaths
。下载完所有资源,并把下载的新资源的存放目录的路径获取到,使用Array.prototype.unshift.apply(searchPaths, newPaths)
生成新的搜索目录数组,并把可写目录放到前面,存入本地。游戏再次启动的时候,在main.js文件中获取到当前的搜索目录,设置进去,使热更新生效。
原理就是这样。
回顾热更新的核心功能
- 读取配置文件
- 从远程下载新的配置文件并和本地进行对比
- 下载更新文件,并写入本地
- 设置搜索路径的优先级
Creator的AssetsManager替我们做了几项工作,读取配置文件,下载更新文件并写入本地。
在照着官方文档做过程中可能会遇到各种问题,但是都可以通过细读代码解决,还可能会由于Creator版本问题导致Demo运行不起来,没有什么好办法,去改代码,哪里报错看哪里,哪里出错改哪里。代码对我们程序员来说是难题吗?不是,难的是解决问题的思路和方法。