提交 78bda207 编写于 作者: O oceanxiao

migrate

上级 24c59bb1
.DS_Store
**/.DS_Store
\ No newline at end of file
# 使用Android CPU Profiler性能调优
1. 在Android微信小游戏打开调试进行录制
<image src='../image/androidprofile1.png' width="300"/>
2. 停止性能数据录制
与步骤1相似,在相同的菜单中选择Stop CPU Profile
3. 传输录制文件到PC
录制结束后,Android会生成一份xxx.cpuprofile,该文件格式可以使用chrome进行解析。
因此我们需要将录制后的文件传输到PC使用chrome进行分析。
文件路径通常为:Android/data/com.tencent.mm/MicroMsg/appbrand/trace
<image src='../image/androidprofile2.png' width="300"/>
4. 利用PC(Windows/Mac)的chrome加载数据
<image src='../image/androidprofile3.png' width="500"/>
5. 使用JavaScriptProfile进行数据分析
<image src='../image/androidprofile4.png' width="700"/>
注意:仅当导出Develop版本时才能在函数堆栈中看到函数名称,否则为函数ID,此时有两种做法进行解读:
1. 通过webgl导出目录下的symbols文件对照映射
2. 通过[替换脚本](../tools/update_v8_wasm_profile.py)对cpuprofile进行自动映射到真实函数。使用方式:python update_v8_wasm_profile.py $cpuprofile $symbol
\ No newline at end of file
# 资源优化
可通过转换工具配套提供的资源优化工具,将游戏内纹理资源**针对webgl导出**做优化。
## 工具入口
菜单栏-微信小游戏-资源优化工具
![入口](../image/asset-optimization/entrance.png)
## 工具介绍
### Texture
![资源处理工具面板](../image/asset-optimization/asset-analysis-window.png)
1. 区域1: 选中目录,以设定规则扫描目录下文件。
搜索规则
- 已开启MipMap: 搜索已开启了MipMap的纹理
- NPOT: 搜索非POT图片
- isReadable: 搜索已开启readable纹理
- MaxSize大于512: 搜素MaxSize大于512纹理
2. 区域2: 以设定规则修复列表中选中的资源
**注意: 每次修复时将当前所要修复资源的importer覆盖写入到Library/AssetImporterbak; 还原时,从Library/AssetImporterbak读取选中资源的importer设置并还原后,会删除Library/AssetImporterbak,并不是undo/redo逻辑**
- 禁用readable:关闭readable
- 禁用mipmap:关闭mipmap
- 优化maxsize:勾选后,可选择优化maxsize的方式
![maxsize](../image/asset-optimization/change-max-size.png)
其中,自动减半根据当前纹理尺寸,将maxsize设置为纹理宽高最大值的一半。
- change texture format: 勾选后可修改纹理压缩格式
![textureformat](../image/asset-optimization/change-format.png)
3. 区域3: 筛选列表展示结果
4. 区域4: 列表总览
5. 区域5: 资源列表
支持一键全选、按资源名搜索
6. 区域6: 引用查找
选中一个资源,查找资源被引用关系
## 优化建议
- 小游戏平台下不需要太大的纹理,合理设置纹理maxsize可以减小纹理资源大小,最大不要超过1024。
- MipMap: UI不需要开启mipmap,开启会增大1/3内存。
- read/write enabled: 一般不需要访问纹理像素, 并且unity也是默认关闭。开启后会占用GPU显存和CPU内存。
- NPOT纹理无法使用压缩纹理,注意修改纹理尺寸。
\ No newline at end of file
# 音频优化
由于导出成WEBGL的游戏,音频处理上存在一定的性能损耗,建议直接调用SDK中的音频API直接控制播放,能达到不错效果。
### API使用
代码如下,可参考[微信开发者文档](https://developers.weixin.qq.com/minigame/dev/api/media/audio/InnerAudioContext.html),其中src为音频地址,可填本地路径如 Assets/xx.wav,运行时会自动和配置的音频地址前缀做拼接得到最终线上地址。
* 使用方法一:
为了减少用户等待时间和避免音频延迟,
对于重要的音频可以等待`onCanplay`事件之后再调用`Play`播放。onCanplay事件是表示,当前音频已经可以播了,可以直接掉用`Play`,不会有延迟。而对于没那么重要的音频,允许部分延迟的,可以不等待onCanplay事件,而直接调用`Play`方法,这样音频就会边加载边播放,效果取决于用户网络,可能会稍微有点延迟。如果希望等音频全部加载了再播放,以及便于后续复用,避免后续延迟的话,可以在初始化是加上needDownload = true,
```
// 第一次使用
var music = WX.CreateInnerAudioContext(new InnerAudioContextParam()
{
src = "Audio/0.wav",
needDownload = true
});
music.OnCanplay(() =>
{
music.Play();
});
// 后续需要需要同时创建多个音频实例时,可以直接Play
var music2 = WX.CreateInnerAudioContext(new InnerAudioContextParam()
{
src = "Audio/0.wav"
});
music2.Play();
```
* 使用方法二:
如果想直接将用到的音频先完全下载避免延迟,用到的时候直接使用,则可以如下调用:
```
string[] a = { "Assets/Audio/0.wav", "Assets/Audio/1.wav" }; //需要预下载列表
WX.PreDownloadAudios(a, (int res) =>
{
//下载完成后回调
Debug.Log("Downloaded" + res);
if (res == 0)
{
//可以直接调用,无网络延迟
var music0 = WX.CreateInnerAudioContext(new InnerAudioContextParam()
{
src = "Audio/0.wav"
});
music0.Play();
var music1 = WX.CreateInnerAudioContext(new InnerAudioContextParam()
{
src = "Audio/1.wav"
});
music1.Play();
}
});
```
`注意` WX.CreateInnerAudioContext 返回的音频对象是可以复用的,就是可以多次调用Play方法播放,但是如果需要多个音频同时播放就要创建多个音频对象了。
### 导出设置
勾选使用微信音频API,并填上"Assets目录对应CDN地址",比如填写的地址为https://wx.qq.com/data/Assets/,而API的src地址为 Audio/Chill_1.wav,则最终会请求https://wx.qq.com/data/Assets/Audio/Chill_1.wav
### 将音频文件上传CDN
游戏内的音频会被导出到导出目录的Assets文件夹下,需要将其上传至您的CDN,保证用户能访问到
![avatar](../image/assets2.png)
\ No newline at end of file
# 后端服务指引
## 后端服务模式
微信小游戏对后端服务无任何限制,可以根据自身需求选择,比如:
1. 自建后端:支持 websocket、HTTP 通信
2. 云开发:云开发可以降低运维、研发成本,详见[云开发-指引](https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/)
3. 1、2 混合的模式
## 自建后端服务
登录态参考[登录态管理](https://developers.weixin.qq.com/minigame/dev/guide/open-ability/login.html)
通过 websocket 或者 HTTP 通信,理论上 App 和微信小游戏大部分后端服务可以直接复用
## 云开发
以实现一个服务端校验内容安全的云函数为例对云开发流程进行简单介绍,更多文档和示例请参考[云函数文档](https://developers.weixin.qq.com/minigame/dev/guide/)
> 注意:此示例仅供参考云函数使用流程,正常情况下对内容进行安全校验的逻辑应该是内容校验通过+内容存储到后端在同一个云函数中实现,避免直接调用存储接口绕开校验的情况出现。
1. 新建一个云函数的空工程
云函数可以和前端的游戏逻辑独立创建为不同的项目。当然也可以在同一个项目中(project.config.json 中指定 cloudfunctionRoot,避免云函数代码被业务逻辑代码一同打包发布到外网),以下示例为独立项目的模式
<image src='../image/cf/init-cf-proj.jpg' width="800"/>
<image src='../image/cf/cf-overview.png' width="800"/>
2. 该小游戏首次使用云开发需要先开通云开发
<image src='../image/cf/open-cf.jpg' width="800"/>
<image src='../image/cf/cf-free.jpg' width="800"/>
> 云开发有一个基本的免费套餐,请求量达到一定上限后需要[配额调整](https://developers.weixin.qq.com/minigame/dev/wxcloud/billing/quota.html)或[申请代金券后更换配额套餐](https://developers.weixin.qq.com/minigame/dev/wxcloud/billing/voucher.html)
3. 实现一个云函数
以“内容安全检查”云函数(msgSecCheck)为例,复制示例中 login 文件夹,重命名为 msgSecCheck,修改 index.js 中的内容实现服务端逻辑:
```Javascript
/*
* // msgSecCheck
* message Request {
* string content;
* }
* message Response {
* int code; // 返回码, 0:内容正常; 1:参数非法; 87014: 内容含有违法违规内容
* }
*/
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
function Response(code) {
return {
code,
}
}
exports.main = async (event, context) => {
if (!event.content) return Response(1);
try {
// https://developers.weixin.qq.com/minigame/dev/api-backend/open-api/sec-check/security.msgSecCheck.html
await cloud.openapi.security.msgSecCheck({
content: event.content,
});
return Response(0);
} catch (e) {
return Response(e.errorCode);
}
}
```
修改 config.json,增加对 openapi.security.msgSecCheck 的声明,如没有用到 openapi 则无需修改 config.json:
```
"permissions": {
"openapi": [
"security.msgSecCheck"
]
}
```
> openapi 更多使用细节参考[云调用文档](https://developers.weixin.qq.com/minigame/dev/wxcloud/guide/openapi/openapi.html#%E4%BA%91%E8%B0%83%E7%94%A8)
4. 部署云函数
- 云函数可以同时存在多个环境(可自定义,如测试环境、生产环境。本示例为"product"),选中需要部署的云函数环境:
<image src='../image/cf/choose-env.jpg' width="800"/>
- 选中需要部署的云函数(本示例为"msgSecCheck"),点击“创建并部署:云端安装依赖”(本云函数仅依赖了 wx-server-sdk 模块,选云端安装依赖即可;若使用了其他第三方 node 模块,则需要选“创建并部署:所有文件”)
<image src='../image/cf/upload-cf.png' width="800"/>
- 等云函数部署完成(看工具的提示),Console 可以输入调用云函数代码进行简单测试,或者右键云函数-开启云函数本地调试
```Javascript
wx.cloud.init({env:"product"})
wx.cloud.callFunction({
name: 'msgSecCheck',
data: {
content: "hello cf"
},
success:function(res) {
console.log("call success", res);
const isContentOk = (res.result && res.result && res.result.code === 0);
console.log("IsContentOk", isContentOk)
},
fail: function(res){
console.log("call fail", res);
}
})
```
# 压缩纹理使用(Beta)
因为目前Unity导出的WEBGL游戏只支持DXT1和DXT5,在移动端上并不支持,而且除非使用Addressable这种将图片打包出来的手段,否则也会导致包体过大。为了解决这两个问题。我们提供了这个功能,让Unity游戏也能在移动端自动支持ASTC和ETC2的压缩纹理。而且图片也自动做了按需加载,无需再单独拆分打包。
`注意:` 使用压缩纹理的图片或者图集,宽高需要为2的幂次,否则不会转化为移动端的压缩纹理。对于需要动态读取图片内容做处理的,目前还不支持将其转为压缩纹理。目前Android上能正常使用压缩纹理,IOS上需要与我们联系确认。
## 安装ImageMagick
下载并安装[ImageMagick](https://imagemagick.org/index.php)
## Mac上授权
因为Mac上的隐私安全问题,使用mac的用户需要先授权,使用window的用户跳过这步。点击"微信小游戏"->"MacOS脚本授权",进行授权,直到不报错为止。若授权后还是报错,重启unity后再点授权试试。
## 使用Addressable或者AssetBundle的情况
1、首先点击"微信小游戏"->"转换小游戏" 打开转换小游戏弹窗,填入相关的导出配置信息,
2、点击"微信小游戏"->"压缩纹理"->"替换纹理",用到的纹理图片就会自动替换成占位符,展示位黑色的图片,如果项目用到的图片比较多这一步可能需要花费较长时间,比如10多分钟。
3、替换完毕后,再导出你的ab包。
4、在转换小游戏弹窗中点击导出。这里就不需要再勾选压缩纹理替换,否则会重复替换增加导出时间。
<image src="../image/edwindow.png" width="600"/>
5、导出后可以使用"微信小游戏"->"压缩纹理"->"恢复纹理"来恢复被替换的纹理图片。
这里替换纹理后在导出目录会生成一个Assets目录,需要将该目录放到CDN的对应目录, 其对应的是你填写的"Assets目录对应CDN地址" ,比如你填写的地址为 https://wx.qq.com/data/Assets/ ,那么小游戏需要展示图片的时候就会自动去该目录下载对应图片。
## 不使用Addressable和AssetBundle的情况
方法一:在转换小游戏弹窗中勾选上压缩纹理替换,然后导出。这种导出每次都会做一次纹理替换,可能花费较长时间。
方法二:同使用了Addressable的情况,可以先点击"微信小游戏"->"压缩纹理"->"替换纹理",替换纹理图片,再在转换小游戏弹窗中点击导出。这里就不需要再勾选压缩纹理替换,否则会重复替换增加导出时间。
`注意:`"选择图片资源目录"对应的默认为Assets目录,选择更小范围的目录能加快图片替换时间。对于一些不在该目录内的图片,图片format`不能`选择为DTX1和DXT5。
# 方案评估
## 填写评估表
我们为接入游戏提供了评估表,本文档用于阐述“评估表”中的事项以及项目在转换时可能遇到的问题,帮助项目组决定是否使用转换方案。
<image src='../image/evaluation1.png' width="200"/>
## 需要注意的事项
### 1、关于游戏类目
考虑到游戏体积与逻辑复杂度,目前建议中轻度2D/3D游戏进行转换,游戏类目包括:
* 休闲:消除,答题,模拟经营,塔防,益智等
* 动作:跑酷,飞行设计,轻度IO
* 棋牌:棋类,牌类
* 角色:卡牌,回合,策略
对重度游戏如MMO/FPS等游戏需根据实际情况评估
### 2. 关于引擎版本
本转换方案目前支持:
* Unity 2018
* Unity 2019
其他版本在未来也可支持,如果开发者有其他版本需求可与我们联系。
### 3. 开发需要具备的技术栈
* 前端:开发者可以继续使用Unity,编程语言C#,微信平台能力以C# SDK提供
* 后台:通常短连接使用HTTPS接入,长连接使用WSS。也可选择使用小程序云作为后端服务。
* H5开发经验:无需专门的H5经验,但需要简单了解下微信小游戏开发流程与工具
### 4. Unity WebGL在小游戏与浏览器环境的运行差异
* Android/PC(Windows):支持特性与浏览器一致,运行Unity WebGL的核心部分从性能上差别不大
* iOS:支持特性与浏览器一致,性能略低于Webview
建议开发者先将游戏导出为WebGL在浏览器上运行,如果没有严重的兼容性问题则可使用本方案。
### 5. 是否能使用第三方插件
可以,支持C#插件(有无源码均可)与C原生插件(需源码)
### 6. 渲染品质与还原度
在小游戏环境下,Unity WebGL导出格式的底层渲染特性支持WebGL 1.0(OpenGL ES2.0), 未来我们将支持2.0。WebGL 1.0的限制请查阅[WebGL 图形](https://docs.unity3d.com/cn/current/Manual/webgl-graphics.html).
### 7. 是否能使用热更
通常,app开发时大家会由于审核速度问题采用LUA/ILRuntime等热更方案。然而,在小游戏环境中不被允许。原因如下:
* 小游戏运营策略不允许动态变更代码,包括热更
* LUA/ILRuntime等热更方案在Unity WebGL性能急剧下降
### 8. 是否能使用物理引擎
可以,但由于Unity WebGL算力不如原生APP(约1/3),因此建议避免使用较重的物理计算,可结合Fixed Timestep控制计算频率。
### 9. 是否能使用多线程
不可以,Unity WebGL是单线程模式。
### 10. 是否能使用协程
可以
### 11. 动画/Drawcall的限制
复杂的动画与Drawcall对严重消耗CPU资源,如问题6所述受算力影响,开发者需根据实际项目进行Profile。
### 12. 包体大小限制
建议APP端资源体积不超过300MB的游戏进行转换,原因如下:
* 小游戏资源缓存大小为200MB
* 过大的游戏造成的加载缓慢容易导致用户流失
在此前提下,建议开发者根据后续启动性能优化指引进行启动时长的调优
### 13. 资源分包
小游戏环境同时支持AssetsBundle与Addressable资源加载方式
由于完整打包一次性下载会导致启动时间过长,如果原生APP未进行资源异步加载,需要开发者进行改造优化。
### 14. Unity音频支持
目前Unity WebGL的音频虽然能完整支持,在性能与内存占用上还存在问题,建议开发者在使用小游戏提供的音频SDK进行替换。
### 15. 网络接口
* HTTP/WWW:已支持
* TCP: 需替换成Websocket
* UDP: 暂未支持,如果开发者有此需求与我们联系
\ No newline at end of file
# 首场景启动优化
[提升Unity WebGL游戏启动速度](./StartupOptimization.md)章节我们提到过:
>在timelog中呈现的首场景耗时即为引擎初始化与开发者首帧逻辑,关于该阶段耗时,开发者需要注意的是:
>1. MonoBehaviour脚本的首帧Start/Awake应足够少逻辑,优先将画面呈现
>2. 初始场景不宜过大,通常呈现Splash场景即可
>3. 初始场景中需要后续主场景或配置加载时可采取分帧策略,切勿在Start/Awake阻塞
下面介绍如何通过在小游戏下通过Profile的方式对小游戏首场景耗时进行针对性优化。
## 小游戏Profile
微信小游戏在安卓平台提供了CPU Profile能力,使用姿势为:
1.采集Profile: 点击右上角菜单 -> 开发调试 -> Start CPU Profile -> Stop CPU Profile;
<img src="../image/firstscene/profile.png" width="300" /> <img src="../image/firstscene/stop.png" width="300" /> <img src="../image/firstscene/stopprofile.png" width="300" />
2.导出Profile文件
当点击Stop CPU Profile之后,会弹出浮窗告知profile文件的存储路径,通过USB链接电脑或者第三方文件管理器即可将Profile文件传输至电脑。
3.Chrome导入Profile文件
打开Chrome浏览器,右键审查元素或者按F12即可打开控制台界面。选中JavaScript Profile tab(如果默认没有,可能在右上角的三个点展开菜单->More tools里面),点击load导入步骤2的profile文件,Profile文件的使用可以参考Google的开发文档: https://developers.google.com/web/tools/chrome-devtools/rendering-tools/js-execution
<img src="../image/firstscene/chromeload.png" width="900" />
## Unity CallMain
参考文档[小游戏启动流程](./Startup.md)中提到的,当游戏代码包下载编译完成和首包资源下载完成后会运行游戏逻辑,最开始执行的逻辑是CallMain主函数。
在WebGL模式下,CallMain的主要是initWebGLPlayer调用,主要分成两部分调用:
- Unity引擎初始化:主要分成:InitializeIl2CppFromMain、RuntimeInitialize、PlayerInitEngineNoGraphics和PlayerInitEngineGraphics等函数调用;
- 首场景初始化:PlayerLoadFirstScene函数调用;
下表为针对Unity Benchmark项目在不同设备上的callmain耗时分析,Benchmark项目链接:https://github.com/Unity-Technologies/Benchmark
> 下面实验均采用Unity 2018.4.24f1版本,Development模式会对性能有一定影响,下表数据均为Development模式下数据。
| initWebGLPlayer主要函数 | 安卓魅族6s | OPPO A37m | 安卓小米10 | 开发者工具 |
| ---- | ---- | ---- | ---- | ---- |
| InitializeIl2CppFromMain | 525ms |954ms |113ms |39.90ms |
| RuntimeInitialize | 139ms | 649ms | 50.2ms | 2.33ms |
| PlayerInitEngineNoGraphics | 463.8ms | 1.24s | 154ms | 10.46ms |
| PlayerInitEngineGraphics | 1.4s | 2.94s | 403ms | 126ms |
| PlayerLoadFirstScene | **815ms**| **2.12s** | **213ms** |**70.75ms** |
目前为止,开发者可以操作的主要是PlayerLoadFirstScene函数的调用,参照Unity的Benchmark项目,要尽可能将PlayerLoadFirstScene的耗时控制在整个CallMain耗时的<font color="#dd0000">20%</font>左右,如果CallMain耗时过大,可以借助上面提到的Profile工具进行优化,下面简单分析流程。
## CallMain Profile分析
1.改造小游戏代码
因为安卓采集Profile文件需要手动控制,而CallMain会在码包下载编译完成和首包资源下载完成后就执行,为了录制启动阶段完整的Profile,需要保证Profile的录制开始之后才真正执行游戏开始逻辑,找到项目的game.js文件,改造最后几行即可:
```
// gameManager.startGame();
wx.onTouchStart((result) => {
console.log('真正执行开始游戏逻辑');
gameManager.startGame();
})
GameGlobal.manager = gameManager;
```
通过上面的改造,在进入小游戏后,可以按照这个流程采集Profile:Start CPU Profile -> 点击屏幕触发真正的开始游戏逻辑 -> 看到游戏界面后Stop Profile。
2.导入步骤1的启动过程Profile文件
<img src="../image/firstscene/benchmarkprofile.png" width="900" />
导入文件后,我们只需要关注CallMain调用的PlayerLoadFirstScene函数,如果PlayerLoadFirstScene耗时占比过高,大多是因为在首场景执行了不必要的操作,比如大量反序列化操作、配置解析操作,按需剔除后再次Profile即可验证效果。
\ No newline at end of file
# 调试错误与异常处理
本文描述Unity WebGL游戏运行过程中,如果遇到异常(业务代码、引擎)时开发者应该如何进行调试。
## 开启代码异常
<image src='../image/howtodebug1.png' width="600"/>
> 默认情况下,使用“转换插件”这两个选项都已设置完成。
需要说明的是:
* Enable Exception如果设置为"Full Without StackTrace"时将会步骤更多引擎运行时产生的异常。
* Enable Exception如果设置为"Full With StackTrace"时将会看到更多信息,但程序运行性能将受极大影响。
* Debug Symbols不会对程序性能造成影响,该选项建议一直开启,用于后面做异常源码映射。
## 异常产生时如何排查
由于Unity WebGL运行时是基于WebAssembly,该格式并非“人类可读”,直接对WebAssembly进行调试会非常困难。
因此我们建议开发者使用日志与异常堆栈进行问题排查。对于未知问题,开发者往往需要异常产生时的堆栈找到蛛丝马迹,下文将引导开发者如何去根据WebAssembly找到对应的游戏工程源码所在位置。
### 微信开发者工具
### 真机环境
1. 已验证Unity版本:2018.3/2018.4LTS、2019.2/2019.4LTS
安装时选择WebGL组件
2. 如果你用的是Big Sur版本的Mac系统,并且Unity 版本小于 2019.4.14, 则需另外安装 [python3](https://www.python.org/downloads/),并安装brotli 命令如下
```
python3 -m pip install brotli
```
## 二、Unity导出WebGL
1. 下载 [Unity插件](http://res.wx.qq.com/wechatgame/product/webpack/userupload/wasm_plugin/minigame.unitypackage), 并导入至游戏项目中
2. 添加需要导出的scene
<image src='../image/scene.png' width="600"/>
建议仅勾选Loading场景,后续场景使用AssetsBundle/Addressable进行按需加载。
## 三、适配小游戏
1. Mac上授权
因为Mac上的隐私安全问题,使用mac的用户需要先授权,使用window的用户跳过这步。点击"微信小游戏"->"MacOS脚本授权",进行授权,直到不报错为止。若授权后还是报错,重启unity后再点授权试试。
2. 选择 点击 微信小游戏 -> 转换小游戏, 填写相应参数,点击`导出WEBGL并转换为小游戏`按钮 ,等待转换完成。
<image src='../image/export.png' width="600"/>
其中:
* 必须:appid 为 小游戏的Appid
* 必须:游戏资源CDN为首包资源所在的HTTP或CDN目录
* 可选:视频url 用来作为初始加载阶段的封面视频,视频格式请参考[视频规范](Design/video.md)
* 可选:AB包CDN地址为StreamingAssets目录的上一级注意末尾加`/`,若没有用到AB包则不需要填
生成目录下 webgl目录为游戏对应的webgl版本,minigame目录为转化后的小游戏代码。
注意:
* 将生成目录下的`webgl/ProjectName.data.unityweb.bin.txt`上传至“游戏资源CDN”。您的CDN最好支持gzip压缩txt文件,这样能减少网络传输数据和时间。
* 项目使用了小游戏Unity适配插件,若小游戏是第一次使用本插件,在开发者工具会报错提示插件未授权,具体可参考[使用Loader进行游游戏加载](UsingLoader.md)
* 使用AssetsBundle时,请将资源上传到“AB包CDN地址”
至此,可以在[微信开发者工具](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html)打开转化后的小游戏进行预览。
## 四、使用脚本集成到自己的构建系统
如果你希望将导出插件集成到自己的发布流程,想脚本调用的话,可修改 Assets/WX-WASM-SDK/Editor/MiniGameConfig.asset配置,然后调用WXEditorWindow 的 DoExport方法导出小游戏
```
var win = new WXEditorWindow();
win.DoExport();
```
## 五、常见问题
1. 微信版本与运行时基础要求是什么?
* Android版本必须>=7.0.19
* iOS版本必须>=7.0.18
* 基础库版本必须>=2.14.0
具体支持情况参考[支持平台](Design/SupportedPlatform.md)
2. 为什么运行时出现奇怪的“Maximun call stack size exceeded.RangeError”或“Not Impletemented: Class::FromIL2CPPType”?
Unity WebGL 首包资源data文件含有metadata,如果data资源包和wasm对不上会有各种问题,尤其在**浏览器**运行时需清理IndexedDB中的缓存。
3. 为什么资源或网络请求在打开"vConsole"正常,关闭时下载失败?
网络请求必须**配置安全域名**:https://developers.weixin.qq.com/minigame/dev/guide/base-ability/network.html
打开"vConsole"时,小游戏默认不检查因此能请求通过。
4. 中文字体适配
字体必须打包到项目中,才能正常展示。
5. 音频被切到后台会停止
小游戏被切到后台会停止播放音频需代码中监听 [WX.OnShow](https://developers.weixin.qq.com/minigame/dev/api/base/app/life-cycle/wx.onShow.html)事件和 [WX.OnAudioInterruptionEnd](https://developers.weixin.qq.com/minigame/dev/api/base/app/app-event/wx.onAudioInterruptionEnd.html)事件,在该事件之后重新播放音频
# 输入法适配
在小游戏中Unity游戏唤不起输入法,需要使用WX_SDK中提供的方法来唤起输入法,并做简单的逻辑修改来适配。
如下以UGUI的Input组件为例,需要给Input 绑定以下脚本:
```
public class Inputs : MonoBehaviour,IPointerClickHandler,IPointerExitHandler
{
public InputField input;
public void OnPointerClick(PointerEventData eventData)
{
// 监听点击事件唤起微信输入法
WX.ShowKeyboard(new WXShowKeyboardParam()
{
// 这里的参数根据需要自行设置,参见https://developers.weixin.qq.com/minigame/dev/api/ui/keyboard/wx.showKeyboard.html
defaultValue = input.text,
maxLength = 20,
confirmType="go"
}) ;
//绑定回调
WX.OnKeyboardConfirm(OnConfirm);
WX.OnKeyboardComplete(OnComplete);
}
public void OnPointerExit(PointerEventData eventData)
{
// 隐藏输入法
if (!input.isFocused)
{
WX.HideKeyboard();
//删除掉相关事件监听
WX.OffKeyboardInput(OnInput);
WX.OffKeyboardConfirm(OnConfirm);
WX.OffKeyboardComplete(OnComplete);
}
}
public void OnInput(string v)
{
if (input.isFocused)
{
input.text = v;
}
}
public void OnConfirm(string v)
{
// 输入法confirm回调
if (input.isFocused)
{
}
}
public void OnComplete(string v)
{
// 输入法complete回调
if (input.isFocused)
{
}
}
void Start()
{
}
}
```
\ No newline at end of file
# 问题反馈与联系我们
开发者使用转换方案时,如果遇到问题可以随时反馈给我们。目前可通过[Git Issue](https://git.weixin.qq.com/wechat-minigame/unity-webgl-convert/issues)和“研发助手号”微信两种方式。
## Git Issue
开发者遇到BUG或者希望新增特性优先使用[Git Issue](https://git.weixin.qq.com/wechat-minigame/unity-webgl-transform/issues),因为这里可以更好进行问题沉淀、讨论与跟踪。
<image src='../image/issueandcontact1.png'/>
问题复现:
* 操作步骤
* 重现条件
* 最好有截图/数据报告
* 最好能提供非业务相关的可复现代码片段
## 小游戏研发助手
开发者也可以通过“研发助手号”直接与小游戏团队进行技术交流
<image src='../image/issueandcontact2.jpg' width="200"/>
# Unity中如何展示排行榜这类微信关系数据
### 1、设置占位纹理
在游戏需要展示的地方创建一个RawImage,其中Texture属性自己选择透明的图片即可,后续展示时会被动态替换。因为unity纹理与web的绘制存在倒立的差异,请将先将 rotation的x 设置为180,即让 `UI控件延X轴旋转180度` 再调整到游戏中合适的位置,如下图
![avatar](../image/o2.png)
### 2、调用展示SDK的API
在需要展示的地方调用,`WX.ShowOpenData`
```
WX.ShowOpenData(rawImage.texture, x, y, width, height);
```
其中 :
* x : 占位区域对应屏幕左上角横坐标
* y : 占位区域对应屏幕左上角纵坐标,注意左上角为(0,0)
* width : 占位区域对应的宽度
* height : 占位区域对应的高度
需要关闭时则调用,`WX.HideOpenData`
如果需要再Unity中向开放域页面传递数据,可以调用`WX.GetOpenDataContext`,如下代码:
```
var c = WX.GetOpenDataContext();
c.PostMessage("hahaha哈哈");
```
开放域JS代码可以通过:
`wx.onMessage`接收
```
wx.onMessage(data => {
console.log(data);
});
```
注意因为用户隐私问题,目前`只允许Unity向开放域发送通知`,开放域是不能向Unity里面发送通知的。
### 3、导出选项勾选使用好友关系链
![avatar](../image/opendata.png)
### 4、用JS开发排行榜这类微信关系逻辑
在转化后的小游戏中的open-data/index.js 文件中写你对应的业务逻辑即可,如下图所示。 这里的JS开发我们提供了一个简单易上手的
[绘制引擎](https://wechat-miniprogram.github.io/minigame-canvas-engine/),可以参看对应使用文档来绘制页面。而开放域的业务逻辑开发与普通小游戏的开放数据的开发没有差异,可以查看[官网](https://developers.weixin.qq.com/minigame/dev/guide/open-ability/open-data.html)做进一步了解。
![avatar](../image/o3.png)
\ No newline at end of file
# Unity WebGL小游戏适配方案性能标准
## 为什么需要性能评测标准?
性能评测标准希望能引导开发者优化相关性能数据,提升用户体验。
## 开发者需要关注哪些性能指标
从小游戏的运行周期来看,主要由启动和运行两个阶段产生性能问题。
启动阶段:启动时长,该数据将显著影响用户打开留存率
运行阶段:内存峰值、流畅度、CPU占用、网络等
其他兼容性问题,包括逻辑异常、黑屏等严重问题
## 评测标准细则
### 性能基线
评测小游戏性能首先需要确定性能基线, 即先确定机型设备条件。根据目前小游戏玩家机型情况建议以下机型:
Android:
高档机:小米9
中档机:VIVOX21
低档机:OPPO R9S
iOS:
高档机:iPhoneXS/iPhone11
中档机:iPhone8
低档机:iPhone6s
### 评测工具
启动性能:
使用网络模拟工具1MB/s条件,使用Unity适配插件的Loader加载框架提供TimeLog窗口获取性能数据。
运行性能:
PerfDog:游戏主流程对局5~10min, PerfDog记录性能数据并上传,取平均值,每种机型测试3组数据再取平均,内存峰值取最大值。
### 代码与资源体积
首包资源(压缩后): <5M
WASM代码(压缩后): <5M
### Android 评测标准
<image src='../image/perfmeasure3.png' width="500"/>
### iOS 评测标准
<image src='../image/perfmeasure4.png' width="500"/>
\ No newline at end of file
# 性能优化总览
## 一、为何需要进行性能优化?
Unity WebGL导出形式相对于原生APP应用,需要开发者更关注性能与体验调优。有以下几点原因:
1. 小游戏天生为"即开即用",在小游戏生态下玩家对启动耗时更敏感。
2. Unity WebGL底层基于WebAssembly,算力不及原生APP。
3. Unity并未对WebGL平台做特别裁剪,启动较慢。
因此,相对于原生APP,无论从启动还是运行上我们都需要做进一步性能优化。
## 二、优化目标
根据平台在不同游戏类型/机型下的评测,我们给出Unity WebGL小游戏可以参照的的[性能评估标准](Design/PerfMeasure.md),开发者依此对游戏的启动与运行性能进行调优。
## 三、最佳实践
### 3.1 加快游戏启动速度
* 编译选项中仅勾选首场景
* CDN必须开启gzip压缩
* 精简首场景物件,尽快渲染让玩家看到游戏首画面
* 减少初始化与首帧逻辑,首场景Awake/Start/首次Update不要包含过重逻辑
* 减少代码包体, 剔除不必要的插件
* 使用代码分包工具缩减WebAssembly首次下载包体
更多信息请参考:
* [提升Unity WebGL游戏启动速度](StartupOptimization.md)
* [首场景启动优化](FirstSceneOptimization.md)
* [代码分包](WasmSplit.md)
### 3.2 资源按需加载
* 尽量避免在各级Resource包含资源,该目录将被直接打包在首资源包
* 使用AssetsBundle/Addressable进行资源加载
* 单个包体最好不超过2MB
* 资源请求并发数不超过20个
更多信息请阅读:
* [使用Addressable进行资源按需加载](UsingAddressable.md)
### 3.3 资源处理建议
* 贴图maxsize尽量不超过1024,小游戏环境适当降低画质
* 贴图尽量不生成Mipmap
* 贴图尽量不使用可写属性
* 字体文件压缩前最大不超过4MB
更多信息请阅读:
* [资源优化工具与建议](AssetOptimization.md)
### 3.4 降低小游戏内存使用
* 不要初始化所有未使用的资源
* 释放不使用的资源
* 发布前使用压缩纹理工具进行优化
* 使用微信音频接口对Unity音频进行改造优化
更多信息请阅读:
* [压缩纹理优化](CompressedTexture.md)
* [Unity WebGL内存原理详解](https://gameinstitute.qq.com/community/detail/112321)
* [音频适配优化](AudioOptimization.md)
### 3.5 降低计算复杂度
* 尽量使用Android CPU Profiler在小游戏真机环境Profie计算瓶颈
* 提前在Unity环境使用Unity Profiler发现问题
* 物理计算较重的游戏使用Fixed Timestep控制计算频率
更多信息请阅读:
* [使用Android CPU Profiler性能调优](AndroidProfile.md)
* [使用Unity Profiler性能调优](UnityProfiler.md)
\ No newline at end of file
# 使用Loader进行游游戏加载
## 一、什么是Unity Loader?
Unity Loader是在微信小游戏环境加载Unity WebGL游戏的加载与适配器,使用微信小游戏插件技术开发,功能包括:
- WebAssembly代码分包加载与编译
- 资源加载与编译
- 启动进度显示
- 缓存策略
- 数据上报
## 二、如何使用Unity Loader插件
生成微信小游戏项目时,转换工具会在`game.json`中声明:
```json
"plugins": {
"UnityPlugin": {
"version": "major.minor.patch", // 可切换版本号
"provider": "wxe5a48f1ed5f544b7",
"contexts": [
{
"type": "isolatedContext"
}
]
}
}
```
这部分配置表示使用Unity Loader插件进行游戏加载,开发者无需手动修改
首次使用时,会提示添加插件,按提示添加即可
<image src="../image/addPlugin.png">
## 三、配置Unity Loader功能
### 3.1 封面视频
由于Unity WebGL启动加载需要一定时间,因此需要使用视频或图片等内容作为过渡以留住玩家。Unity Loader默认使用视频+进度信息呈现,开发者可以自定义封面视频,可参考[启动Loader视频规范](Design/video.md)进行配置。
### 3.2 预加载资源
为了充分利用网络带宽,在网络空闲时,比如下载完小游戏分包和首包资源后,可以预加载游戏需要用到的AB包。
``` js
let managerConfig = {
/* 省略其他配置 */
/**
* 假设: AB包打包到 path1/StreamingAssets/WebGL这个目录下; STREAMING_CDN是以path1为根路径上传到服务器的CDN地址
*/
// 需要在网络空闲时预加载的资源,支持如下形式的路径。
preloadDataList: [
// '$STREAM_CDN/StreamingAssets/WebGL/textures_8d265a9dfd6cb7669cdb8b726f0afb1e',
// '/WebGL/sounds_97cd953f8494c3375312e75a29c34fc2'
],
}
```
## 3.3 缓存策略
由于缓存目录最大不可超过200M,在下载资源包、下载AB包时,若剩余空间不足以缓存,会进行缓存淘汰。目前规则比较简单,如下:
1. 如果所需空间过大,超过最大限制:下载完成后不缓存文件,也不执行清理逻辑,直接返回下载内容。
2. 清理部分文件可以缓存新文件:按最近使用时间从前往后排序清理,直到清理出所需空间。
> 因为文件解压只能解压到用户目录,所以,若压缩文件过大,无法解压到用户目录时,会返回失败。
### 3.4 可交互视频
***这个配置目前不是对所有游戏适用***
可交互视频相比默认版本的启动页,交互性更强,用户可以通过操作(目前是点击)来切换画面。
可交互视频实际是多段视频,通过点击来切换视频。
视频是流式加载,但也需要平衡好视频质量和视频大小,避免抢占带宽,影响游戏主要启动流程。
``` js
// 是否开启可交互视频
const NEED_INTERACTION = false
// 需要使用可交互视频方案时,需要引入这两个文件
import PageManager from './loading/pageManager'
import VideoManager from './loading/videoManager'
if (NEED_INTERACTION) {
const videoManager = new VideoManager({
// 填写一个或多个视频视频地址,可选择传入视频封面
videoList: [
// {
// src: '',
// poster: ''
// },
// {
// src: '',
// poster: ''
// }
]
})
const pageManager = new PageManager({
// jumpBtnImg: 'images/jump-btn.png', // 跳过按钮图片地址,可本地,可远程
// continueBtnImg: 'images/PageTip.png', // 点击继续按钮图片地址,可本地,可远程
})
managerConfig = {
...managerConfig,
// 可交互视频相关配置
finalPageVideoUrl: '', // 点击跳过后,循环播放的视频地址,不传默认使用LOADING_VIDEO_URL
videoManager,
pageManager,
probability: 0.3, // 可交互视频出现概率
}
// 点击跳过按钮
pageManager.eventEmitter.once('jumpClick', () => {
const gameManager = GameGlobal.manager
if (gameManager) {
gameManager.reportJump()
gameManager.showFinalPage()
}
})
// 点击继续按钮
pageManager.eventEmitter.on('continueClick', () => {
videoManager.continue()
})
// 可交互视频播放完毕
videoManager.once('allvideoEnd', () => {
const gameManager = GameGlobal.manager
pageManager.hideContinueBtn()
gameManager && gameManager.showFinalPage()
})
// 每段视频开始前,隐藏点击继续按钮
videoManager.on('oneVideoStart', () => {
pageManager.hideContinueBtn()
})
// 每段视频播放完毕,显示点击继续按钮
videoManager.on('oneVideoEnd', () => {
pageManager.showContinueBtn()
})
}
```
### 3.5 手动可配置参看
`game.js`中修改插件配置
### 基本配置
```js
let managerConfig = {
DATA_FILE_MD5: "$DATA_MD5", // 转换脚本自动填写,无需关注
CODE_FILE_MD5: "$CODE_MD5", // 转换脚本自动填写,无需关注
GAME_NAME: "$GAME_NAME", // 转换脚本自动填写,无需关注
DATA_FILE_SIZE: "$DATA_FILE_SIZE", // 资源文件大小,自动填写无需关注
APPID: "$APP_ID", // 转换脚本自动填写,无需关注
LOADING_VIDEO_URL: "$LOADING_VIDEO_URL", // 默认加载视频地址
DATA_CDN: "$DEPLOY_URL", // 下载资源文件的CDN
STREAMING_CDN: "$STREAM_CDN", // AB包存放地址,有用到AB包时需要填写
}
```
\ No newline at end of file
# 使用Unity Profiler性能调优
1. 导出选项时勾选"Development Build"与"Autoconnection Profiler"
<image src='../image/profile1.png' width="400"/>
2. 打开Unity-Window-Analysis-Profile窗口
<image src='../image/profile2.png' width="400"/>
Unity将自启动监听端口34999等待调试链接,对于WebGL版本会启动websockify.js(用于websocket转发)。
此时,导出的WebGL游戏在浏览器时能自动连接到Unity Profiler。
3. 微信开发者工具小游戏Profile
使用转换脚本导出微信小游戏包并启动小游戏,微信小游戏将自动连接到Unity Profiler
<image src='../image/profile3.png' width="400"/>
4. 真机调试
Android或iOS启动之后将使用"ws://ip:port"自动连接到“Unity Profiler”, 如果无法连接请关注vConsole输出的IP:Port是否可达。
如果需要手工调整端口可通过修改以下代码:
<image src='../image/profile4.png' width="400"/>
> 一般来说,保持端口为54998,如修改端口需重启websocketfy.js:
> windows: "$UNITY_PATH/Editor/Data/Tools/nodejs/node.exe" "$UNITY_PATH/Editor/Data/PlaybackEngines/WebGLSupport/BuildTools/websockify/websockify.js" 0.0.0.0:port localhost:34999
> mac: /Applications/Unity/Hub/Editor/$Verson/Unity.app/Contents/Tools/nodejs/bin/node /Applications/Unity/Hub/Editor/$Verson/PlaybackEngines/WebGLSupport/BuildTools/websockify/websockify.js 0.0.0.0:port localhost:34999 -vv
附录:
- Profiler窗口使用说明 https://docs.unity3d.com/cn/2019.4/Manual/ProfilerWindow.html
# 启动留存数据上报统计
## 一、概述
在小游戏环境下,玩家对启动时长与体验非常敏感(尤其从“广告”等买量场景进入的玩家)。开发者往往需要分析玩家从点击到进入游戏核心玩法的整个过程流失率。
[Unity Loader](UsingLoader.md)的数据统计功能如下:
1. Unity Loader插件自动统计了代码包、首包资源、代码编译、引擎与首场景初始化,无需手动上报
2. 通过C# SDK接口让开发者上报自定义启动阶段,分析每个环节可能存在的流失
## 二、上报自定义阶段
为了详细统计玩家的流失情况以便开发者进行优化,我们拆分了三个部分。
<image src='../image/reportstartupstat3.png'/>
其中**自动上报**为Unity Loader自动完成开发者无需关注,但**自定义阶段****启动加载完成**需开发者主动调用接口进行上报。详细接口可参考C# SDK中的WX.cs,此处列出关键接口:
**1. 定义阶段**
设置游戏当前阶段,通过此接口,插件能感知到游戏业务所处阶段。
``` C#
WX.SetGameStage(int stageType)
```
stageType取值范围:[200,10000]
**2. 上报阶段耗时**
上报当前自定义阶段耗时
``` C#
WX.ReportGameStageCostTime(int costTime, string extJsonStr)
```
**3. 游戏完成所有加载时上报**
当游戏完成所有加载阶段,进入核心玩法时(如进入新手引导或大厅)调用
``` C#
WX.ReportGameStart(int costTime, string extJsonStr)
```
**4. 上报当前自定义阶段错误信息**
``` C#
WX.ReportGameStageError(int errorType, string errStr, string extJsonStr)
```
errorType取值:[0,10000]
## 三、获取数据统计
数据报表包含Unity Loader自动上报与开发者自定义阶段。关注总体流失漏斗以确定需要优化的方向,同时分阶段的耗时分布有利于帮助我们分析该阶段的对应耗时的用户占比。
<image src='../image/reportstartupstat1.png'/>
<image src='../image/reportstartupstat2.png'/>
注:
目前该数据统计报表需要建联[小游戏研发助手](IssueAndContact.md)获取,未来会开放到《小游戏数据助手》。
\ No newline at end of file
# 转换案例
## TrashDash
<image src='../image/showcase2.png' width="200"/>
《Trash dash》是Unity制作的[
EndlessRunnerSampleGame](https://github.com/Unity-Technologies/EndlessRunnerSampleGame)的小游戏适配。 一款有趣的跑酷类游戏,控制一条勇敢的街头猫,沿着一个大城市的街道跑步,沿途收集鱼骨和沙丁鱼罐头,同时避开各种致命的障碍和怪物。
## 网吧模拟器
<image src='../image/showcase1.png' width="200"/>
《网吧模拟器》由成都品游科技有限公司(Papa Box)开发的模拟经营向的游戏。一款挂机模拟经营类的休闲游戏。玩家将会作为一名网吧老板誓要濒临破产的网吧经营为一个奢华的网咖,玩家需要通过宣传推广,购置机器,雇佣员工等手段将网吧做大做强。
## Match3
<image src='../image/showcase5.png' width="200"/>
《Match3》是经典三消游戏类型中很棒的完整项目,玩法上具有许多目标和障碍。
## OTowerDefence
<image src='../image/showcase4.jpg' width="200"/>
《OTowerDefence》由Unity Learn的官方教程移植,该示例阐述了塔防的所有游戏元素,玩家可以选择不同的塔防建筑对抗AI敌人的进攻。建筑物可升级,售卖,具备丰富的玩法组合。
## AirStrike
<image src='../image/showcase6.png' width="200"/>
《AirStrike》是一款休闲类3D飞行游戏,丰富的关卡设计与战机造型, 模拟真实的飞行体验与环境,紧张刺激的敌我空战让玩家沉浸其中。
\ No newline at end of file
# 小游戏启动流程与时序
## 启动加载时序
Unity WebGL转换的小游戏主要依靠Unity Loader进行初始化,典型的流程如下图所示:
<image src="../image/launch1.png">
## Unity Loader工作流程
<image src="../image/launch.png">
关键过程:
1. 启动准备阶段有两个处理分支:资源与代码分包处理,两者并行。
2. 资源(data)处理:下载、解压并保持在内存,资源包括:构建场景及依赖、Resource目录所有资源、Unity默认内建资源
3. 代码分包(wasm.code.br)处理:下载代码分包,解压到内存、编译与实例化
4. 完成准备阶段后进入Unity引擎初始化与首场景加载
## QA:
1. 有哪些文件需要放CDN服务器?
首资源包:webgl/ProjectName.data.unityweb.bin.txt
资源分包:Adressable/AssetsBundle生成的文件
2. 资源(data)或资源包是否需每次下载?
为加快二次启动,默认loader再次启动时资源(首包资源或资源分包)将直接使用本地缓存。如需手动删除缓存可在真机中小游戏下拉历史栏将小游戏删除。
# 提升Unity WebGL游戏启动速度
## 一、 为什么要做启动优化
### 1.1 小游戏与手游APP的启动差异
微信小游戏具有“即开即用“的特性,手游APP则往往需要较长时间的下载。小游戏玩家对于启动时长更为敏感,因此过长的启动时间将导致用户显著流失。
### 1.2 优化的目标与标准
目前普通小游戏普遍启动时间为7~10s,而如果不经优化的Unity WebGL游戏启动会是该时间的2-3倍以上。我们建议开发者将启动优化作为上线前最为重要的事项。
## 二、分析小游戏启动速度
### 2.1. 查看启动耗时
通过timelog开发者可以看到小游戏目前的启动首屏时长:
<image src='../image/startupop1.png' width="400"/>
要知道各个阶段的含义,我们必要理解[启动流程](Startup.md)
小游戏启动主要由三部分影响:
> 1. 首包资源下载
> 2. WASM代码下载和编译
> 3. 引擎初始化与开发者首帧逻辑
**建议首屏启动时间控制在10~15s甚至更短**
### 2.2 分阶段耗时
### 2.2.1 资源下载阶段与首包体积
此阶段主要是小游戏资源下载的消耗时间,新用户或者版本资源更新时玩家需要重新下载。开发者需要了解小游戏玩家网速情况:
> 1. 网络条件绝大部分为wifi或4G(另,微信广告可指定网络条件)
> 2. 玩家平均下载速度约1MB/s
> 3. 微信用户存在不少<300KB/s的低网速玩家
我们建议首包资源**不超过5MB**以减少此阶段的耗时。
### 2.2.2 WASM代码下载和编译
WASM分包的大小会直接影响代码下载时长以及程序初始化编译的时间,关于WASM代码对启动速度的影响,开发者需要注意:
>1. 转换工具会将Unity WebGL包进行br压缩(压缩至原code包的20%)
>2. WASM代码下载与首包资源并行下载,因此占用下载带宽
>3. WASM编译对于低端机来说时间依然可观
>4. WASM编译在中高端机器很快,但对于低端机来说时间依然可观
我们建议原始代码包**不超过30MB**, 此处建议开发者**勾选"Strip Engine Code"并设置"Managed Stripping Level"为High**。同时,开发者可以使用[代码分包](WasmSplit.md)工具将代码包减少到原始尺寸的到1/3。
### 2.2.3 引擎初始化与开发者首帧逻辑
在timelog中呈现的首场景耗时即为引擎初始化与开发者首帧逻辑,关于该阶段耗时,开发者需要注意的是:
>1. MonoBehaviour脚本的首帧Start/Awake应足够少逻辑,优先将画面呈现
>2. 初始场景不宜过大,通常呈现Splah场景即可
>3. 初始场景中需要后续主场景或配置加载时可采取分帧策略,切勿在Start/Awake阻塞。
### 2.2.4 游戏内资源按需加载
前面我们提到开发者需要将资源从首包分离以较少首屏加载时间,同理,而对于其余的资源开发者最好使用按需加载的方式进行加载,减少玩家进行核心玩法的等待时间。
优化可参考 [使用Addressable Assets System进行资源按需加载](UsingAddressable.md)
### 2.3 优化总览
我们总结下启动时序以及开发者、平台提升启动性能的优化事项:
<image src='../image/startupop2.png'/>
## 三、常用启动优化技巧
当分析出小游戏需要进行启动优化时,请继续阅读:
* [使用Addressable Assets System进行资源按需加载](UsingAddressable.md)
* [首场景启动优化](FirstSceneOptimization.md)
## 四、常用启动优化工具
### 4.1. AssetStudio(推荐)
https://github.com/Perfare/AssetStudio
一款开源的资源查看工具,可以检查data首包以及AssetsBundle(或新Addressable)的资源内容,对于分析打包的资源正确性和冗余具有很好的帮助。
### 4.2 BuildReportTool(推荐)
https://assetstore.unity.com/packages/tools/utilities/build-report-tool-8162?locale=zh-CN
很好的前端用于查看Unity编译信息,BRT显示了编译时包括的每个资源占用的存储空间以及未使用资源情况。
### 4.3 Asset Hunter
https://assetstore.unity.com/packages/tools/utilities/asset-hunter-pro-135296
资源清理插件,可将项目中无用资源清理
### 4.4 Unity Addressable Assets System
https://docs.unity3d.com/Packages/com.unity.addressables@1.16/manual/index.html
Unity全新资源管理流程
# Unity WebGL小游戏适配方案概述
## 一、介绍
## 1.1 什么是Unity WebGL小游戏适配方案
简单来讲:本方案是支持Unity WebGL导出格式在微信小游戏环境的运行适配。
因此,开发者无需更换Unity引擎与重写核心代码的情况下发布到微信小游戏。
## 1.2 技术原理
<image src='../image/summary1.png' width="1080"/>
方案特点:
1. 原Unity项目需能支持发布为WebGL,由转换工具与微信小游戏运行环境保证适配兼容
2. 微信小游戏平台能力以C# SDK方式提供给开发者,无需JavaScript即可对接平台开放能力
3. 支持游戏中使用的第三方插件(C原生插件除外)
## 二、接入流程
接入流程主要包含以下几个环节:
<image src='../image/summary2.png' width="800"/>
> 注:图中每个环节的人力时间为预计,具体时间因具体项目不同。
### 2.1 评估反馈
我们为接入游戏提供了评估表,问卷内容是本转换方案最重要的影响因素。当根据实际项目填写之后与我们联系后一起评估可行性和技术风险点,详细内容请参考[方案评估](Evaluation.md)
### 2.2 工具转换
我们提供了**Unity转换插件**帮助开发者将项目自动导出为微信小游戏包,随后即可使用微信开发者工具或Android/iOS真机进行预览。
关于转换工具的使用请参考文档:
* [使用转换工具导出微信小游戏](Transform.md)
### 2.3 平台能力接入
微信小游戏平台提供众多开放能力,但目前是以JavaScript API形式提供。
为了降低开发者进行平台能力对接的门槛,我们提供了平台能力C# SDK,因此依然可以熟悉的C#接口进行平台能力使用。
关于平台能力C# SDK请参考文档:
* [WX SDK平台能力适配](WX_SDK.md)
### 2.4 调优体验
转换工具能帮助开发者快速转换原有Unity项目,然而对于优质的小游戏我们还需要更多的体验调优以取得良好的上线表现。
关于调优请参考文档:
* [Unity WebGL小游戏转换方案性能标准](PerfMeasure.md)
* [提升Unity WebGL游戏启动速度](StartupOptimization.md)
\ No newline at end of file
# 支持平台
## 操作系统
目前由Unity WebGL转换的小游戏兼容性与性能在不同平台的表现会有所差异,部分平台依然在迭代优化。
<image src='../image/supportedplatfoerm1.png'/>
## 微信小游戏基础库
微信小游戏从2.14.0开始支持Unity WebGL转换适配,现网玩家占比可参看[基础库版本分布](https://developers.weixin.qq.com/miniprogram/dev/framework/client-lib/version.html)
\ No newline at end of file
# 转换工具导出微信小游戏
>当前文档适用导出工具版本202104011707,独立域插件版本0.0.93
## 一、 前置要求
1. 已验证Unity版本:2018.3/2018.4LTS、2019.2/2019.4LTS
安装时选择WebGL组件
2. 如果你用的是Big Sur版本的Mac系统,并且Unity 版本小于 2019.4.14, 则需另外安装 [python3](https://www.python.org/downloads/),并安装brotli 命令如下
```
python3 -m pip install brotli
```
## 二、Unity导出WebGL
1. 下载 [Unity插件](http://res.wx.qq.com/wechatgame/product/webpack/userupload/wasm_plugin/minigame.unitypackage), 并导入至游戏项目中
2. 添加需要导出的scene
<image src='../image/scene.png' width="600"/>
建议仅勾选Loading场景,后续场景使用AssetsBundle/Addressable进行按需加载。
## 三、适配小游戏
1. Mac上授权
因为Mac上的隐私安全问题,使用mac的用户需要先授权,使用window的用户跳过这步。点击"微信小游戏"->"MacOS脚本授权",进行授权,直到不报错为止。若授权后还是报错,重启unity后再点授权试试。
2. 选择 点击 微信小游戏 -> 转换小游戏, 填写相应参数,点击`导出WEBGL并转换为小游戏`按钮 ,等待转换完成。
<image src='../image/export.png' width="600"/>
其中:
1. 必须
- 游戏appid:小游戏的appid
- 游戏资源CDN:首包资源加载方式选择CDN时,需要提供游戏首包资源所在HTTP或CDN地址
2. 可选
- 小游戏项目名:开发者工具中展示的小游戏项目名
- 首包资源加载方式:CDN-使用CDN下载首包资源;小游戏分包-使用小游戏代码分包下载资源
- AB包CDN地址:用到AB包时需要,地址为StreamingAssets目录的上一级目录
- 加载阶段视频URL:启动需要一定耗时,在启动加载时会循环播放这段视频,视频格式请参考[视频规范](Design/video.md)
- Assets目录对应CDN地址:使用压缩纹理时,生成的webgl目录下会包含Assets目录,填写Assets目录对应CDN地址
- 游戏内存大小:playersetting中memorysize
- 游戏方向:游戏是横屏还是竖屏
生成目录下 webgl目录为游戏对应的webgl版本,minigame目录为转化后的小游戏代码。
注意:
* 若首包资源加载方式选择'小游戏分包',会根据生成的wasm代码包和首包资源大小是否超过20M,若超过还是会使用CDN加载,参考[使用Loader进行游游戏加载](UsingLoader.md)
* 若首包资源加载方式选择'CDN',将生成目录下的`webgl/ProjectName.data.unityweb.bin.txt`上传至“游戏资源CDN”。您的CDN最好支持gzip压缩txt文件,这样能减少网络传输数据和时间。
* 项目使用了小游戏Unity适配插件,若小游戏是第一次使用本插件,在开发者工具会报错提示插件未授权,具体可参考[使用Loader进行游游戏加载](UsingLoader.md)
* 使用AssetsBundle时,请将资源上传到“AB包CDN地址”
至此,可以在[微信开发者工具](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html)打开转化后的小游戏进行预览。
## 四、使用脚本集成到自己的构建系统
如果你希望将导出插件集成到自己的发布流程,想脚本调用的话,可修改 Assets/WX-WASM-SDK/Editor/MiniGameConfig.asset配置,然后调用WXEditorWindow 的 DoExport方法导出小游戏
```
var win = new WXEditorWindow();
win.DoExport();
```
## 五、常见问题
1. 微信版本与运行时基础要求是什么?
* Android版本必须>=7.0.19
* iOS版本必须>=7.0.18
* 基础库版本必须>=2.14.0
具体支持情况参考[支持平台](Design/SupportedPlatform.md)
2. 为什么运行时出现奇怪的“Maximun call stack size exceeded.RangeError”或“Not Impletemented: Class::FromIL2CPPType”?
Unity WebGL 首包资源data文件含有metadata,如果data资源包和wasm对不上会有各种问题,尤其在**浏览器**运行时需清理IndexedDB中的缓存。
3. 为什么资源或网络请求在打开"vConsole"正常,关闭时下载失败?
网络请求必须**配置安全域名**:https://developers.weixin.qq.com/minigame/dev/guide/base-ability/network.html
打开"vConsole"时,小游戏默认不检查因此能请求通过。
4. 中文字体适配
字体必须打包到项目中,才能正常展示。
5. 音频被切到后台会停止
小游戏被切到后台会停止播放音频需代码中监听 [WX.OnShow](https://developers.weixin.qq.com/minigame/dev/api/base/app/life-cycle/wx.onShow.html)事件和 [WX.OnAudioInterruptionEnd](https://developers.weixin.qq.com/minigame/dev/api/base/app/app-event/wx.onAudioInterruptionEnd.html)事件,在该事件之后重新播放音频
# 使用Unity Profiler性能调优
1. 导出选项时勾选"Development Build"与"Autoconnection Profiler"
<image src='../image/profile1.png' width="400"/>
2. 打开Unity-Window-Analysis-Profile窗口
<image src='../image/profile2.png' width="400"/>
Unity将自启动监听端口34999等待调试链接,对于WebGL版本会启动websockify.js(用于websocket转发)。
此时,导出的WebGL游戏在浏览器时能自动连接到Unity Profiler。
3. 微信开发者工具小游戏Profile
使用转换脚本导出微信小游戏包并启动小游戏,微信小游戏将自动连接到Unity Profiler
<image src='../image/profile3.png' width="400"/>
4. 真机调试
Android或iOS启动之后将使用"ws://ip:port"自动连接到“Unity Profiler”, 如果无法连接请关注vConsole输出的IP:Port是否可达。
如果需要手工调整端口可通过修改以下代码:
<image src='../image/profile4.png' width="400"/>
> 一般来说,保持端口为54998,如修改端口需重启websocketfy.js:
> windows: "$UNITY_PATH/Editor/Data/Tools/nodejs/node.exe" "$UNITY_PATH/Editor/Data/PlaybackEngines/WebGLSupport/BuildTools/websockify/websockify.js" 0.0.0.0:port localhost:34999
> mac: /Applications/Unity/Hub/Editor/$Verson/Unity.app/Contents/Tools/nodejs/bin/node /Applications/Unity/Hub/Editor/$Verson/PlaybackEngines/WebGLSupport/BuildTools/websockify/websockify.js 0.0.0.0:port localhost:34999 -vv
附录:
- Profiler窗口使用说明 https://docs.unity3d.com/cn/2019.4/Manual/ProfilerWindow.html
# 使用Addressable Assets System进行资源按需加载
## 一、概述
对于Unity WebGL转换的小游戏启动耗时,资源下载通常是贡献最大的部分。这是由于手游APP往往很少针对首包资源进行特殊优化。
那么,接下来的问题是:小游戏中多大的首包资源合适? 剩余的游戏资源如何加载?
在此,我们建议得优化原则是:
> 1. 首包资源量不超过5M
> 2. 资源按需延迟加载,拆分得尽量细
本文介绍如何使用Unity新的资源管理流程[Addressable Assets System](https://docs.unity3d.com/Packages/com.unity.addressables@1.1/manual/index.html)进行资源的按需加载。
附可参考的项目:
https://git.weixin.qq.com/wechat-minigame/unity-webgl-addressable
## 二、Addressable在小游戏中的应用
### 2.1 什么是Addressable Assets System
Unity在2018版本中推出了Addressable Assets System(以下简称Addressable)的预览版本,并在2019的版本中已经成为正式版本,可以用于生产(仅表示发布时间,实际上大部分Unity版本都可正常使用)。
Addressable提供了以下能力:
> 低使用门槛:使用Addressable在开发前期就进入快速开发的阶段,使用任何你喜欢的资源管理技术,你都能快速的切换来Addressable系统中,几乎不需要修改代码。
> 依赖管理:Addressable系统不仅仅会帮你管理、加载你指定的内容,同时它会自动管理并加载好该内容的全部依赖。在所有的依赖加载完成,你的内容彻底可用时,它才会告诉你加载完成。
> 内存管理:Addressable不仅仅能记载资源,同时也能卸载资源。系统自动启用引用计数,并且有一个完善的Profiler帮助你指出潜在的内存问题。
> 内容打包:Addressable系统自动管理了所有复杂的依赖连接,所以即使资源移动了或是重新命名了,系统依然能够高效地找到准确的依赖进行打包。当你需要将打包的资源从本地移到服务器上面,Addressable系统也能轻松做到,几乎不需要任何代价。
### 2.2 相对于AssetsBundles的优势
Unity中资源按需加载也可以使用老的AssetBundle,然而使用AB需要做不少的工作:标识Asset、组织Bundle、编译、Load/Unload、依赖关系以及后期维护的复杂工作。新一代的Addressable正是对这些痛点做了不少改进,开发者只需要将Asset设置为addressable然后加载即可,[功能强大并且学习曲线变得平滑](https://docs.google.com/document/d/1hPLNLdrF0qAvjEJTpKf-cuO_d4FCV0H2cqBeP1Zo6mA/edit)
<image src='../image/addressable7.png' width="600"/>
### 2.3 在小游戏中使用Addressable Assets System
无论是Addressable还是AssetBundle在微信小游戏底层都使用XHR进行远程资源访问,并使用微信小游戏文件存储系统进行缓存。对于已有的游戏资源,如果我们需要尽量少的工作量去做到像H5游戏按需加载,使用Addressable是最佳做法。
## 三、启动优化与资源优化实战
### 3.1 从首包开始
首包资源应该只包含首屏所需资源,比如Splash界面以及对应文案。
<image src='../image/addressable2.png' width="800"/>
精简场景使用zip压缩后3M左右最佳,不应超过5M。
首屏资源需要注意:
> 1. 导出场景不要勾选任何其他场景
> 2. 不要打包字体文件,字体往往压缩率很低。
> 3. 通过Addressable检查Bultin分组,特别注意不要随意放置资源到Resources目录,该目录将无条件被打包到首包中。
> 4. 上线前使用专业版剔除Unity Splash资源
### 3.2 资源按需加载
#### 3.2.1 场景动态加载
如前所述,我们构建时仅选择了splash场景,那么主场景(如大厅/战斗等)该如何加载?此时我们可以**将每个场景单独作为Addressable分组**,在用到的时候才下载该场景包。
<image src='../image/addressable5.png' width="800"/>
使用Addressables.LoadSceneAsync可以动态加载场景与获取加载进度:
```
IEnumerator LoadMain()
{
var handle = Addressables.LoadSceneAsync("Assets/RPGPP_LT/Scene/rpgpp_lt_scene_1.0.unity", LoadSceneMode.Single, true);
handle.Completed += (obj) =>
{
Debug.LogWarning($"Load async scene complete{obj.Status}");
};
while (!handle.IsDone)
{
// 在此可使用handle.PercentComplete进行进度展示
yield return null;
}
}
```
选择分组,设置分组属性如下:
<image src='../image/addressable8.png' width="800"/>
如果我们仅将场景作为分组,其中静态摆放的物件不单独设置为Addressable也会一并打包到场景所在bundle。那么,这是会产生一个问题:两个场景都使用同样资源是否产生冗余?答案是肯定的!!
那么,如何消除冗余呢?当我们Adressable面板的Tools-->Analyze进行分析时,可看到以下内容:
<image src='../image/addressable9.png' width="800"/>
此时,我们应将这些冗余的内容单独进行设置为Addressable。而更为简单的做法是:选中“Check Duplicate Bundle Dependencies”,点击“Fixed Selected Rules”,工具会自动将冗余项逐个设置为Addressable。
### 3.2.2 物件动态加载
除了静态场景外,我们还会经常动态实例化(Instantiate)或在内存中创建资源对象。比如:
<image src='../image/addressable3.png' width="600"/>
```
public class LoadAssetScript : MonoBehaviour
{
public GameObject somePrefab;
private void Start()
{
Instantiate(somePrefab);
}
}
```
然而,这样做有个非常严重的问题:Prefab本身以及依赖的所有资源需要在场景加载前完成。在小游戏环境,**这意味着即使还没调用Instantiate,这部分资源就必须准备好,这会极大影响场景初始化速度**
那么,如果我们希望把somePrefab以及它的依赖资源打包在Addressable分组进行按需下载应该如何做呢?答案是AssetReference。上述代码改写成:
```
public class LoadAssetScript : MonoBehaviour
{
public AssetReference somePrefab;
private void Start()
{
somePrefab.InstantiateAsync().Completed += (obj) =>
{
// 加载完成回调
};
}
}
```
或协程写法:
```
public class LoadAssetScript : MonoBehaviour
{
public AssetReference somePrefab;
private IEnumerator spawnSomathing()
{
var hanle = somePrefab.InstantiateAsync();
while(!handle.IsDone) {yield return null;}
// 加载完成回调
var gameObject = hanle.Result;
}
}
```
同时,对应的Prefab在editor中需设置为Addressable,并重新为somePrefab赋值。
### 3.2.3 Resources.Load改造
使用这种方式加载资源,通常需要再Asset或其子目录下创建Resources的文件夹,然后使用类似这种方式加载:
```
TextAsset text = Resources.Load<TextAsset>("MyConfig");
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(text.text);
```
然而,Resources目录的内容都会被打包进首包资源,对于小游戏来说是**不推荐使用的方式**
开发者在Addressable的default中能看到所有这些资源,我们需要将这些资源设置为“Addressable”,Unity将自动移动到“Resources_Moved”目录。
加载代码改写成:
```
var handle = UnityEngine.AddressableAssets.Addressables.LoadAssetAsync<TextAsset>("MyConfig");
handle.Completed += (obj) =>
{
var text = obj.Result;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(text.text);
...
}
```
### 3.2.4 Addressable编译与部署
默认情况下,当编译Addressable资源时会输出到Library/com.unity.addressables/,项目发布为WebGL或转换为小游戏时Unity会自动拷贝Bundle文件到最终的生成目录下。我们只需要将对应的StreammingAssets上传到对应的CDN服务器即可。
### 3.2.5 资源预加载
<image src='../image/addressable10.png' width="700"/>
我们可以根据资源的加载时序以及重要程度,修改game.js文件填写预加载资源列表。小游戏加载框架将利用网络空闲期进行资源预加载。
## 四、总结
Unity WebGL转换的小游戏普遍存在首包资源较大的情况,而新Address提供了非常好的资源管理流程。我们建议开发者:
> 1. 精简首场景,首包资源中确保只包含轻量的首屏以及依赖资源
> 2. 延迟加载,避免业务逻辑需要全量资源的情况,设计上尽量按需加载
> 3. 资源拆分,利用Addressable进行更灵活和细粒度的拆解
> 4. 预加载,根据优先级设置需要预加载的分包,利用网络空闲期
### 五、参考资料
1. Addressable Asset System for Unity (Overview)
https://docs.google.com/document/d/1hPLNLdrF0qAvjEJTpKf-cuO_d4FCV0H2cqBeP1Zo6mA/edit
2. 官方Demo
https://github.com/Unity-Technologies/Addressables-Sample
3. 视频范例
https://drive.google.com/file/d/1w-Lh_jsD2VSHNvkzexJLSc6bA3gUQC8d/view
4. uwa关于Addressable的介绍
https://blog.uwa4d.com/archives/USparkle_Addressable4.html
4. Addressable与AssetBundle的对比
https://www.jianshu.com/p/8009c16fcab3
# 使用Loader进行游游戏加载
## 一、什么是Unity Loader?
Unity Loader是在微信小游戏环境加载Unity WebGL游戏的加载与适配器,使用微信小游戏插件技术开发,功能包括:
- WebAssembly代码分包加载与编译
- 资源加载与编译
- 启动进度显示
- 缓存策略
- 数据上报
## 二、如何使用Unity Loader插件
生成微信小游戏项目时,转换工具会在`game.json`中声明:
```json
"plugins": {
"UnityPlugin": {
"version": "major.minor.patch", // 可切换版本号
"provider": "wxe5a48f1ed5f544b7",
"contexts": [
{
"type": "isolatedContext"
}
]
}
}
```
这部分配置表示使用Unity Loader插件进行游戏加载,开发者无需手动修改
首次使用时,会提示添加插件,按提示添加即可
<image src="../image/addPlugin.png">
## 三、配置Unity Loader功能
以下配置都在导出的minigame/game.js中
### 3.1 启动界面
由于Unity WebGL启动加载需要一定时间,因此需要使用视频或图片等内容作为过渡以留住玩家。Unity Loader默认使用视频+进度信息呈现,开发者可以自定义封面视频,可参考[启动Loader视频规范](Design/video.md)进行配置。
界面有以下两种
1. 使用coverview渲染进度(默认方式)
<image src="/image/coverview_loading.png" height="500">
这种方式的优势在于可以覆盖因首帧逻辑过重,导致启动过程中可能出现的黑屏,等游戏画面真正出现时再隐藏启动界面
支持参数
```js
loadingPageConfig: {
// 背景图或背景视频,两者都填时,先展示背景图,视频可播放后,播放视频
backgroundImage: 'images/background.jpg', // 默认的背景图,可自行替换,支持本地图片和网络图片
backgroundVideo: '', // 视频url,需要开发者提供,只支持网络url
// 以下是默认值
totalLaunchTime: 15000, // 默认总启动耗时,即加载动画默认播放时间,可根据游戏实际情况进行调整
textDuration: 1500, // 当downloadingText有多个文案时,每个文案展示时间
firstStartText: '首次加载请耐心等待', // 首次启动时提示文案
downloadingText: ['正在加载资源'], // 加载阶段循环展示的文案
compilingText: '编译中', // 编译阶段文案
initText: '初始化中', // 初始化阶段文案
completeText: '开始游戏', // 初始化完成
}
```
> backgroundImage需要注意图片宽高不可超过2048,否则无法显示
> 使用coverview需要基础库版本>=2.16.1,插件已做兼容,若不支持,降级为使用离屏canvas渲染进度的方式
2. 使用离屏canvas渲染进度
<image src="/image/default_loading.jpg" height="500" />
支持参数
```js
let managerConfig = {
// ...省略其他配置
LOADING_VIDEO_URL: "", // 视频url
}
```
当基础库>=2.16.1时,默认使用coverview,否则使用离屏canvas
### 3.2 首包资源加载方式
**加载方式在转换工具导出时就确定好了,开发者一般不需要修改**
当游戏资源量比较少时,可选择将首包资源作为小游戏分包加载,了解[小游戏分包](https://developers.weixin.qq.com/minigame/dev/guide/base-ability/sub-packages.html)
wasm代码已是通过代码分包加载,当wasm代码+首包资源>20M时,资源包不可再使用小游戏分包加载。
当首包资源通过小游戏代码分包下载时,会将首包资源存放在minigame/data-package这个分包下
相关配置
```js
let managerConfig = {
/* 省略其他配置 */
loadDataPackageFromSubpackage: $LOAD_DATA_FROM_SUBPACKAGE, // 转换工具自动替换
}
```
- 若手动将`loadDataPackageFromSubpackage`改为false,需要将webgl目录下的资源包上传到CDN,并将CDN地址填写到game.js`DATA_CDN`字段
- 同样的,若手动将`loadDataPackageFromSubpackage`改为true,需要将webgl目录下的资源包copy到minigame/data-package下
### 3.3 预加载资源
为了充分利用网络带宽,在网络空闲时,比如下载完小游戏分包和首包资源后,可以预加载游戏需要用到的AB包。
``` js
let managerConfig = {
/* 省略其他配置 */
/**
* 假设: AB包打包到 path1/StreamingAssets/WebGL这个目录下; STREAMING_CDN是以path1为根路径上传到服务器的CDN地址
*/
// 需要在网络空闲时预加载的资源,支持如下形式的路径。
preloadDataList: [
// '$STREAM_CDN/StreamingAssets/WebGL/textures_8d265a9dfd6cb7669cdb8b726f0afb1e',
// '/WebGL/sounds_97cd953f8494c3375312e75a29c34fc2'
],
}
```
### 3.4 资源缓存与淘汰策略
#### 资源缓存
首包资源和wasm代码会自动缓存。
使用bundle时,插件也会做缓存处理,但插件需根据下载文件名进行处理,因此需要注意:
1. bundle文件名不可重名,否则同名文件无法使用缓存
2. bundle名需要带上md5 [BuildAssetBundleOptions.AppendHashToAssetBundleName](https://docs.unity3d.com/ScriptReference/BuildAssetBundleOptions.AppendHashToAssetBundleName.html),以便清理掉该文件的旧缓存
3. bundle存放路径需要包含StreamingAssets这个path(一般addressable默认打包会放在StreamingAssets/aa/WebGL/WebGL/这个目录下),或者文件名包含.bundle,才能启用缓存
#### 资源淘汰
由于缓存目录最大不可超过200M,在下载资源包、下载AB包时,若剩余空间不足以缓存,会进行缓存淘汰。目前规则比较简单,如下:
1. 如果所需空间过大,超过最大限制:下载完成后不缓存文件,也不执行清理逻辑,直接返回下载内容。
2. 清理部分文件可以缓存新文件:按最近使用时间从前往后排序清理,直到清理出所需空间。
> 因为文件解压只能解压到用户目录,所以,若压缩文件过大,无法解压到用户目录时,会返回失败。
### 3.5 其他可配置参看
`game.js`中修改插件配置
### 基本配置
```js
let managerConfig = {
DATA_FILE_MD5: "$DATA_MD5", // 转换脚本自动填写,无需关注
CODE_FILE_MD5: "$CODE_MD5", // 转换脚本自动填写,无需关注
GAME_NAME: "$GAME_NAME", // 转换脚本自动填写,无需关注
APPID: "$APP_ID", // 转换脚本自动填写,无需关注
DATA_FILE_SIZE: "$DATA_FILE_SIZE", // 资源文件大小,自动填写无需关注
LOADING_VIDEO_URL: "$LOADING_VIDEO_URL", // 默认加载视频地址
DATA_CDN: "$DEPLOY_URL", // 下载资源文件的CDN
AUDIO_PREFIX: "$AUDIO_PREFIX", // 音频资源cdn
STREAMING_CDN: "$STREAM_CDN", // AB包存放地址,有用到AB包时需要填写
loadDataPackageFromSubpackage: $LOAD_DATA_FROM_SUBPACKAGE, // 资源包是否作为小游戏分包加载
};
```
\ No newline at end of file
# SDK 调用微信API
将 WX-WASM-SDK 这个目录拷贝至unity工程 Assets目录下,在主入口初始化,回调后再执行你的主逻辑
```
WX.InitSDK((int code)=> {
// 你的主逻辑
});
```
API调用,如调用振动:
```
WX.VibrateShort(
(msg) => { Debug.Log("success"); },
(msg) => { Debug.Log("fail"); },
(msg) => { Debug.Log("completed"); }
);
```
更多API可以直接看`WX.cs`这个文件,里面有`详细注释说明`
本Unity的SDK的API大体与[官网](https://developers.weixin.qq.com/minigame/dev/guide/)的JS版本API类似,使用时可以参考之。
如JS版的banner广告的调用如下:
```
var bannerAd = wx.createBannerAd({
adUnitId: "xxxx",
adIntervals: 30,
style: {
left: 0,
top: 0,
width: 600,
height:200
}
});
bannerAd.onLoad(() => {
bannerAd.show();
});
bannerAd.onError((res)=>{
console.log(res);
});
```
而对于Unity版的调用如下:
```
var bannerAd = WX.CreateBannerAd(new WXCreateBannerAdParam()
{
adUnitId = "xxxx",
adIntervals = 30,
style = new Style()
{
left = 0,
top = 0,
width = 600,
height = 200
}
});
bannerAd.OnLoad(()=> {
bannerAd.Show();
});
bannerAd.OnError((WXADErrorResponse res)=>
{
Debug.Log(res.errCode);
});
```
大体是将JS版中的`wx`替换为Unity版的`WX`,然后对应方法名首字母由小写改为大写,如`createBannerAd`就变为`CreateBannerAd`
## 目前已完成API列表:
<table>
<tr>
<th>分类</th>
<th>类名</th>
<th>方法名</th>
</tr>
<tr>
<td>初始化SDK</td>
<td rowspan="64">WX</td>
<td>InitSDK</td>
</tr>
<tr>
<td rowspan="2">振动</td>
<td>VibrateShort</td>
</tr>
<tr>
<td>VibrateLong</td>
</tr>
<tr>
<td rowspan="9">本地存储</td>
<td>StorageSetIntSync</td>
</tr>
<tr>
<td>StorageGetIntSync</td>
</tr>
<tr>
<td>StorageSetStringSync</td>
</tr>
<tr>
<td>StorageGetStringSync</td>
</tr>
<tr>
<td>StorageSetFloatSync</td>
</tr>
<tr>
<td>StorageGetFloatSync</td>
</tr>
<tr>
<td>StorageDeleteAllSync</td>
</tr>
<tr>
<td>StorageDeleteKeySync</td>
</tr>
<tr>
<td>StorageHasKeySync</td>
</tr>
<tr>
<td rowspan="3">登录 <a href="https://developers.weixin.qq.com/minigame/dev/guide/open-ability/login.html">参考地址</a></td>
<td>Login </td>
</tr>
<tr>
<td>CheckSession</td>
</tr>
<tr>
<td>Authorize</td>
</tr>
<tr>
<td rowspan="2">用户信息 <a href="https://developers.weixin.qq.com/minigame/dev/guide/open-ability/user-info.html">参考地址</a></td>
<td>GetUserInfo</td>
</tr>
<tr>
<td>CreateUserInfoButton</td>
</tr>
<tr>
<td rowspan="3">客户端信息</td>
<td>GetSystemInfo</td>
</tr>
<tr>
<td>GetSystemInfoSync</td>
</tr>
<tr>
<td>GetSystemLanguage</td>
</tr>
<tr>
<td rowspan="14">分享转发 <a href="https://developers.weixin.qq.com/minigame/dev/guide/open-ability/share/share.html">参考地址</a></td>
<td>UpdateShareMenu</td>
</tr>
<tr>
<td>ShowShareMenu</td>
</tr>
<tr>
<td>HideShareMenu</td>
</tr>
<tr>
<td>SetMessageToFriendQuery</td>
</tr>
<tr>
<td>ShareAppMessage</td>
</tr>
<tr>
<td>ShowShareMenu</td>
</tr>
<tr>
<td>OnShareAppMessage</td>
</tr>
<tr>
<td>OffShareAppMessage</td>
</tr>
<tr>
<td>OnShareTimeline</td>
</tr>
<tr>
<td>OffShareTimeline</td>
</tr>
<tr>
<td>OnAddToFavorites</td>
</tr>
<tr>
<td>OffAddToFavorites</td>
</tr>
<tr>
<td>GetShareInfo</td>
</tr>
<tr>
<td>AuthPrivateMessage</td>
</tr>
<tr>
<td rowspan="6">广告 <a href="https://developers.weixin.qq.com/minigame/dev/guide/open-ability/ad/ad.html">参考地址</a></td>
<td>CreateRewardedVideoAd</td>
</tr>
<tr>
<td>CreateFixedBottomMiddleBannerAd</td>
</tr>
<tr>
<td>CreateInterstitialAd</td>
</tr>
<tr>
<td>CreateGridAd</td>
</tr>
<tr>
<td>CreateBannerAd</td>
</tr>
<tr>
<td>CreateCustomAd</td>
</tr>
<tr>
<td rowspan="9">生命周期</td>
<td>OnShow</td>
</tr>
<tr>
<td>OffShow</td>
</tr>
<tr>
<td>OnHide</td>
</tr>
<tr>
<td>OffHide</td>
</tr>
<tr>
<td>GetLaunchOptionsSync</td>
</tr>
<tr>
<td>OnAudioInterruptionBegin</td>
</tr>
<tr>
<td>OnAudioInterruptionEnd</td>
</tr>
<tr>
<td>OffAudioInterruptionEnd</td>
</tr>
<tr>
<td>OffAudioInterruptionBegin</td>
</tr>
<tr>
<td >文件</td>
<td>GetFileSystemManager</td>
</tr>
<tr>
<td rowspan="5">开放数据</td>
<td>GetOpenDataContext</td>
</tr>
<tr>
<td>ShowOpenData</td>
</tr>
<tr>
<td>HideOpenData</td>
</tr>
<tr>
<td>SetUserCloudStorage</td>
</tr>
<tr>
<td>RemoveUserCloudStorage</td>
</tr>
<tr>
<td rowspan="9">输入法</td>
<td>UpdateKeyboard</td>
</tr>
<tr>
<td>ShowKeyboard</td>
</tr>
<tr>
<td>HideKeyboard</td>
</tr>
<tr>
<td>OnKeyboardInput</td>
</tr>
<tr>
<td>OnKeyboardConfirm</td>
</tr>
<tr>
<td>OnKeyboardComplete</td>
</tr>
<tr>
<td>OffKeyboardInput</td>
</tr>
<tr>
<td>OffKeyboardConfirm</td>
</tr>
<tr>
<td>OffKeyboardComplete</td>
</tr>
<tr>
<td>渲染</td>
<td>WXCanvas</td>
<td>ToTempFilePathSync</td>
</tr>
</table>
\ No newline at end of file
# 代码分包
目前我们采用了一种 Profile Guided Optimization 的方式,通过离线运行时收集信息,将小游戏的 wasm 代码包进行拆分,使得小游戏可以先加载较小的首包进入主场景,再异步加载剩下的分包
我们提供了一个开发者工具插件来辅助分包过程,依赖的[开发者工具](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html)版本为 1.05.2104251 RC 及以上,稳定版 1.05.2105100 已支持
分包工作流如图所示
<image src="../image/wasmsplit/workflow.png">
## 打开分包
通过开发者工具的设置-拓展设置-编辑器自定义拓展,打开 minigame.codeSplit 这个插件的开关
<image src="../image/wasmsplit/extension-panel.png">
<image src="../image/wasmsplit/enable-plugin.png">
## 使用流程
1. 打开插件开关后,在目录树上的工具栏中,可以看到插件的按钮,如图所示,点击后即可进入插件页:
<image src="../image/wasmsplit/code-split-index.png">
2. 点击启用代码分包,新导出的小游戏(注:同一游戏从 unity 的不同次导出也认为是新的)会提示输入版本描述,简单输入方便识别版本的描述即可,然后插件会自动进行首次分包
3. 第一次分包完成后,就可以开始迭代式收集,每一轮迭代流程如下:
- 点击开始收集
- 点击开发者工具的预览,在真机上跑,有条件的话可以尽量覆盖各种机型(主流品牌)以及平台(Android/iOS)
- 当插件页显示的收集到增量函数个数相对稳定时,可以点击结束收集
- 点击生成 profile 包
<image src="../image/wasmsplit/code-split-index-2.png">
<image src="../image/wasmsplit/profiling.png">
随着迭代轮数增多,新增函数会越来越少,
这里没有完成收集的标准,建议开发者能回归覆盖游戏的各种启动场景即可(不同进度,二次启动等等),目的是为了延迟依赖剩下的分包的时间
4. 当收集评估 ok 时,可以点击生成 release 版分包,生成最终的发布版本(如果生成了 release 包,下次收集前需要先生成 profile 包)
## 关闭分包
如果想发布不分包的版本,点击插件页的关闭代码分包按钮即可
# 屏幕适配
## 安全区域适配
小游戏的屏幕适配与unity游戏适配常见手机屏幕没有区别。常见的适配方式都可以在小游中使用。区别的是一些屏幕信息的获取。
如安全区域的获取需调用 WX.GetSystemInfoSync得到其中的安全区域。
如下是适配刘海屏(这里以竖屏游戏按高度适配为例)的示例:
```
var info = WX.GetSystemInfoSync();
float py = (float)info.safeArea.top / (float)info.windowHeight;
// Rootrect初始时设置其Anchor,使其与父节点一样大,也就是屏幕的大小
// 调整屏幕移到刘海屏下面,
Rootrect.anchorMin = new Vector2((float)info.safeArea.left / (float)info.windowWidth, -(float)info.safeArea.top / (float)info.windowHeight );
// 重新计算缩放,让高度占满刘海屏以下的区域
cs.referenceResolution = new Vector2(cs.referenceResolution.x, cs.referenceResolution.y * (1.0f+py));
```
## 高分辨屏下模糊问题
因为 Unity 2019.3之前的版本对高分辨屏适配不是很好。会出现画面模糊的情况,所以最好选择`Unity 2019.3`之后的版本来构建你的游戏。如果游戏不能升级版本,也可以引入[SDK](./WX_SDK.md),在初始化SDK后,SDK会自动对页面做一个兼容适配,避免模糊的问题。
```
//初始化SDK
WX.InitSDK((int code)=> {
// 你的主逻辑
});
```
\ No newline at end of file
# 视频设计
*建议启动视频内带有团队宣传logo动画,游戏logo动画,带有吸引力的游戏玩法视频*
---
视频格式参考:
```
Loading 视频规范:
横屏视频:16:9(素材分辨率:960 px × 540 px)
竖屏视频:9:16(视频分辨率:750 px × 1334 px)
视频尺寸:不超过 10MB
视频时长:6~15 秒
支持格式:MP4
注:视频会在 Loading 期间重复播放
----
以下为视频建议参数(仅供参考)
视频编码:H.264
FPS:24
码率:小于 800 kbit/s
MP4 Profile:Main
音轨格式:AAC
音轨Sample Rate:44.1 kHz
音轨 Datarate:96 kbit/sec
```
\ No newline at end of file
# Unity WebGL 小游戏适配方案(内测)
欢迎使用 Unity WebGL 小游戏适配(转换)方案,本方案设计目的是**降低 Unity 游戏转换到微信小游戏的开发成本**。开发者无需更换 Unity 引擎工作流与重写核心代码的情况下,将原有游戏项目适配到微信小游戏。
# 文档总览
概述
- [方案概述](Design/Summary.md)
- [支持平台](Design/SupportedPlatform.md)
- [转换案例](Design/ShowCase.md)
工具指引
- [方案兼容性评估](Design/Evaluation.md)
- [快速开始:转换工具导出微信小游戏](Design/Transform.md)
性能优化
- [性能优化总览](Design/PerfOptimization.md)
- [性能评估标准](Design/PerfMeasure.md)
- 启动性能
- [提升 Unity WebGL 游戏启动速度](Design/StartupOptimization.md)
- [启动流程与时序](Design/Startup.md)
- [使用 Loader 进行游游戏加载](Design/UsingLoader.md)
- [使用 Addressable 进行资源按需加载](Design/UsingAddressable.md)
- [首场景启动优化](Design/FirstSceneOptimization.md)
- [启动留存数据上报统计](Design/ReportStartupStat.md)
- [代码分包](Design/WasmSplit.md)
- 运行性能
- [使用 Android CPU Profiler 性能调优](Design/AndroidProfile.md)
- [使用 Unity Profiler 性能调优](Design/UnityProfiler.md)
- [音频适配优化](Design/AudioOptimization.md)
- [压缩纹理优化](Design/CompressedTexture.md)
- [资源优化工具与建议](Design/AssetOptimization.md)
能力适配
- [WX SDK 平台能力适配](Design/WX_SDK.md)
- [屏幕适配](Design/fixScreen.md)
- [输入法适配](Design/InputAdaptation.md)
- [排行榜与微信关系数据](Design/OpenData.md)
- [后端服务指引](Design/BackendServiceStartup.md)
问题反馈
- [问题反馈与联系我们](Design/IssueAndContact.md)
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册