Cocos2d-JS 热更新

Cocos2d-JS 热更新,第1张

概述前言 工作需要,在空闲时间看了下Cocos2d-JS的热更新。对其进行了一个简单的实现,这里总结分享一下。 Cocos2d-JS 热更新 Cocos2d-JS 热更新是啥?Cocos2d-JS终归还是一个游戏引擎,就以游戏的过程来理解吧。传统游戏需要更新人物动画、地图场景、游戏逻辑、背景音乐怎么办?新出一个APP放到应用商店等用户下载,或者好一点游戏内提示又升级并自行下载完整的新版本APP。 使用

前言
工作需要,在空闲时间看了下Cocos2d-Js的热更新。对其进行了一个简单的实现,这里总结分享一下。


Cocos2d-Js 热更新

Cocos2d-Js 热更新是啥?Cocos2d-Js终归还是一个游戏引擎,就以游戏的过程来理解吧。传统游戏需要更新人物动画、地图场景、游戏逻辑、背景音乐怎么办?新出一个APP放到应用商店等用户下载,或者好一点游戏内提示又升级并自行下载完整的新版本APP。

使用Cocos2d-Js的热更新,那就大不一样了。它可以做到进入游戏后下载需要更新的资源甚至是脚本本身,而且更新过程不需要退出游戏。只要用户联网,就能够保证使用到最新的资源。

这些场景就非常适合使用Hot Fix

想在游戏中对春节开放新活动,不能保证应用商店能准时过审核上线

发现一个严重的BUG,需要立即修复

需要经常换游戏资源,提升新鲜感

先放一个中规中矩的版本过市场审核,然后绕过审核自己直接推新内容,哈哈

对于游戏来说,这个特性是比较重量级的。不过因为JavaScript的语言特性能支持这一功能,所以Cocos2d-Js能用此特性,而Cocos2d-x无法使用。


特性

以下是官方提供的特性

多线程并行下载支持

两层进度统计信息:文件级以及字节级

Zip压缩文件支持

断点续传

详细的错误报告

文件下载失败重试支持


Simple

1. manifest配置文件

热更新需要用到的配置文件有2种,都是以.manifest结尾。

一种是精简版,一种是完整版。精简版称为version.manifest,完整版称为project.manifest。

version.manifest:

1 2 3 4 5 6 7 8 9 10 11 { "packageUrl" : "http://127.0.0.1:8080/JsUpdateServer/res" , "remoteManifestUrl" "http://127.0.0.1:8080/JsUpdateServer/res/project.manifest" "remoteVersionUrl" "http://127.0.0.1:8080/JsUpdateServer/res/version.manifest" "version" "1.0.0" "groupVersions" :{ "1" "1.0.1" "2" "1.0.2" }, "engineVersion" "3.3" }

version.manifest是可选的,其中的所有字段也出现在project.manifest中,并且内容一样,如果没有就会从服务端下载完整版。但是如果完整版特别大,那么这个小版本的优势就很明显了。

project.manifest:

11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
"3.3" "assets" :{ "update1" :{ "path" "src/app.zip" "md5" "41D8E948052B5B714B14F81612CF534D" "compressed" : true "group" "1" "update2" :{ "res/HelloWorld.png" "A0FA3FA681D500575012D5E802F74D50" "1" "update3" :{ "res/Bound.png" "E7D4218B02CD0C5BB35ADC55E133DBA2" "1" "update4" :{ "src/resource.Js" "BA47101EBB65FBFCFB61C4CC57A306CA" "2" } "searchPaths" :[ "res/" ] 所有字段具体含义,官方文档的定义还是比较详细,只是少了groupVersions这个新字段:

packageUrl : 远程资源的下载根路径。

remoteVersionUrl : 远程版本文件的路径,用来判断服务器端是否有新版本的资源。

remoteManifestUrl : 远程配置文件的路径,包含版本信息以及所有资源信息。

version : 配置文件对应的版本。

groupVersions : 是新增的功能字段,用于做增量更新很方便。

engineVersion : 配置文件对应的引擎版本。

assets : 所有资源信息。

key : 升级名称

path : 键代表资源的相对路径(相对于packageUrl)。

md5 : md5值代表资源文件的版本信息。

compressed : [可选项] 如果值为true,文件被下载后会自动被解压,目前仅支持zip压缩格式。

searchPaths : 需要添加到Cocos2d引擎中的搜索路径列表。

以我的配置为例,一个完整的更新流程是这样的:

先通过本地的version.manifest和服务端的version.manifest比较,如果本地version低于服务端,那么就会再去获取project.manifest。

如果version相同,那么会比较groupVersions。

如果本地没有下载过groupVersions中的任何更新,那么会依次下载升级包。

如果本地下载过1.0.1版本的升级包,那么就会跳过1.0.1下载属于1.0.2版本的升级内容。

如果下载失败,或者没有网络导致更新失败的,会继续使用未更新前的版本。并且下次启动会继续尝试更新。

2. 创建后台

用一种你喜欢的方式起一个后台服务。我朴实无华的用IDE(Eclipse for JavaEE)建立了一个空的Web工程,用Tomcat起了一个后台。

从刚才的配置文件可以看出,Web工程名为JsUpdateServer。

在WebContent的根目录下新建一个index.HTML文件,随便在里边写些东西,用来验证我的后台已经起来。

Run起来之后,在浏览器里输入http://localhost:8080/JsUpdateServer/index.HTML看看是不是能够看到你刚才写的内容?

到这里你的后台已经OK了~

3. 创建Cocos2d-Js工程

新建一个Cocos2d-Js工程,可以用专用的IDE(Cocos Code IDE),官网有下载,也有如何配置的教程,这里就不多说了。

在res目录下,新建一个project.manifest文件。这个是初始版本信息,之后的更新会用到这个文件。内容如下:

"1.0":{:[] 在src目录下,新建一个JsList.Js文件,内容如下:

4
varJsList=[ "src/app.Js" ]

这个文件里包括的是需要加载的Js文件路径,将会在其他地方加载。

在src目录下,新建一个assetsManagerScene.Js文件,这里就是热更新的主要逻辑了。代码如下:

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
varfailCount=0; varmaxFailCount=1; //最大错误重试次数 /** *自动更新Js和资源 */ varassetsManagerLoaderScene=cc.Scene.extend({ _am:null, _progress:null, _percent:0, run:function(){ if (!cc.sys.isNative){ this .loadGame(); return ; } varlayer= new cc.Layer(); .addChild(layer); ._progress= cc.LabelTTF.create( "update0%" "Arial" ._progress.x=cc.winSize.wIDth/2; ._progress.y=cc.winSize.height/2+50; layer.addChild( ._progress); varstoragePath=(Jsb.fileUtils?Jsb.fileUtils.getWritablePath(): "./" ); cc. log ( "storagePathis" +storagePath); ._am= Jsb.AssetsManager( "res/project.manifest" ._am.retain(); (! ._am.getLocalManifest().isLoaded()) //if(true) { "Failtoupdateassets,stepskipped." ); .loadGame(); } else { varthat= ; cc.EventListenerAssetsManager varListener= Jsb.EventListenerAssetsManager( ._am,function(event){ switch (event.getEventCode()){ case Jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: "Nolocalmanifestfilefound,skipassetsupdate." ); that.loadGame(); break ; Jsb.EventAssetsManager.UPDATE_PROGRESSION: that._percent=event.getPercent(); (that._percent+ "%" ); varmsg=event.getMessage(); (msg){ (msg); } ; Jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: Jsb.EventAssetsManager.ERROR_PARSE_MANIFEST: "Failtodownloadmanifestfile,updateskipped." ); that.loadGame(); ; Jsb.EventAssetsManager.ALREADY_UP_TO_DATE: "ALREADY_UP_TO_DATE." ); that.loadGame(); ; Jsb.EventAssetsManager.UPDATE_FINISHED: "Updatefinished." ); that.loadGame(); ; Jsb.EventAssetsManager.UPDATE_Failed: "UpdateFailed." +event.getMessage()); failCount++; (failCount<maxFailCount) { that._am.downloadFailedAssets(); } else { "Reachmaximumfailcount,exitupdateprocess" ); failCount=0; that.loadGame(); } ; Jsb.EventAssetsManager.ERROR_UPDATING: "Assetupdateerror:" +event.getAssetID()+ "," +event.getMessage()); that.loadGame(); ; Jsb.EventAssetsManager.ERROR_DECOMPRESS: (event.getMessage()); that.loadGame(); ; default : ; } }); cc.eventManager.addListener(Listener,1); ._am.update(); cc.director.runScene( ); } .schedule( .updateProgress,0.5); loadGame:function(){ //JsList是JsList.Js的变量,记录全部Js。 cc.loader.loadJs([ "src/JsList.Js" ],function(){ cc.loader.loadJs(JsList,function(){ HelloWorldScene()); }); }); updateProgress:function(dt){ ._progress.string= "update" + ._percent+ ; onExit:function(){ "AssetsManager::onExit" ); ._am.release(); ._super(); } });

热更新主要是通过引擎提供的AssetsManager来实现的。

在AssetsManagerLoaderScene里,用到了res/project.manifest,是用来对版本对比的。

下载完成后的资源会被解压,然后放在Jsb.fileUtils.getWritablePath()的路径下,加到引擎的搜索范围中,并且他们的优先级是高于APP原本的资源路径的。所以相同的资源名称,引擎会优先使用更新路径下的文件,就达到更新的目的。

下载成功后会执行loadGame方法,里边会加载src/JsList.Js的所有资源。也就是说通过AssetsManagerLoaderScene来确保热更新完成后,再加载所有资源。

由于资源和脚本的加载顺序发生了改变,所以还要修改根目录下的main.Js和“project.Json”。

main.Js:

9
cc.game.onStart=function(){ cc.vIEw.adjustVIEwPort( ); cc.vIEw.setDesignResolutionSize(800,450,cc.ResolutionPolicy.SHOW_ALL); cc.vIEw.resizeWithbrowserSize( ); varscene= AssetsManagerLoaderScene(); scene.run(); }; cc.game.run();

将启动场景改为我的热更新场景AssetsManagerLoaderScene。

project.Json:

16
"project_type" "JavaScript" "deBUGMode" :1,monospace!important; Font-size:1em!important; min-height:inherit!important; color:blue!important">"showFPS" "frameRate" :60,monospace!important; Font-size:1em!important; min-height:inherit!important; color:blue!important">"ID" "gameCanvas" "renderMode" :0,monospace!important; Font-size:1em!important; min-height:inherit!important; color:blue!important">"engineDir" "frameworks/cocos2d-HTML5" "modules" "cocos2d" "extensions" "JsList" :[ "src/assetsManagerScene.Js" ] 将JsList的值改为src/assetsManagerScene.Js,只要加载这个Js,其他Js会在AssetsManagerLoaderScene中被加载。

到这里你可以跑起来看下,虽然获取不到升级文件,但是可以以最原始的版本跑起来。

4. 在后台配置升级文件

现在到后台工程的WebContent目录下添加升级文件。

从我的manifest配置文件你可以看到,我又建立了一个res文件夹,在其中分别建立src和res分别对应Cocos2d-Js工程中的资源、脚本文件夹。

随意修改一下app.Js,将其作为更新内容使用。总结了几个要注意的地方。

升级文件的路径,一定要和配置文件中的path内容一致。

升级文件是.zip,记得compressed改为true。

添加新的资源文件要记得同步更新resource.Js。

新增Js文件也记得同步更新JsList.Js。

5. 回到Cocos2d-Js工程

核对一下project.manifest文件中的地址是不是和后台一致,内容是否正确。

没问题的话就可以跑起来了,会发现你的升级内容被下载下来并且更新了。


参考

本文工程后台&Cocos2d-Js的简陋源码戳这里,Cocos2d-Js工程中的文件替换你的文件,通用的引擎工程太大没有上传。

其他可以使用到的教程

资源管理器ASSETS MANAGER

Cocos2d-JS热更新

来源网址:http://karelgt.com/Cocos2dJS 热更新/

总结

以上是内存溢出为你收集整理的Cocos2d-JS 热更新全部内容,希望文章能够帮你解决Cocos2d-JS 热更新所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/web/1055558.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)