# 前言
最近接到一个新需求,需要使用 CocosCreator 完成微信小游戏和字节小游戏的代码与资源热更,并完成对应平台的分包需求。好久没碰 Cocos 了,那就来开心的调研一下吧~🙈
# 热更篇
# 代码热更
Cocos 是自带热更解决方案的,详情参考这里。然而,Pay Attention,这里的解决方案是基于原生平台的,即 Android、IOS、Windows 和 Mac。由于微信小游戏是基于微信小程序平台的,微信小程序的 io 与基本文件 io 是不一样的,微信粑粑把你 ban 了不允许你进行文件读写操作,不然还要审核机制干什么。那么有没有什么解决方案呢,答:有滴,那就是自己写解释器,参考这里,哈哈哈,原地放弃好嘛。
总结:由于平台原因,暂时放弃小游戏端代码热更
# 资源热更
呜呜呜,既然代码不能热更了,那资源总能热更吧,不然总共就 20M 的包体怎么放得下啊。ok,Cocos 提供了远程包的解决方案,可以将资源包放在远端提供下载,诶嘿,具体实现方式就往下看吧~
# 分包配置篇
微信小游戏不同于其他手机游戏,为了实现快速加载对于包体有一定的限制,要求点如下:
- 小游戏的主包体积不能超过 4MB,主包 + 分包不能超过 20MB,包含所有代码和资源,额外的资源必须通过网络请求下载
- 对于小游戏包内资源,小游戏环境内并不是按需加载的,而是一次性加载所有包内资源,然后再启动页面
- 不可以从远程服务器下载脚本文件
为啥要提出分包这个概念呢?原因是为了让玩家更快进入游戏,微信小游戏是偏向轻度类型的游戏,为了让玩家更快地开始游戏所以限制了主包 4M 的大小却没限制单个分包的大小,即分包可以有多个也可以只有一个,只要主包加分包一共不超过 20M 就行啦。
字节小游戏的分包要求与微信小游戏有些不同,总包体还是 20M 不变,但单个分包要求也不能超过 4M,也就是说最多存在 1-4 个分包
# AssetBundle
在介绍如何配置分包之前先聊一聊 Cocos 的资源包吧
从 v2.4 开始,Creator 正式支持 Asset Bundle 功能。Asset Bundle 作为资源模块化工具,允许开发者按照项目需求将贴图、脚本、场景等资源划分在多个 Asset Bundle 中,然后在游戏运行过程中,按照需求去加载不同的 Asset Bundle,以减少启动时需要加载的资源数量,从而减少首次下载和加载游戏时所需的时间。
通常一个 cocos 项目内置了 main
、 resources
、 start-scene
这三个 Asset Bundle,用户也可通过点击文件夹勾选右侧的 Is Bundle
来配置文件夹为一个 bundle 包,用户可通过 cc.assetManager.loadBundle
来加载资源包,并加载资源包内的具体资源
assetManager.loadBundle('bundle name', (err, bundle) => { | |
bundle.load('asset name'); | |
}); |
注意:bundle 包拥有优先级设置,共用同一个资源的时候,cocos 会根据优先级设置,在构建发布时将该资源优先放在优先级高的 bundle 中,如果两个优先级相同,那么会分别拷贝这个资源到这两个 bundle 包中,造成资源浪费,因此,在配置 bundle 包时请优先考虑包的优先级,避免资源浪费,低优先级的资源在用到高优先级资源时请确保高优先级资源已被加载,避免报错
想了解更多请看官方文档这里
# 配置小游戏分包
选择要配置为分包的文件夹,在资源管理器勾选 配置为 Bundle
,目标平台设置为 微信小游戏
,压缩类型设置为 小游戏分包
好啦,小游戏分包配置完成啦,在选择微信小游戏平台构建发布后,所生成的 bundle 包就会出现在 wechatgame/subpackages/xxx
下
注意:分包资源虽然会随着主包一起上传至微信开发者平台,但在小游戏启动的时候是只会加载主包的资源的,如果想使用分包资源的话需要自己去手动加载 bundle 包
想了解更多请看官方文档这里
# 配置远程包
选择要配置为分包的文件夹,在资源管理器勾选 配置为 Bundle
,目标平台设置为 微信小游戏
,勾选 配置为远程包
,构建发布配置勾选 MD5缓存
,填入 远程资源服务器地址
就好啦
好啦,配完啦,构建发布完成后就会在 wechatgame/remote/xxx
下生成 bundle 包,把你的远端包放到远端上就好啦 (放到远端后,本地的文件就可以删啦),是不是很简单,对,就是这么简单~
注意,远程包中的代码是无法被动态解析执行的!!!
# 一个小栗子
介绍了这么多枯燥的内容,那么来举一个小小的栗子吧~😘
项目结构如下:
base 主包文件夹
mini 分包文件夹
remote 远端包文件夹
主包 test.ts
this.btn.node.on(Button.EventType.CLICK,()=>{ | |
// 点击按钮加载 mini 分包,随后切换至 mini 场景 | |
assetManager.loadBundle('mini',null,(err, bundle)=>{ | |
bundle.loadScene('mini',(err, sceneAsset) => director.runScene(sceneAsset)); | |
}); | |
}); |
- 分包 mini.ts
start() { | |
// 获取 mini 包内的图,由于 test.ts 中已经加载过 mini 包了,我们也没释放,所以直接 getBundle 就好了 | |
assetManager.getBundle('mini').load('ship1/spriteFrame', SpriteFrame, (err, sp) => { | |
if (err) { | |
console.error(err) | |
return | |
} | |
this.ship.spriteFrame = sp | |
}) | |
// 加载远端包 | |
var url = 'http://127.0.0.1:5502/remote'; | |
assetManager.loadBundle(url, null, (err, bundle) => { | |
if (err) { | |
console.error(err) | |
return | |
} | |
console.log("加载远程包成功") | |
bundle.load('depot_close/spriteFrame', SpriteFrame, (err, sp) => { | |
if (err) { | |
console.error(err) | |
return | |
} | |
this.img.spriteFrame = sp | |
}) | |
setTimeout(()=>{ | |
// 切换至远端包场景 | |
bundle.loadScene('remote',(err, sceneAsset) => director.runScene(sceneAsset)); | |
},1000) | |
}); | |
} |
注意:上方在加载图片时都路径基础上加了 /spriteFrame
, 这是因为在打成 bundle 包的时候,图片路径默认是 ImageAsset
格式的,类型转换要加上对应的格式后缀
- 远端包 remote.ts
start() { | |
console.log("成功啦") | |
} |
✔️简单打印一句话,成功输出日志,证明挂在预制的脚本是能被加载运行的
- 远端包 inst.ts
export class inst { | |
constructor(){ | |
} | |
public getStr():string{ | |
return "233333333" | |
} | |
} |
在 remote.ts 中加入
var inst = new inst(); | |
console.log(inst.getStr()) |
💔运行报错,inst is not a constructor,证明 inst 无法被编译执行
补充:在微信开发者工具加载远程包时可能会加载失败,这是由于工具默认开启了域名校验,在右上方设置 -> 本地设置 -> 勾选不校验合法域名再试一下就行啦。
在 Web 端的远程加载受到浏览器的 CORS 跨域策略限制,如果对方服务器禁止跨域访问,那么会加载失败,而且由于 WebGL 安全策略的限制,即便对方服务器允许 http 请求成功之后也无法渲染。
# 缓存管理器
在加载远端包的时候,由于存在缓存机制加载出来的资源可能依旧是旧资源,那么该怎么办呢~
- 方法 1
微信开发者工具上方清缓存->全部清除
- 方法 2
构建发布配置勾选MD5缓存
- 方法 3
代码释放
//remote 远端包 | |
var url = 'http://127.0.0.1:5502/remote'; | |
// 遍历当前所有缓存文件,清除掉包含某个远端包路径的资源 | |
assetManager.cacheManager.cachedFiles.forEach((v,k)=>{ | |
if(k.indexOf(url)>=0){ | |
assetManager.cacheManager.removeCache(k); | |
} | |
}) |
想了解更多请看官方文档这里