Cocos2d-Js热更新(最完整版本,包括自己做的过程中遇到的坑都在里面)
2016-12-30 09:58
363 查看
最近主要进行游戏脚本化相关工作,脚本化的目的就是为了热更新,所以就写个demo研究下热更新。
cocos版本: 3.12
需要热更新js不放在project.json中定义,等AssetsManager更新完了,用cc.loader.loadJs动态加载;
所以在jsb.fileUtils.getWritablePath()目录下载的资源和js文件,与项目目录保持一致,那么优先加载新下载的资源和js文件,再进入游戏,从而实现热更新的目的。
- 多线程并行下载支持
- 两层进度统计信息:文件级以及字节级
- Zip压缩文件支持
- 断点续传
- 详细的错误报告
- 文件下载失败重试支持
在res资源目录下,新建project.manifest文件,填入初始信息,内容如下:
其中地址为我自己的测试地址,各位看官自己进行相应的替换(这里有个坑,详见文末)
src目录下新建jsList.js文件,需要动态加载的js文件都写在jsFiles这个数组里,这样js文件有增加变化,这个files.js一并更新,方便动态加载,内容如下:
src目录下新建assetsManagerScene.js文件,内容如下:
这里用到了上一步创建的res/project.manifest文件,目的是用于进行版本对比。
因为下载的文件是保存在可写目录下,加载的顺序发生了变化,所以需要修改根目录下的main.js和“project.json”两个文件。
main.js :
project.json :
到这一步客户端基本配置已经完成,这个时候客户端是可以运行的。
新建web工程,用作测试服务器(我这里使用Myeclipse新建的web工程,搭建web服务器这里不做赘述)
在WebRoot目录下,添加res文件夹,在其内部分别建立res和src用于对应cocos工程的资源文件夹和源文件夹目录,拷贝app.js到src目录下,随便修改一点东西,作为更新内容。
res文件夹目录下添加配置文件version.manifest和project.manifest文件
version.manifest :
project.manifest :
packageUrl : 远程资源的下载根路径。
remoteVersionUrl : 远程版本文件的路径,用来判断服务器端是否有新版本的资源。
remoteManifestUrl : 远程配置文件的路径,包含版本信息以及所有资源信息。
version : 配置文件对应的版本。
groupVersions : 是新增的功能字段,用于做增量更新很方便。
engineVersion : 配置文件对应的引擎版本。
assets : 所有资源信息。
key : 升级名称
path : 键代表资源的相对路径(相对于packageUrl)。
md5 : md5值代表资源文件的版本信息。
compressed : [可选项] 如果值为true,文件被下载后会自动被解压,目前仅支持zip压缩格式。
searchPaths : 需要添加到Cocos2d引擎中的搜索路径列表。
通过学习底层逻辑,发现更新的逻辑顺序是这样的:
先通过本地的version.manifest和服务端的version.manifest比较,如果本地没有version.manifest,则会先进行下载,如果本地version低于服务端,那么就会再去获取project.manifest。
如果version相同,那么会比较groupVersions。
如果本地没有下载过groupVersions中的任何更新,那么会依次下载升级包。
如果本地下载过1.0.1版本的升级包,那么就会跳过1.0.1下载属于1.0.2版本的升级内容。
如果下载失败,或者没有网络导致更新失败的,会继续使用未更新前的版本。并且下次启动会继续尝试更新。
这是我的web工程目录
至此,基本的更新逻辑已经完成。
增量更新只是在服务端version.manifest和project.manifest文件增加相应的描述即可。
我在做的过程中主要遇到了一下几个问题:
1. 由于公司内部网络比较多,大家接入的都是内网,导致测试手机和web服务器不在一个网络,这个是个人原因
2. web工程目录问题,导致文件下载失败,错误描述如图:
这个主要原因是,manifest文件中的packageUrl和path拼接后的地址与web工程的目录不符合,建议各位看官在web工程搭建完成之后先在网页访问下需要下载的文件,确保拼接后的地址能过够正常访问。
点我获取源码
cocos版本: 3.12
1、基本思路
cocos的热更新主要采用其自带的AssetsManager,执行AssetsManager后,搜索路径增加了jsb.fileUtils.getWritablePath()目录,并且搜索级别最优;需要热更新js不放在project.json中定义,等AssetsManager更新完了,用cc.loader.loadJs动态加载;
所以在jsb.fileUtils.getWritablePath()目录下载的资源和js文件,与项目目录保持一致,那么优先加载新下载的资源和js文件,再进入游戏,从而实现热更新的目的。
2、特性
此为官方说明的特性~~~~- 多线程并行下载支持
- 两层进度统计信息:文件级以及字节级
- Zip压缩文件支持
- 断点续传
- 详细的错误报告
- 文件下载失败重试支持
3、我的流程
新建空的cocos2d-js工程并进行相应的修改用作demo在res资源目录下,新建project.manifest文件,填入初始信息,内容如下:
{ "packageUrl" : "http://192.168.3.139:8080/hotUpdateService/res/", "remoteManifestUrl" : "http://192.168.3.139:8080/hotUpdateService/res/project.manifest", "remoteVersionUrl" : "http://192.168.3.139:8080/hotUpdateService/res/version.manifest", "version" : "1.0.0", "engineVersion" : "3.12", "groupVersions" : { "1" : "1.0.0" }, "assets" : { }, "searchPaths" : [ ] }
其中地址为我自己的测试地址,各位看官自己进行相应的替换(这里有个坑,详见文末)
src目录下新建jsList.js文件,需要动态加载的js文件都写在jsFiles这个数组里,这样js文件有增加变化,这个files.js一并更新,方便动态加载,内容如下:
var jsList = [ "src/resource.js", "src/app.js" ];
src目录下新建assetsManagerScene.js文件,内容如下:
/** * Created by MartinYing on 2016/12/28. */ var failCount = 0; var maxFailCount = 1; //最大错误重试次数 /** * 自动更新js和资源 */ var AssetsManagerLoaderScene = cc.Scene.extend({ _am:null, _progress:null, _percent:0, run:function(){ cc.log("enter run function ..... "); if (!cc.sys.isNative) { this.loadGame(); return; } var layer = new cc.Layer(); this.addChild(layer); this._progress = new cc.LabelTTF.create("update 0%", "Arial", 38); this._progress.x = cc.winSize.width / 2; this._progress.y = cc.winSize.height / 2 + 50; layer.addChild(this._progress); var storagePath = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : "./"); cc.log("storagePath is " + storagePath); this._am = new jsb.AssetsManager("res/project.manifest", storagePath); this._am.retain(); if (!this._am.getLocalManifest().isLoaded()) //if (true) { cc.log("Fail to update assets, step skipped."); this.loadGame(); } else { var that = this; var listener = new jsb.EventListenerAssetsManager(this._am, function(event) { switch (event.getEventCode()){ case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: cc.log("enter ERROR_NO_LOCAL_MANIFEST ..... "); cc.log("No local manifest file found, skip assets update."); that.loadGame(); break; case jsb.EventAssetsManager.UPDATE_PROGRESSION: cc.log("enter UPDATE_PROGRESSION ..... "); that._percent = event.getPercent(); cc.log(that._percent + "%"); var msg = event.getMessage(); if (msg) { cc.log(msg); } break; case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: cc.log("enter ERROR_DOWNLOAD_MANIFEST ..... "); cc.log("Fail to download manifest file, update skipped."); that.loadGame(); break; case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST: cc.log("enter ERROR_PARSE_MANIFEST ..... "); cc.log("Fail to download manifest file, update skipped."); that.loadGame(); break; case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: cc.log("enter ALREADY_UP_TO_DATE ..... "); cc.log("ALREADY_UP_TO_DATE."); that.loadGame(); break; case jsb.EventAssetsManager.UPDATE_FINISHED: cc.log("enter UPDATE_FINISHED ..... "); cc.log("Update finished."); that.loadGame(); break; case jsb.EventAssetsManager.UPDATE_FAILED: cc.log("enter UPDATE_FAILED ..... "); cc.log("Update failed. " + event.getMessage()); failCount++; if (failCount < maxFailCount) { that._am.downloadFailedAssets(); } else { cc.log("Reach maximum fail count, exit update process"); failCount = 0; that.loadGame(); } break; case jsb.EventAssetsManager.ERROR_UPDATING: cc.log("enter ERROR_UPDATING ..... "); cc.log("Asset update error: " + event.getAssetId() + ", " + event.getMessage()); that.loadGame(); break; case jsb.EventAssetsManager.ERROR_DECOMPRESS: cc.log("enter ERROR_DECOMPRESS ..... "); cc.log(event.getMessage()); that.loadGame(); break; default: break; } }); cc.eventManager.addListener(listener, 1); this._am.update(); cc.director.runScene(this); } this.schedule(this.updateProgress, 0.5); }, loadGame:function(){ cc.log("enter loadGame function ..... "); //jsList是jsList.js的变量,记录全部js。 cc.loader.loadJs(["src/jsList.js"], function(){ cc.loader.loadJs(jsList, function(){ cc.director.runScene(new HelloWorldScene()); }); }); }, updateProgress:function(dt){ cc.log("enter updateProgress function ..... "); this._progress.string = "update " + this._percent + "%"; }, onExit:function(){ cc.log("AssetsManager::onExit"); this._am.release(); this._super(); } });
这里用到了上一步创建的res/project.manifest文件,目的是用于进行版本对比。
因为下载的文件是保存在可写目录下,加载的顺序发生了变化,所以需要修改根目录下的main.js和“project.json”两个文件。
main.js :
cc.game.onStart = function(){ if(!cc.sys.isNative && document.getElementById("cocosLoading")) //If referenced loading.js, please remove it document.body.removeChild(document.getElementById("cocosLoading")); // Pass true to enable retina display, on Android disabled by default to improve performance cc.view.enableRetina(cc.sys.os === cc.sys.OS_IOS ? true : false); // Adjust viewport meta cc.view.adjustViewPort(true); // Uncomment the following line to set a fixed orientation for your game // cc.view.setOrientation(cc.ORIENTATION_PORTRAIT); // Setup the resolution policy and design resolution size cc.view.setDesignResolutionSize(960, 640, cc.ResolutionPolicy.SHOW_ALL); // The game will be resized when browser size change cc.view.resizeWithBrowserSize(true); var scene = new AssetsManagerLoaderScene(); scene.run() // //load resources // cc.LoaderScene.preload(g_resources, function () { // cc.director.runScene(new HelloWorldScene()); // }, this); }; cc.game.run();
project.json :
{ "project_type": "javascript", "debugMode" : 1, "showFPS" : true, "frameRate" : 60, "noCache" : false, "id" : "gameCanvas", "renderMode" : 0, "engineDir":"frameworks/cocos2d-html5", "modules" : ["cocos2d", "extensions"], "jsList" : [ "src/assetsManagerScene.js" ] }
到这一步客户端基本配置已经完成,这个时候客户端是可以运行的。
新建web工程,用作测试服务器(我这里使用Myeclipse新建的web工程,搭建web服务器这里不做赘述)
在WebRoot目录下,添加res文件夹,在其内部分别建立res和src用于对应cocos工程的资源文件夹和源文件夹目录,拷贝app.js到src目录下,随便修改一点东西,作为更新内容。
res文件夹目录下添加配置文件version.manifest和project.manifest文件
version.manifest :
{ "packageUrl" : "http://192.168.3.139:8080/hotUpdateService/res/", "remoteManifestUrl" : "http://192.168.3.139:8080/hotUpdateService/res/project.manifest", "remoteVersionUrl" : "http://192.168.3.139:8080/hotUpdateService/res/version.manifest", "version" : "1.0.0", "groupVersions" : { "1" : "1.0.1" }, "engineVersion" : "3.12" }
project.manifest :
{ "packageUrl" : "http://192.168.3.139:8080/hotUpdateService/res/", "remoteManifestUrl" : "http://192.168.3.139:8080/hotUpdateService/res/project.manifest", "remoteVersionUrl" : "http://192.168.3.139:8080/hotUpdateService/res/version.manifest", "version" : "1.0.0", "groupVersions" : { "1" : "1.0.1" }, "engineVersion" : "3.12", "assets" : { "update1" : { "path" : "src/app.zip", "md5" : "D7698389FD1CA121DCD896035D67687C", "compressed" : true , "group" : "1" } }, "searchPaths" : [ ] }
packageUrl : 远程资源的下载根路径。
remoteVersionUrl : 远程版本文件的路径,用来判断服务器端是否有新版本的资源。
remoteManifestUrl : 远程配置文件的路径,包含版本信息以及所有资源信息。
version : 配置文件对应的版本。
groupVersions : 是新增的功能字段,用于做增量更新很方便。
engineVersion : 配置文件对应的引擎版本。
assets : 所有资源信息。
key : 升级名称
path : 键代表资源的相对路径(相对于packageUrl)。
md5 : md5值代表资源文件的版本信息。
compressed : [可选项] 如果值为true,文件被下载后会自动被解压,目前仅支持zip压缩格式。
searchPaths : 需要添加到Cocos2d引擎中的搜索路径列表。
通过学习底层逻辑,发现更新的逻辑顺序是这样的:
先通过本地的version.manifest和服务端的version.manifest比较,如果本地没有version.manifest,则会先进行下载,如果本地version低于服务端,那么就会再去获取project.manifest。
如果version相同,那么会比较groupVersions。
如果本地没有下载过groupVersions中的任何更新,那么会依次下载升级包。
如果本地下载过1.0.1版本的升级包,那么就会跳过1.0.1下载属于1.0.2版本的升级内容。
如果下载失败,或者没有网络导致更新失败的,会继续使用未更新前的版本。并且下次启动会继续尝试更新。
这是我的web工程目录
至此,基本的更新逻辑已经完成。
增量更新只是在服务端version.manifest和project.manifest文件增加相应的描述即可。
我在做的过程中主要遇到了一下几个问题:
1. 由于公司内部网络比较多,大家接入的都是内网,导致测试手机和web服务器不在一个网络,这个是个人原因
2. web工程目录问题,导致文件下载失败,错误描述如图:
这个主要原因是,manifest文件中的packageUrl和path拼接后的地址与web工程的目录不符合,建议各位看官在web工程搭建完成之后先在网页访问下需要下载的文件,确保拼接后的地址能过够正常访问。
点我获取源码
相关文章推荐
- JS完整获取IE浏览器信息包括类型、版本、语言等等
- IOS版本更新过程中遇到未能创建图标的问题
- node.js项目开发问题集锦(不定期更新,随时把开发过程中遇到的问题加上)
- 阿里云服务器设置-图文教程完整版本及在配置过程中遇到的问题解决方案
- [置顶] Cocos2d-x使用过程中遇到的错误(VS下开发,移植到Android发布)(更新ing)
- 上手Android源码探索AOSP的奥秘过程笔记(不是教程,只是自己遇到的问题或者心得的笔记,持续更新)
- cocos2d-js 使用过程中遇到的问题总结
- 在开放过程中要时刻关注技术的更新,并将自己的项目即使更新。(注:js中可以用三目运算)
- node.js项目开发问题集锦(不定期更新,随时把开发过程中遇到的问题加上)--转载
- Cocos2dx:cocos2d-x-3.2版本学习过程中所遇到的一些问题(每一次都是一次作死的感觉)
- Cocos2dx:cocos2d-x-3.2版本学习过程中所遇到的一些问题
- JS完整获取IE浏览器信息包括类型、版本、语言等等
- mac配置Android 环境,完整过程,其中包括遇到的设置$JAVA_HOME问题,新建项目报 Errors running builder 'Android Resource Manager问题
- cocos2d-x 3.4版本游戏打包AKP (重点记录如何解决打包过程中遇到的各种问题)
- 从零开始教你制作cocos2dx-3.0 版本FlappyBird(可上架版本)(包括添加广告等)完整制作过程
- cocos2d-html5里面修改了js之后,真机查看效果时,确保 Xcode 每次 Build 时都自动更新资源
- Cocos2d-x使用过程中遇到的错误(VS下开发,移植到Android发布)(更新ing)
- Cocos2d-JS连载之环境搭建遇到的问题及android打包过程中遇到的问题
- ubuntu11.0.4下编译Android2.3源码过程遇到问题解决【不断更新】
- SVN版本库迁移过程遇到的问题