转载至: http://blog.csdn.net/pt_xxj/article/details/68927705
为什么还要再写一篇关于cocos2d js热更新的笔记,最单纯的想法就是记录心得,另外也是因为添加了一个记录热更新资源大小的小功能,故而想分享一下。
在cocos2d js引擎中的js是作为一种特殊的资源文件使用(脚本文件),那么游戏运行过程中通过定制的资源管理器从服务器获取新的js文件并重新加载运行,那么也就实现了看似比较玄妙的热更新功能。当然此资源管理器需要具备资源对比、资源下载、资源处理、资源覆盖及资源加载等等的一系列功能,幸运的是cocos已经实现了这些功能,我们需要做的就是学会配置使用而已。
此热更新基于cocos2d-js 3.6.1测试使用。
————————————————朴实无华分割线,以上内容不重要————————————————
cocos2d js的热更新功能主要由jsb.AssetsManager实现,绑定了引擎底层的C++代码AssetsManager类,具体的逻辑和实现都可以查看此类。具体的热更新的是通过一个预先配置的project.manifest以及服务器端的project.manifest和version.manifest配合使用来实现的。
1、热更新系统实现
初始project.manifest:
{
"packageUrl": "http://192.168.0.73:8080/projectName/std/",
"remoteManifestUrl": "http://192.168.0.73:8080/projectName/std/project.manifest",
"remoteVersionUrl": "http://192.168.0.73:8080/projectName/std/version.manifest",
"version": "1.0.0",
"groupVersions" :
{
},
"groupSizes" :
{
},
"engineVersion": "3.6.1",
"assets" :
{
},
"searchPaths":
[
]
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
具有两个版本增量更新的服务器project.manifest:
{
"packageUrl": "http://192.168.0.73:8080/projectName/std/",
"remoteManifestUrl": "http://192.168.0.73:8080/projectName/std/project.manifest",
"remoteVersionUrl": "http://192.168.0.73:8080/projectName/std/version.manifest",
"version": "1.1.2",
"groupVersions" :
{
"1":"1.1.1",
"2":"1.1.2"
},
"groupSizes" :
{
"1":115.0,
"2":115.0
},
"engineVersion": "3.6.1",
"assets" :
{
"update1":
{
"path":"update1.zip",
"md5":"",
"compressed" : true,
"group":"1"
},
"update2":
{
"path":"update2.zip",
"md5":"",
"compressed" : true,
"group":"2"
}
},
"searchPaths":
[
"update1/",
"update2/"
]
}
- 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
- 34
- 35
- 36
- 37
- 38
- 39
具有两个版本增量更新的服务器version.manifest:
{
"packageUrl": "http://192.168.0.73:8080/projectName/std/",
"remoteManifestUrl": "http://192.168.0.73:8080/projectName/std/project.manifest",
"remoteVersionUrl": "http://192.168.0.73:8080/projectName/std/version.manifest",
"version": "1.1.2",
"groupVersions" :
{
"1":"1.1.1",
"2":"1.1.2"
},
"groupSizes" :
{
"1":115.0,
"2":115.0
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
此热更新配置文件我添加了groupSizes字段,用于展示更新资源文件大小,方便用户玩家自主选择更新时机。另外配置的时候按照渠道划分的,这个是std是标准渠道的热更新文件配置。至于其他字段网路上已经有太多的讲解,就不再重复。
资源文件配置完毕,那么就是具体的使用了,我使用了一个js的静态类来实现热更新的检测以及更新过程,我看其他人的实现一般都是直接强制更新,个人感觉不太友好,所以我的热更新实现是分步进行的。
hotfix-manager.js
/**
* 热更新管理器
*/
var HotFixManager = HotFixManager || {
// 更新资源管理器
_mAssetsMgr:null,
_mTryCount:0,
// 更新基本配置
_mManiFest:"",
_mWritePath:"",
_mTryTimes:1,
// 更新过程进度回调函数
_mUpdateCall:null,
_mUpdatetarget:null,
// 更新完成回调
_mFinishCall:null,
_mFinishtarget:null,
// 更新已找到标识
_mHadFind:false,
/**
* 热更新系统初始化
* @param call 更新完成回调函数
* @param target 回调函数绑定节点
*/
init:function(call,target)
{
this._mFinishCall = call;
this._mFinishtarget = target;
// 这一部分的配置使用也可以通过传递参数进行实现
this._mManiFest = "res/static/project.manifest";
this._mWritePath = jsb.fileUtils?jsb.fileUtils.getWritablePath()+"hotFix/":"./hotFix/";
this._mTryTimes = 1;
this._mAssetsMgr = new jsb.AssetsManager(this._mManiFest, this._mWritePath);
this._mAssetsMgr.retain();
cc.eventManager.addListener(new jsb.EventListenerAssetsManager(this._mAssetsMgr,this._eventListener.bind(this)),1);
},
_eventListener:function(event)
{
cc.log("assetsMgr:state = %s;event:id = %s,code = %s,message = %s",
this._mAssetsMgr.getState(),event.getAssetId(),
event.getEventCode(),JSON.stringify(event.getMessage()));
switch(event.getEventCode())
{
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: // 0
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: // 1
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST: // 2
case jsb.EventAssetsManager.ERROR_UPDATING: // 7
case jsb.EventAssetsManager.ERROR_DECOMPRESS: // 10
// 此处可以另加一个this.onError(event.getEventCode())操作,不过个人感觉没必要,直接直接做失败结束处理
this.onFinish(false);
break;
case jsb.EventAssetsManager.UPDATE_FINISHED: // 8
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: // 4
this.onFinish(true);
break;
// 资源下载完成,进行游戏内资源更新(包括资源解压等)
case jsb.EventAssetsManager.ASSET_UPDATED: // 6
break;
// 此处checkUpdate()和 update()都会触发新版本发现操作(区别在于前者到此处任务已结束,后者会继续后续资源下载操作)
case jsb.EventAssetsManager.NEW_VERSION_FOUND: // 3
this._onFind();
break;
case jsb.EventAssetsManager.UPDATE_PROGRESSION: // 5
// 状态值8代表热更新资源下载中,添加此判断是筛选更新下载进度
if(this._mAssetsMgr.getState() == 8)
{
cc.log(event.getPercent());
this._onUpdate(event.getPercent());
}
break;
case jsb.EventAssetsManager.UPDATE_FAILED: // 9
this._mTryCount++;
if (this._mTryCount < this._mTryTimes)
{
this._mAssetsMgr.downloadFailedAssets();
}
else
{
this._mTryCount = 0;
this.onFinish(false);
}
break;
default:break;
}
},
/**
* 检测是否有热更新资源
*/
checkUpdate:function()
{
this._mAssetsMgr.checkUpdate();
},
_onFind:function()
{
// 系统在进行正式更新时,在下载project.manifest后仍旧会再次回调此处回调
if(this._mHadFind)
{
return;
}
this._mHadFind = true;
// 计算热更新资源大小,需在cocos提供的文件配置基础上自主增加groupSizes字段
var updateSize = 0.0;
try
{
var vSize = 0.0;
var pSize = 0.0;
var versionString = jsb.fileUtils.getStringFromFile(this._mWritePath+"version.manifest");
var projectString = jsb.fileUtils.getStringFromFile(this._mWritePath+"project.manifest");
if(versionString)
{
var vJson = JSON.parse(versionString);
for(var it in vJson.groupSizes)
{
vSize += parseFloat(vJson.groupSizes[it]);
}
}
if(projectString)
{
var pJson = JSON.parse(projectString);
for(var it in pJson.groupSizes)
{
pSize += parseFloat(pJson.groupSizes[it]);
}
}
updateSize = vSize - pSize;
}
catch (e)
{
cc.log(e);
}
var layer = new HotFixFindLayer(updateSize);
cc.director.getRunningScene().addChild(layer);
},
/**
* 执行热更新
*/
doUpdate:function()
{
this._mAssetsMgr.update();
},
/**
* 设置热更新下载进度回调
* @param call
* @param target
*/
setUpdateCall:function(call,target)
{
this._mUpdateCall = call;
this._mUpdatetarget = target;
},
_onUpdate:function(percent)
{
this._mUpdateCall&&this._mFinishtarget&&this._mFinishtarget.runAction(cc.callFunc(this._mUpdateCall, this._mFinishtarget, percent));
},
/**
* 热更新结束
* @param success bool更新是否成功
*/
onFinish:function(success)
{
this._mFinishCall&&this._mFinishtarget&&this._mFinishtarget.runAction(cc.callFunc(this._mFinishCall, this._mFinishtarget, success));
this._mAssetsMgr.release();
this._mHadFind = false;
}
};
- 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
- 34
- 35
- 36
- 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
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
/**
* 热更新找到选择对话框,主要考虑到资源比较大,又不是强制更新的情况
*/
var HotFixFindLayer = cc.Layer.extend({
ctor:function(updateSize)
{
this._super();
var popBoxBg = new cc.LayerColor(cc.color(0, 0, 0, 200),this.width*0.75, this.height*0.15);
popBoxBg.setPosition(this.width*0.5-popBoxBg.width/2, this.height*0.5-popBoxBg.height/2);
this.addChild(popBoxBg);
var HotUpdateFindDesc = new ccui.Text("从游戏服务器检测到有"+updateSize+"M资源更新,是够立即进行更新操作?", "Arial", 28);
HotUpdateFindDesc.setContentSize(popBoxBg.width*0.85, popBoxBg.height*0.7);
HotUpdateFindDesc.setPosition(popBoxBg.width*0.5, popBoxBg.height*0.5);
HotUpdateFindDesc.ignoreContentAdaptWithSize(false);
popBoxBg.addChild(HotUpdateFindDesc);
var sureItem = new cc.MenuItemLabel(ccui.Text("立即更新", "Arial", 28),function(){
HotFixManager.doUpdate();
popBoxBg.removeAllChildren(true);
var percentDesc = new ccui.Text("更新进度:0%", "Arial", 32);
percentDesc.setAnchorPoint(0,0.5);
percentDesc.setPosition(popBoxBg.width*0.25, popBoxBg.height*0.5);
popBoxBg.addChild(percentDesc);
HotFixManager.setUpdateCall(function(target,data){
percentDesc.setString(cc.formatStr("更新进度:%s%",parseInt(data)));
if(parseInt(data) == 100)
{
this.removeFromParent(true);
HotFixManager.setUpdateCall();
}
},this);
},this);
var sureMenu = new cc.Menu(sureItem);
sureMenu.setPosition(popBoxBg.width*0.25, popBoxBg.height*0.2)
popBoxBg.addChild(sureMenu);
var cancelItem = new cc.MenuItemLabel(ccui.Text("下次更新", "Arial", 28),function(){
this.removeFromParent(true);
HotFixManager.onFinish(false);
},this);
var cancelMenu = new cc.Menu(cancelItem);
cancelMenu.setPosition(popBoxBg.width*0.75, popBoxBg.height*0.2)
popBoxBg.addChild(cancelMenu);
}
});
- 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
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
其实这个更新操作的实现方式不是特别符合设计模式,在HotFixManager的_onFind函数尾部
var layer = new HotFixFindLayer(updateSize);
cc.director.getRunningScene().addChild(layer);
- 1
- 2
是不该直接实现界面显示的,应该通过使用时自己定制一个弹窗界面然后再根据玩家选择调用HotFixManager对应的函数进行操作,不过我的初始想法就是让使用尽量方便,所以我尽量去掉了资源的使用,当成了一个系统内的控件进行了尽量的封装操作。
2、热更新系统使用
新建一个scene使用即可hotfix-scene.js:
var HotFixScene = cc.Scene.extend({
ctor:function()
{
this._super();
var layer = new cc.Layer();
this.addChild(layer);
var loadPoster = new cc.Sprite("res/static/poster.png");
loadPoster.setPosition(layer.width*0.5,layer.height*0.5)
loadPoster.setScale(layer.height/loadPoster.height);
layer.addChild(loadPoster);
// 热更新系统使用
HotFixManager.init(this._updateFinish,this);
HotFixManager.checkUpdate();
},
_updateFinish:function()
{
var delay_func = function()
{
// js文件列表文件,此文件在热更新过程中应该保持路径不变
// 因为此处是写死的,不太好改变,除非你自己在热更新配置一个字段,用于保存此路径
var jsFile = "src/dynamic/js-list.js";
// 加载js文件列表
cc.loader.loadJs(jsFile, function(){
// 加载js文件列表中的js文件
cc.loader.loadJs(jsList, function(){
// 跳转到正式游戏scene
cc.director.runScene(new GameScene());
});
});
}
// 此操作是为了避开当前帧跳转,不然界面会有卡顿感觉
this.scheduleOnce(delay_func, 0.05);
}
});
- 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
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
热更新系统的使用是特别简单的,只需要在main.js的cc.game.onStart函数执行
cc.director.runScene(new HotFixScene());
- 1
就可以了。当然这两个文件你要配置在project.json中。
3、热更新系统应用于已完成项目
其实我真正想做到的是作为一个插件使用,对于已经完成的项目,要添加此热更新插件特别简单,只需要做几件事情就可以了:
1、将原有的project.json中的js文件列表写进”src/dynamic/js-list.js”文件的var jsList=[]数组中(jsList名字对应于HotFixScene中的jsList)。
2、将上述两个文件加入游戏,并将其路径配置到project.json。
3、配置自己的热更新文件配置(包括搭建自己的服务器),修改HotFixManager.init()中的读写目录(若跟我的目录一致则不用改写)。
4、修改热更新结束后的scene跳转为已实现的文件跳转。
// 跳转到正式游戏scene
cc.director.runScene(new GameScene());
- 1
- 2
5、测试运行。
————————————————朴实无华分割线,以下内容不重要————————————————
下面是两张实际运行图:(背景图借用皇室战争的海报图)
选择界面:
资源更新中界面:
结束
为什么还要再写一篇关于cocos2d js热更新的笔记,最单纯的想法就是记录心得,另外也是因为添加了一个记录热更新资源大小的小功能,故而想分享一下。
在cocos2d js引擎中的js是作为一种特殊的资源文件使用(脚本文件),那么游戏运行过程中通过定制的资源管理器从服务器获取新的js文件并重新加载运行,那么也就实现了看似比较玄妙的热更新功能。当然此资源管理器需要具备资源对比、资源下载、资源处理、资源覆盖及资源加载等等的一系列功能,幸运的是cocos已经实现了这些功能,我们需要做的就是学会配置使用而已。
此热更新基于cocos2d-js 3.6.1测试使用。
————————————————朴实无华分割线,以上内容不重要————————————————
cocos2d js的热更新功能主要由jsb.AssetsManager实现,绑定了引擎底层的C++代码AssetsManager类,具体的逻辑和实现都可以查看此类。具体的热更新的是通过一个预先配置的project.manifest以及服务器端的project.manifest和version.manifest配合使用来实现的。
1、热更新系统实现
初始project.manifest:
{
"packageUrl": "http://192.168.0.73:8080/projectName/std/",
"remoteManifestUrl": "http://192.168.0.73:8080/projectName/std/project.manifest",
"remoteVersionUrl": "http://192.168.0.73:8080/projectName/std/version.manifest",
"version": "1.0.0",
"groupVersions" :
{
},
"groupSizes" :
{
},
"engineVersion": "3.6.1",
"assets" :
{
},
"searchPaths":
[
]
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
具有两个版本增量更新的服务器project.manifest:
{
"packageUrl": "http://192.168.0.73:8080/projectName/std/",
"remoteManifestUrl": "http://192.168.0.73:8080/projectName/std/project.manifest",
"remoteVersionUrl": "http://192.168.0.73:8080/projectName/std/version.manifest",
"version": "1.1.2",
"groupVersions" :
{
"1":"1.1.1",
"2":"1.1.2"
},
"groupSizes" :
{
"1":115.0,
"2":115.0
},
"engineVersion": "3.6.1",
"assets" :
{
"update1":
{
"path":"update1.zip",
"md5":"",
"compressed" : true,
"group":"1"
},
"update2":
{
"path":"update2.zip",
"md5":"",
"compressed" : true,
"group":"2"
}
},
"searchPaths":
[
"update1/",
"update2/"
]
}
- 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
- 34
- 35
- 36
- 37
- 38
- 39
具有两个版本增量更新的服务器version.manifest:
{
"packageUrl": "http://192.168.0.73:8080/projectName/std/",
"remoteManifestUrl": "http://192.168.0.73:8080/projectName/std/project.manifest",
"remoteVersionUrl": "http://192.168.0.73:8080/projectName/std/version.manifest",
"version": "1.1.2",
"groupVersions" :
{
"1":"1.1.1",
"2":"1.1.2"
},
"groupSizes" :
{
"1":115.0,
"2":115.0
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
此热更新配置文件我添加了groupSizes字段,用于展示更新资源文件大小,方便用户玩家自主选择更新时机。另外配置的时候按照渠道划分的,这个是std是标准渠道的热更新文件配置。至于其他字段网路上已经有太多的讲解,就不再重复。
资源文件配置完毕,那么就是具体的使用了,我使用了一个js的静态类来实现热更新的检测以及更新过程,我看其他人的实现一般都是直接强制更新,个人感觉不太友好,所以我的热更新实现是分步进行的。
hotfix-manager.js
/**
* 热更新管理器
*/
var HotFixManager = HotFixManager || {
// 更新资源管理器
_mAssetsMgr:null,
_mTryCount:0,
// 更新基本配置
_mManiFest:"",
_mWritePath:"",
_mTryTimes:1,
// 更新过程进度回调函数
_mUpdateCall:null,
_mUpdatetarget:null,
// 更新完成回调
_mFinishCall:null,
_mFinishtarget:null,
// 更新已找到标识
_mHadFind:false,
/**
* 热更新系统初始化
* @param call 更新完成回调函数
* @param target 回调函数绑定节点
*/
init:function(call,target)
{
this._mFinishCall = call;
this._mFinishtarget = target;
// 这一部分的配置使用也可以通过传递参数进行实现
this._mManiFest = "res/static/project.manifest";
this._mWritePath = jsb.fileUtils?jsb.fileUtils.getWritablePath()+"hotFix/":"./hotFix/";
this._mTryTimes = 1;
this._mAssetsMgr = new jsb.AssetsManager(this._mManiFest, this._mWritePath);
this._mAssetsMgr.retain();
cc.eventManager.addListener(new jsb.EventListenerAssetsManager(this._mAssetsMgr,this._eventListener.bind(this)),1);
},
_eventListener:function(event)
{
cc.log("assetsMgr:state = %s;event:id = %s,code = %s,message = %s",
this._mAssetsMgr.getState(),event.getAssetId(),
event.getEventCode(),JSON.stringify(event.getMessage()));
switch(event.getEventCode())
{
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: // 0
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: // 1
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST: // 2
case jsb.EventAssetsManager.ERROR_UPDATING: // 7
case jsb.EventAssetsManager.ERROR_DECOMPRESS: // 10
// 此处可以另加一个this.onError(event.getEventCode())操作,不过个人感觉没必要,直接直接做失败结束处理
this.onFinish(false);
break;
case jsb.EventAssetsManager.UPDATE_FINISHED: // 8
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: // 4
this.onFinish(true);
break;
// 资源下载完成,进行游戏内资源更新(包括资源解压等)
case jsb.EventAssetsManager.ASSET_UPDATED: // 6
break;
// 此处checkUpdate()和 update()都会触发新版本发现操作(区别在于前者到此处任务已结束,后者会继续后续资源下载操作)
case jsb.EventAssetsManager.NEW_VERSION_FOUND: // 3
this._onFind();
break;
case jsb.EventAssetsManager.UPDATE_PROGRESSION: // 5
// 状态值8代表热更新资源下载中,添加此判断是筛选更新下载进度
if(this._mAssetsMgr.getState() == 8)
{
cc.log(event.getPercent());
this._onUpdate(event.getPercent());
}
break;
case jsb.EventAssetsManager.UPDATE_FAILED: // 9
this._mTryCount++;
if (this._mTryCount < this._mTryTimes)
{
this._mAssetsMgr.downloadFailedAssets();
}
else
{
this._mTryCount = 0;
this.onFinish(false);
}
break;
default:break;
}
},
/**
* 检测是否有热更新资源
*/
checkUpdate:function()
{
this._mAssetsMgr.checkUpdate();
},
_onFind:function()
{
// 系统在进行正式更新时,在下载project.manifest后仍旧会再次回调此处回调
if(this._mHadFind)
{
return;
}
this._mHadFind = true;
// 计算热更新资源大小,需在cocos提供的文件配置基础上自主增加groupSizes字段
var updateSize = 0.0;
try
{
var vSize = 0.0;
var pSize = 0.0;
var versionString = jsb.fileUtils.getStringFromFile(this._mWritePath+"version.manifest");
var projectString = jsb.fileUtils.getStringFromFile(this._mWritePath+"project.manifest");
if(versionString)
{
var vJson = JSON.parse(versionString);
for(var it in vJson.groupSizes)
{
vSize += parseFloat(vJson.groupSizes[it]);
}
}
if(projectString)
{
var pJson = JSON.parse(projectString);
for(var it in pJson.groupSizes)
{
pSize += parseFloat(pJson.groupSizes[it]);
}
}
updateSize = vSize - pSize;
}
catch (e)
{
cc.log(e);
}
var layer = new HotFixFindLayer(updateSize);
cc.director.getRunningScene().addChild(layer);
},
/**
* 执行热更新
*/
doUpdate:function()
{
this._mAssetsMgr.update();
},
/**
* 设置热更新下载进度回调
* @param call
* @param target
*/
setUpdateCall:function(call,target)
{
this._mUpdateCall = call;
this._mUpdatetarget = target;
},
_onUpdate:function(percent)
{
this._mUpdateCall&&this._mFinishtarget&&this._mFinishtarget.runAction(cc.callFunc(this._mUpdateCall, this._mFinishtarget, percent));
},
/**
* 热更新结束
* @param success bool更新是否成功
*/
onFinish:function(success)
{
this._mFinishCall&&this._mFinishtarget&&this._mFinishtarget.runAction(cc.callFunc(this._mFinishCall, this._mFinishtarget, success));
this._mAssetsMgr.release();
this._mHadFind = false;
}
};
- 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
- 34
- 35
- 36
- 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
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
/**
* 热更新找到选择对话框,主要考虑到资源比较大,又不是强制更新的情况
*/
var HotFixFindLayer = cc.Layer.extend({
ctor:function(updateSize)
{
this._super();
var popBoxBg = new cc.LayerColor(cc.color(0, 0, 0, 200),this.width*0.75, this.height*0.15);
popBoxBg.setPosition(this.width*0.5-popBoxBg.width/2, this.height*0.5-popBoxBg.height/2);
this.addChild(popBoxBg);
var HotUpdateFindDesc = new ccui.Text("从游戏服务器检测到有"+updateSize+"M资源更新,是够立即进行更新操作?", "Arial", 28);
HotUpdateFindDesc.setContentSize(popBoxBg.width*0.85, popBoxBg.height*0.7);
HotUpdateFindDesc.setPosition(popBoxBg.width*0.5, popBoxBg.height*0.5);
HotUpdateFindDesc.ignoreContentAdaptWithSize(false);
popBoxBg.addChild(HotUpdateFindDesc);
var sureItem = new cc.MenuItemLabel(ccui.Text("立即更新", "Arial", 28),function(){
HotFixManager.doUpdate();
popBoxBg.removeAllChildren(true);
var percentDesc = new ccui.Text("更新进度:0%", "Arial", 32);
percentDesc.setAnchorPoint(0,0.5);
percentDesc.setPosition(popBoxBg.width*0.25, popBoxBg.height*0.5);
popBoxBg.addChild(percentDesc);
HotFixManager.setUpdateCall(function(target,data){
percentDesc.setString(cc.formatStr("更新进度:%s%",parseInt(data)));
if(parseInt(data) == 100)
{
this.removeFromParent(true);
HotFixManager.setUpdateCall();
}
},this);
},this);
var sureMenu = new cc.Menu(sureItem);
sureMenu.setPosition(popBoxBg.width*0.25, popBoxBg.height*0.2)
popBoxBg.addChild(sureMenu);
var cancelItem = new cc.MenuItemLabel(ccui.Text("下次更新", "Arial", 28),function(){
this.removeFromParent(true);
HotFixManager.onFinish(false);
},this);
var cancelMenu = new cc.Menu(cancelItem);
cancelMenu.setPosition(popBoxBg.width*0.75, popBoxBg.height*0.2)
popBoxBg.addChild(cancelMenu);
}
});
- 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
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
其实这个更新操作的实现方式不是特别符合设计模式,在HotFixManager的_onFind函数尾部
var layer = new HotFixFindLayer(updateSize);
cc.director.getRunningScene().addChild(layer);
- 1
- 2
是不该直接实现界面显示的,应该通过使用时自己定制一个弹窗界面然后再根据玩家选择调用HotFixManager对应的函数进行操作,不过我的初始想法就是让使用尽量方便,所以我尽量去掉了资源的使用,当成了一个系统内的控件进行了尽量的封装操作。
2、热更新系统使用
新建一个scene使用即可hotfix-scene.js:
var HotFixScene = cc.Scene.extend({
ctor:function()
{
this._super();
var layer = new cc.Layer();
this.addChild(layer);
var loadPoster = new cc.Sprite("res/static/poster.png");
loadPoster.setPosition(layer.width*0.5,layer.height*0.5)
loadPoster.setScale(layer.height/loadPoster.height);
layer.addChild(loadPoster);
// 热更新系统使用
HotFixManager.init(this._updateFinish,this);
HotFixManager.checkUpdate();
},
_updateFinish:function()
{
var delay_func = function()
{
// js文件列表文件,此文件在热更新过程中应该保持路径不变
// 因为此处是写死的,不太好改变,除非你自己在热更新配置一个字段,用于保存此路径
var jsFile = "src/dynamic/js-list.js";
// 加载js文件列表
cc.loader.loadJs(jsFile, function(){
// 加载js文件列表中的js文件
cc.loader.loadJs(jsList, function(){
// 跳转到正式游戏scene
cc.director.runScene(new GameScene());
});
});
}
// 此操作是为了避开当前帧跳转,不然界面会有卡顿感觉
this.scheduleOnce(delay_func, 0.05);
}
});
- 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
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
热更新系统的使用是特别简单的,只需要在main.js的cc.game.onStart函数执行
cc.director.runScene(new HotFixScene());
- 1
就可以了。当然这两个文件你要配置在project.json中。
3、热更新系统应用于已完成项目
其实我真正想做到的是作为一个插件使用,对于已经完成的项目,要添加此热更新插件特别简单,只需要做几件事情就可以了:
1、将原有的project.json中的js文件列表写进”src/dynamic/js-list.js”文件的var jsList=[]数组中(jsList名字对应于HotFixScene中的jsList)。
2、将上述两个文件加入游戏,并将其路径配置到project.json。
3、配置自己的热更新文件配置(包括搭建自己的服务器),修改HotFixManager.init()中的读写目录(若跟我的目录一致则不用改写)。
4、修改热更新结束后的scene跳转为已实现的文件跳转。
// 跳转到正式游戏scene
cc.director.runScene(new GameScene());
- 1
- 2
5、测试运行。
————————————————朴实无华分割线,以下内容不重要————————————————
下面是两张实际运行图:(背景图借用皇室战争的海报图)
选择界面:
资源更新中界面:
结束