之前一直从事android开发,接触cocos creator不久。近期公司安排我研究大厅子游戏模式的热更新,前后花了近一周时间,在论坛上查资料,请教大神,期间得到了一些帮助,在此很感谢cocos中文论坛里的前辈们。

谈到cocos creator的子游戏的热更新,相较于其他,诸如cocos-js,cocos2d-x等开发模式是有本质区别的。究其原因,是由于creator项目中无论是脚本或是资源,对整体项目而言,都是数据文件。由creator生成的项目是纯粹的数据驱动而非代码驱动,一切皆为数据,包括场景、脚本、图片、音频、动画等。而数据驱动需要有一个入口,即是creator编译器为我们生成的main.js文件,当然除了main.js这个入口文件,还有描述整个资源的配置文件settings.js,而我们自己编写的脚本,在编译后统一存放在了文件project.js中。代码驱动模式下的子游戏热更只需要更新资源以及项目的代码文件,很好理解。而在数据驱动模式下,开发者开发的项目是纯数据,要想运行这份数据,必须要入口文件(main.js)以及配置文件(setting.js),这就是两种模式在子游戏热更新领域的本质区别。

基于上述特性,在官方热更新的基础上(具体详见官方文档),我们开辟了一条子游戏独有的更新思路,那就是将编译生成的main.js文件一并移入src目录,让其成为数据的一部分,在子游戏更新的时候会下载完整的res、src目录,其中就囊括了我们执行所需的main.js文件,在运行子游戏的时候,我们只需要require main.js这个文件即可。

对于构建所生成的main.js文件,显然并非是我们想要的,因为它会加载默认的配置,对于整个原生应用而言,默认配置即为大厅的配置路径,所以我们必须修改main.js文件,使其找到子游戏自身的整个游戏环境配置文件,以下是子游戏main.js文件的精简代码,基本上注释已经很完整,写这篇文章的目的不是为了写实现流程,因为大厅子游戏模式的实现流程论坛中已有较为完整的demo,这篇文章主要是分析子游戏的两个进出文件,另外是想说明在真实项目中运用这种方式会遇到哪些问题,以及我们怎么处理这些问题。

(function () {

  //以下两句主要作用是释放资源,垃圾回收,个人感觉作用不大,有或者没有可 

    //能对内存有一点影响,释放不会很干净

cc.loader.releaseAll();

cc.sys.garbageCollect();

//以下这句话官方解释是fix bug ,不过实际上似乎没什么用,在这加着也无妨

cc.director.startAnimation();

'use strict';

var settings = null;

//以下是定义子游戏的路径,与大厅中热更新的某个子游戏路径对应

cc.INGAME = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + “ALLGame/         subgame/4/“;

//以下的逻辑判断做了下配置加载缓存,第二次进入就不重复加载setting.js 与   project.js了 很有必要

if (!cc.subgame4) {

console.log("首次加载路径名称-------" + cc.INGAME);

//这里的require,论坛中没有说清楚,在1.5.2版本中creator可以通过var settings =require(‘…..')这种方式给settings赋值,但是后面引擎组对require                   //做了修改,后续版本中当require(‘…’)后,去取变量要看require对应的文件中的代码而定.

require(cc.INGAME + 'src/settings.js');

cc.subgame4 = settings = window._CCSettings;

console.log("首次加载成功-------" + JSON.stringify(cc.subgame4));

require(cc.INGAME + 'src/project.js');

} else {

console.log("非首次加载路径名称-------" + cc.INGAME);

console.log("非首次加载成功-------" + JSON.stringify(cc.subgame4));

settings = cc.subgame4;

}

//将_ccsettings置位undefined,main.js都是这么写的

window._CCSettings = undefined;

//以下代码就是处理setting.js中的资源了,debug模式与非debug模式是不一样的,下面的代码都是在非debug模式下的

var uuids = settings.uuids;

var rawAssets = settings.rawAssets;

var assetTypes = settings.assetTypes;

var realRawAssets = settings.rawAssets = {};

for (var mount in rawAssets) {

var entries = rawAssets[mount];

var realEntries = realRawAssets[mount] = {};

for (var id in entries) {

var entry = entries[id];

var type = entry[1];

// retrieve minified raw asset

if (typeof type === 'number') {

entry[1] = assetTypes[type];

}

// retrieve uuid

realEntries[uuids[id] || id] = entry;

}

}

var scenes = settings.scenes;

for (var i = 0; i < scenes.length; ++i) {

var scene = scenes[i];

if (typeof scene.uuid === 'number') {

scene.uuid = uuids[scene.uuid];

}

}

var packedAssets = settings.packedAssets;

for (var packId in packedAssets) {

var packedIds = packedAssets[packId];

for (var j = 0; j < packedIds.length; ++j) {

if (typeof packedIds[j] === 'number') {

packedIds[j] = uuids[packedIds[j]];

}

}

}

//游戏的启动函数

var onStart = function () {

cc.view.resizeWithBrowserSize(true);

// UC browser on many android devices have performance issue with retina display

if (cc.sys.os !== cc.sys.OS_ANDROID || cc.sys.browserType !== cc.sys.BROWSER_TYPE_UC) {

cc.view.enableRetina(true);

}

if (cc.sys.isMobile) {

if (settings.orientation === 'landscape') {

cc.view.setOrientation(cc.macro.ORIENTATION_LANDSCAPE);

}

else if (settings.orientation === 'portrait') {

cc.view.setOrientation(cc.macro.ORIENTATION_PORTRAIT);

}

// qq, wechat, baidu

cc.view.enableAutoFullScreen(

cc.sys.browserType !== cc.sys.BROWSER_TYPE_BAIDU &&

cc.sys.browserType !== cc.sys.BROWSER_TYPE_WECHAT &&

cc.sys.browserType !== cc.sys.BROWSER_TYPE_MOBILE_QQ

);

}

// Limit downloading max concurrent task to 2,

// more tasks simultaneously may cause performance draw back on some android system / brwosers.

// You can adjust the number based on your own test result, you have to set it before any loading process to take effect.

if (cc.sys.isBrowser && cc.sys.os === cc.sys.OS_ANDROID) {

cc.macro.DOWNLOAD_MAX_CONCURRENT = 2;

}

//初始化资源方法,下面的路径就是子游戏目录下的,所以要加上cc.INGAME

cc.AssetLibrary.init({

libraryPath: cc.INGAME + 'res/import',

rawAssetsBase: cc.INGAME + 'res/raw-',

rawAssets: settings.rawAssets,

packedAssets: settings.packedAssets,

md5AssetsMap: settings.md5AssetsMap

});

var launchScene = settings.launchScene;

if (cc.runtime) {

cc.director.setRuntimeLaunchScene(launchScene);

}

// load scene

cc.director.loadScene(launchScene, null,

function () {

cc.loader.onProgress = null;

console.log('Success to load scene: ' + launchScene);

}

);

};

// jsList,脚本的路径也要加cc.INGAME

var jsList = settings.jsList;

var bundledScript = settings.debug ? cc.INGAME + 'src/project.dev.js' : cc.INGAME + 'src/project.js';

if (jsList) {

jsList = jsList.map(function (x) {

return cc.INGAME + 'src/' + x;

});

jsList.push(bundledScript);

}

else {

jsList = [bundledScript];

}

var option = {

id: 'GameCanvas',

scenes: settings.scenes,

debugMode: settings.debug ? cc.DebugMode.INFO : cc.DebugMode.ERROR,

showFPS: settings.debug,

frameRate: 60,

jsList: jsList,

groupList: settings.groupList,

collisionMatrix: settings.collisionMatrix,

renderMode: 0

};

//运行游戏

cc.game.run(option, onStart);

cc.sys.garbageCollect();

})();

再来看dating.js文件

(function () {

cc.loader.releaseAll();

cc.sys.garbageCollect();

'use strict’;

//这里只针对android的路径做了修改,因为我发现论坛中如果定义cc.INGAME=“”,会找不到主大厅资源,ios没有测

cc.INGAME = "assets/";

require(cc.INGAME+'src/settings.js');

var settings = window._CCSettings;

window._CCSettings = undefined;

var uuids = settings.uuids;

var rawAssets = settings.rawAssets;

var assetTypes = settings.assetTypes;

var realRawAssets = settings.rawAssets = {};

for (var mount in rawAssets) {

var entries = rawAssets[mount];

var realEntries = realRawAssets[mount] = {};

for (var id in entries) {

var entry = entries[id];

var type = entry[1];

// retrieve minified raw asset

if (typeof type === 'number') {

entry[1] = assetTypes[type];

}

// retrieve uuid

realEntries[uuids[id] || id] = entry;

}

}

var scenes = settings.scenes;

for (var i = 0; i < scenes.length; ++i) {

var scene = scenes[i];

if (typeof scene.uuid === 'number') {

scene.uuid = uuids[scene.uuid];

}

}

var packedAssets = settings.packedAssets;

for (var packId in packedAssets) {

var packedIds = packedAssets[packId];

for (var j = 0; j < packedIds.length; ++j) {

if (typeof packedIds[j] === 'number') {

packedIds[j] = uuids[packedIds[j]];

}

}

}

var onStart = function () {

cc.view.resizeWithBrowserSize(true);

// UC browser on many android devices have performance issue with retina display

if (cc.sys.os !== cc.sys.OS_ANDROID || cc.sys.browserType !== cc.sys.BROWSER_TYPE_UC) {

cc.view.enableRetina(true);

}

if (cc.sys.isMobile) {

if (settings.orientation === 'landscape') {

cc.view.setOrientation(cc.macro.ORIENTATION_LANDSCAPE);

}

else if (settings.orientation === 'portrait') {

cc.view.setOrientation(cc.macro.ORIENTATION_PORTRAIT);

}

// qq, wechat, baidu

cc.view.enableAutoFullScreen(

cc.sys.browserType !== cc.sys.BROWSER_TYPE_BAIDU &&

cc.sys.browserType !== cc.sys.BROWSER_TYPE_WECHAT &&

cc.sys.browserType !== cc.sys.BROWSER_TYPE_MOBILE_QQ

);

}

// Limit downloading max concurrent task to 2,

// more tasks simultaneously may cause performance draw back on some android system / brwosers.

// You can adjust the number based on your own test result, you have to set it before any loading process to take effect.

if (cc.sys.isBrowser && cc.sys.os === cc.sys.OS_ANDROID) {

cc.macro.DOWNLOAD_MAX_CONCURRENT = 2;

}

// init assets

cc.AssetLibrary.init({

libraryPath: cc.INGAME + 'res/import',

rawAssetsBase: cc.INGAME + 'res/raw-',

rawAssets: settings.rawAssets,

packedAssets: settings.packedAssets,

md5AssetsMap: settings.md5AssetsMap

});

//我们游戏大厅就一个场景,所以无论是直接定义还是使用settings文件中的launchScene都是可以的,如果大厅包含两个场景,那需要这种方式了,不过做这种热更方式,不建议大厅有多场景,可能会遇到意想不到的结果.

var launchScene = "db://assets/scene/MainScene.fire";

// load scene

if (cc.runtime) {

cc.director.setRuntimeLaunchScene(launchScene);

}

cc.director.loadScene(launchScene, null,

function () {

cc.loader.onProgress = null;

console.log('Success to load scene: ' + launchScene);

}

);

};

// jsList

var jsList = settings.jsList;

var bundledScript = settings.debug ? cc.INGAME + 'src/project.dev.js' : cc.INGAME + 'src/project.js';

if (jsList) {

jsList = jsList.map(function (x) {

return cc.INGAME + 'src/' + x;

});

jsList.push(bundledScript);

}

else {

jsList = [bundledScript];

}

var option = {

id: 'GameCanvas',

scenes: settings.scenes,

debugMode: settings.debug ? cc.DebugMode.INFO : cc.DebugMode.ERROR,

showFPS: settings.debug,

frameRate: 60,

jsList: jsList,

groupList: settings.groupList,

collisionMatrix: settings.collisionMatrix,

renderMode: 0

};

cc.game.run(option, onStart);

cc.sys.garbageCollect();

})();

以上是从大厅进入子游戏的main.js与子游戏回大厅的dating.js的精简版,当然还可以更精简。

我们利用代码运行两个helloword子游戏时是不会存在任何问题的,一旦把实际项目集成进来时问题就逐一显现了,下面来一一列举。

问题一:从大厅进入一个子游戏,退回大厅,再进另一个子游戏时,画面整个错乱

引发原因:两个子游戏中,有部分资源uuid重复,很可能是在开发一个游戏的时候,导出了该游戏的部分资源或场景进入了第二个游戏。

解决方法:  https://github.com/lategege/CocosUpdate下载uuid.php这个脚本,放入assets目录下运行 php uuid.php 将项目中所有的uuid替换,前提是事先要安装php环境,注意脚本中 $QIANZIU = “b11”; 每一个子游戏都需要改一下这个值,因为这个脚本会把所有uuid的前三位替换为在此定义的三位。

问题二:修改了uuid觉得万事大吉的时候,那就想的太简单了,你忽然发现进了一个游戏,进另外一个黑了。

原因:场景、脚本、节点名称重复,程序没有重新载入。

解决办法:每个游戏非公有的代码的脚本名称,组件名称以及场景全部改名,建议以子游戏名称打头。

问题三:运行了一个游戏后,再运行另一个游戏,某些组件为null,声音还在播放。

原因:返回游戏时事件没有反注册,事件监听器中存在组件实例。垃圾回收后,组件实例被回收,但事件监听器中引用仍然存在,下一个游戏发了同一条事件后,就会报错,声音资源没有被释放。

解决办法:返回游戏时,清空事件监听器,释放声音资源。

问题四:全局变量被覆盖。

原因:两个游戏中存在相同名称全局变量,值不同,导致游戏运行会有问题。

解决办法:每个游戏的全局变量必须保证名称不一致。

问题五:回大厅发现存在子游戏的画面。

原因之一:定时任务没有在返回时关闭。

解决办法:查找游戏中的每一个定时任务,在返回时记得关闭任务。

以上问题都解决完之后似乎可以不冲突运行了,伴随而来的是内存的压力,不过无所谓,问题肯定还会有,不过已经少了。

对上述问题的的总结:在公司项目确定要使用这种方案来做子游戏更新时,劝大家评估一下项目命名是否规范,是否能容忍进出游戏的缓慢,对项目命名的建议是:公共代码保证所有游戏一样时,无需对代码及变量改名,一旦公共代码载入内存,不会加载第二次,所以不用理会,同时在公共代码中不要保存任何非公共的变量以及对象引用。对于非公共部分,要保证资源名称,js脚本名称以及场景、节点名称尽量使用子游戏名称打头来命名,然而即使都没问题了,进入游戏与返回大厅的速度依然是硬伤。

另外看到论坛中http://forum.cocos.com/t/topic/54899 子游戏+大厅共享资源临时实现方案,此方案通过修改jsb_polyfill.js 代码,也就是定义几个全局变量,这种方式在creator 1.8.2中行不通,进大厅就黑屏,creator1.7.2可行。这种方式确实能访问到主大厅的资源,甚至能通过加载场景的方式进出子游戏,而且速度非常快,与加载一般场景的速度没什么区别,但是这其中的问题也非常多,就我遇到的,重启大厅后进子游戏,子游戏都打不开了,还有一些资源的丢失问题,要使用这种方式需要对引擎有足够多的了解,小弟不才,对cocos引擎研究不深。

无论是require方式还是通过修改jsb_polyfill.js方式,都有弊病,这种非官方的加载方式能否商用,值得商榷,至少我觉得真想做creator大厅子游戏模式还是等待官方更新比较靠谱,以上是我一个礼拜的cocos子游戏更新的心得,借此分享一下。

cocos creator 大厅+子游戏模式探讨(creator版本1.8.2)相关推荐

  1. CocosCreator 2.4.3热更新实现方案(AssetBundle),大厅+子游戏模式快速实现

    _ 实现功能 项目环境 关于 Asset Bundle 实现过程 工程结构 快速使用代码 构建发布 Creator 构建 制作热更新资源 制作随包发布模块 测试功能 模块完整下载 windows下测试 ...

  2. Cocos Creator子游戏动态下载实现(大厅+子游戏模式)

    热更新 在App开发过程中,当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就会忙得焦头烂额:重新打包App.测试.向各个应用市场和渠道换包.提示用户升级.用户下载.覆盖 ...

  3. cocos creator 大厅 ➕ 子游戏(子游戏作为单独的项目更新)

    先标记上文章的出处,嘿休嘿,感谢这位:https://www.jianshu.com/p/fe54ca980384 cocos creator版本: 1.9.1 首先建立大厅的项目 添加文件  Sub ...

  4. CocosCreator大厅+子游戏+热更新方案

    转载自:https://www.jianshu.com/p/efee9f5937a3 前言 随着游戏的玩法越来越多,也就意味着包体越来越大,对于玩家来说,首次下载的包体就会越来越大,从而也会增加首次启 ...

  5. cocos creator大厅、子游戏实现方案

    参考cocos论坛:http://forum.cocos.com/t/1-5-2-demo/48200 demo: https://github.com/zhangjiangyi/HallAndChi ...

  6. 网狐荣耀6701/6801服务端 子游戏编译 部署

    1.前言 工作需要,接触到网狐的代码,因为没接触过vc和cocos经过一番才搭建起来,记录起来供大家参考. 服务端包括系统模块(协调服务器.聊天服务器.登录服务器.游戏服务器),网站(网狐前台.后台. ...

  7. java游戏开发毕业论文_基于JAVA的五子游戏的开发设计毕业论文

    基于JAVA的五子游戏的开发设计毕业论文 毕业论文基于 JAVA 的五子游戏的开发设计山东凯文科技职业学院摘 要人工智能是一门正在迅速发展的新兴的,综合性很强的交叉科学.它与生物工程.空间技术一起被并 ...

  8. OpenSource的开发模式探讨

    为什么80%的码农都做不了架构师?>>>    OpenSource的开发模式探讨zt http://mail.mailer.com.cn:81/article/articlevie ...

  9. Qt Creator 插件开发(1):编译 Qt Creator

    2019独角兽企业重金招聘Python工程师标准>>> 从技术架构角度来说,Qt Creator 可以看做是一个插件平台,其大部分功能都是以插件的形式提供的. 本系列文章将着重介绍如 ...

最新文章

  1. matlab振动频谱分析是不是要,VB和Matlab混编实现振动信号的频谱分析
  2. 深度学习和浅层学习 Deep Learning and Shallow Learning
  3. nginx_keepalived配置(转载保存)
  4. [Turn]C# 强制关闭当前程序进程(完全Kill掉不留痕迹)
  5. [LeetCode]Remove Nth Node From End of List
  6. 带头结点的单链表的初始化,建立,插入,查找,删除
  7. spring中bean属性scope
  8. kernel32.dll动态链接库报错解决方法win7,怎么修复kernel32.dll文件缺失
  9. python汉化 草蟒_草蟒首页、文档和下载 - Python 汉化版
  10. Hive精选高频面试题
  11. 【每日更新】万维钢精英日课3课程分享笔记:模糊逻辑:灰度认知,灰度决策,黑白执行
  12. 重复测量设计计算机结果分析,重复测量设计样本含量估计.pdf
  13. 方锥体积的计算公式以及定义 初中高中数学
  14. Python周刊518期
  15. 源码:Spark SQL 分区特性第一弹
  16. code flattening —— conversion to R1CS——formulation of QAP
  17. 第三类边界条件 matlab,matlab怎么解偏微分方程
  18. dva如何去掉hash
  19. 水果店的问题和风险,开水果店会遇到什么问题
  20. 瑞银:2030年Waymo将占自动驾驶出租车市场60% 汽车私有化可能会受到冲击

热门文章

  1. 诺基亚忘记锁码怎么办
  2. 微信怎么@所有人?必备苹果手机小技巧
  3. 【STM32】#147-D: declaration is incompatible 类错误的解决方式
  4. 机器学习入门案例:鸢尾花数据集分类 绘制PR曲线
  5. 数据分析包的比较:R,Matlab,SciPy,Excel,SAS,SPSS,Stata
  6. 工业机器人技术试题_工业机器人技术题库及答案
  7. 记一次lpk劫持样本分析
  8. 原《担保法》中规定的担保方式在《民法典》中的对应
  9. 【喜讯】Apache DolphinScheduler 荣获 “2020 年度十大开源新锐项目”
  10. centos8之U盘安装操作系统