Cocos Creator 2.0 之后推出了AssetBundle系统,类似于Unity3D的AssetBundle。先简单讲一下AssetBundle的原理作用,不涉及具体引擎的具体细节,各个游戏引擎之间也略有差异。
  AssetBundle是将游戏中使用的资源分成若干个包,这样做的意义在于减少安装包的体积,让用户快速下载并进入游戏,剩下的资源包在游戏过程中后台下载,甚至可以在需要使用某一整块的资源时再下载,节约用户的流量
  另外,可以给AssetBundle增加版本管理,这可以针对不同的玩家提供不同版本的资源,比如针对不同地区的玩家提供不同的语言包。同时,也能够热更新资源。
  在Unity3D中,C#是静态语言,安装之后就无法通过AssetBundle或其他方式更新脚本了(不采用动态库的情况下)。但是,依然可以对传统的文本、声音、图片、动画等资源更新,并且可以通过修改挂载在预制上的脚本中的参数,实现某些更新需求。腾讯提供XLua框架,C#+lua脚本的方式实现Unity3D下的热更新方案。但必须预先由C#提供好接口,并且两种语言开发,负担也不小。
  Cocos Creator 是支持javascript/typescript语言的,本身就是动态脚本运行,那么是否可以在不重启游戏的情况下实现热更新
  下面我以大厅+子游戏为需求背景,在Cocos 技术栈内,列举几个本人或设想或接触或实现的几种方式。

1. H5形式的子游戏
2. Cocos2d-x + lua
3. Cocos Creator 分包下载资源
4. Cocos Creator 新版本支持AssetBundle

 1. H5形式的子游戏

  每个子游戏以网页游戏的方式提供,因为是网页版,所以不存在热更新问题,只要保证新的文件与旧的需要替换的文件名不同就行,一般是在文件名中包含文件的md5。大厅可以选择多种形式,原生安卓可以,cocos工程可以,其实微信小游戏也可以算这种形式,只是运行环境不是原生的浏览器环境。
 
  这种形式有以下优点:
  1. 子游戏只需要提供网页版,不需要嵌入到大厅,可以自由独立开发,不拘泥于引擎,如果是第三方提供子游戏内容,这是最简单的方法。
  2. 非嵌入式,子游戏之间不会相互影响,也不会影响到大厅功能。
 
  缺点:
  1. 如果子游戏和大厅之间存在互动,如子游戏打开大厅的某些界面,要事先提供好接口。
  2. 打开子游戏时间较长,如果是游戏引擎制作的游戏,启动引擎可能就需要几秒种时间。
  3. 如果不作预下载,游戏体验可能不佳,游戏中的图片可能要一段时间才能加载出来。
 
  总结:这种方式适合游戏盒子这类推广平台采用,门槛低,游戏大厅互动少。
 

 2. Cocos2dx + lua

  据了解是目前很多游戏公司使用的方案,个人没有使用过2dx的经验,但是接触过这种框架的项目,这里的优缺点比较可能片面或存在误解,欢迎有经验的同学留言补充,如果是实际项目考量,还请移步Cocos官网查看文档。
 
  优点:
  1. 每个子游戏相对独立,可以实现不重启热更新。
  2. C++和lua的性能还是高于js的。
 
  缺点和U3d中C#+lual类似,这里提点不同的:
  1. Cocos官方目前主推Creator,从文档顺序和质量就可以看出,不排除以后放弃维护的可能。
  2. Creator制作流程更简单快速,也更符合游戏引擎的发展规律,所见即所得。
 
  总结:没有用过2d-x,就不瞎总结了,不过跟着官方走,更容易一些。
 

 3. Cocos Creator 分包下载

  这是工作中实现的方案,主要解决的是分包下载资源,而不是热更新问题。
  首先Creator会把所有的脚本文件(除插件)编译为一个js文件,这就不容易实现每个子游戏独立热更新脚本。当时已有的热更新方案是,在原生端将新的资源下载到指定目录,将目录添加到Cocos资源搜索路径列表下,再启动Cocos引擎。脚本占用资源很少,但其他资源较大,因此,子游戏的脚本在原生端初始时热更新是可以接受的,资源则分成若干个文件夹,并生成对应版本号,需要时下载到热更新目录下。
 
  优点:对原工程侵入性不大,只需要合理挪动资源。
  缺点:资源管理难度加大。
 

 4. Cocos Creator AssetBundle

  Cocos修改了打包机制,所有脚本不再编译成一个js文件,而是每个AssetBundle各自拥有一个。这就意味这脚本可以实现热更新(js中一切都是对象,当新的脚本导入运行时,覆盖之前的对象,即实现了更新)。
  而且引入(整理)了资源管理系统,虽然要手动增减引用计数(在这之前需要自己实现),比起之前,使用起来更加可控。
  在测试过程中,发现如果类添加了@ccclass标签,这个类就无法实现更新,这是因为cocos内部会将有这个标签的类注册管理起来,当加载新的同名类时,不会再注册。在不修改源码的情况下,采用的做法是所有脚本都不使用@ccclass标签,这样就不能直接挂载在预制上,但是可以采用addComponent的方式,在代码中动态挂载。
  这样在每次进入子游戏之前下载子游戏的AssetBundle,如果子游戏需要热更新,只需要下载完成之后,重新进入子游戏,就能脚本代码和资源都是最新内容。
  除了子游戏可以做成单个Bundle,游戏内所有相对独立的部分都可以做成Bundle,甚至大厅也可以是一个Bundle,游戏中可以只留一个最小的启动部分,这样几乎可以完全替代原先的热更新方案。
  另外需要注意的。
  同时使用AssetBundle和原热更新方案,需要两次打包。原先热更新方案打包时是不能勾选md5的,因为启动脚本main.js中是写明了需要导入的脚本名的,如果勾选了md5,热更新打出的脚本名和原先main.js中指定的脚本名不符合,无法启动游戏。而AssetBundle的下载及热更新是基于md5+文件名的,所以需要先勾选md5并将远程AssetBundle包保存出来,再取消勾选md5,打包原生工程。如果只是热更新,打一次包就足够了。 新版本cocos creator 中main.js 不会添加md5文件,所以现在原生端可以勾选md5出包。
  可以将本地AssetBundle包更新为远程包。如果将AssetBundle的方式作为热更新的主要方式,那么大厅也要作为一个AssetBundle存在,但是安装包内希望包含大厅的内容,不然用户很快安装完游戏,进去之后发现还要漫长等待下载大厅资源。因此,在出安装包时选择大厅作为本地包,出热更新时作为远程包,我们还需要另外提供本地包版本和远程包版本作比较。
 
  优点:
  1. 无需重启热更新,体验流程。
  缺点:
  1. 资源管理难度加大。
  2. 要求代码模块引用更加小心谨慎。
 

如何降低资源管理难度?

  资源分包后,用户的体验好了,但是对资源管理这一块的难度也加大了。这个管理分为开发时管理和运行时管理。
  对于运行时管理,随着设备内存的提升,一般规模的游戏很难达到内存崩溃,而资源占用的内存是不易泄漏的,游戏引擎通常会保证一份资源在内存中只有一份拷贝。我们只要关心那些极大的资源,如我们所愿的加载释放即可。我们要小心的是,正在使用的资源被其他地方释放的问题,对于小资源和常用资源,可以不释放。
  而开发时管理是非常难以简化的,这里有两个思路:

  1. 开发时生成资源引用关系。如果是动态引用资源,可能会统计不到,需要额外处理。
  2. 运行是生成资源使用关系。比如进入大厅时,一共加载了那些资源,记录下来,进入一个子游戏后,又加载了哪些资源。这种方式是第三方游戏发行平台,为游戏分包用的,不需要关心代码,直接看运行结果。Unity3D也有类似的功能,之前还是预览功能,不确定现在是否合入正式版本。这种方式缺点也很明显,有些不常用的资源就会记录不到,使用顺序也不一定准确,准确率和游戏运行次数相关。

ps: 欢迎大家指出文中错误和不足,提出意见,以免误导。

更多推荐

Cocos Creator AssetBundle 游戏分包方案评估