Unity 小游戏转换(一)—— WebGL+XLua导出
转载或者引用本文内容请注明来源及原作者
一、前言
- 小游戏的红海赛道,给游戏市场带来了新的活力。小游戏依托微信、抖音等第三方平台,因为买量成本较低、开箱既玩的特性,使得许多开发厂商开始布局小游戏平台。同时Unity引擎也花费了大量的精力(团结引擎),慢慢更改开发者对于Unity庞大繁重,不适合开发微信小游戏的刻板印象,目前市面上已经有多款Unity引擎开发的游戏经受了市场的检验。当Unity引擎解决了这个核心的缺点,Unity引擎在统一公司开发栈、可编程渲染管线、活跃的社区平台、良好的插件生态等方面,具有别的引擎系统无法比拟的优势。
- 可以想象,当游戏开发初期,可以通过Windows、Android等平台,快速验证游戏玩法,确定游戏原型。后期可以根据公司策略,快速导出不同平台进行发布。同时Unity在表现力上有更高的上限,也能够保证美术效果的还原。
- 当然,Unity通向小游戏的路上,最重要的一点就是导出WebGL工程。当整个工程能够导出WebGL工程并运行,后续的小游戏处理工作,大部分只需要对接不同平台的SDK即可。
二、WebGL导出
本文不讨论WebGL的导出流程,我们聚焦于Web平台 + XLua构建遇到一些棘手的问题。详细的导出教程推荐构建和运行 WebGL 项目(Unity官方文档)、Unity WebGL 开发指北(完全篇)等等
1. 如何引入XLua
-
步骤1:将WebGLPlugins放到工程的Assets同级目录下
- 注意:官方使用的Xlua版本为Lua5.3,如果使用低版本或者LuaJit需要注意业务语法是否支持。
- LuaJit中会处理迭代安全(列表遍历移除自身),而Lua5.3中则不会
- string.format("%p", value)在标准Lua5.3中不可用,是LuaJit的拓展方法
- Lua5.3不支持math.pow,通过拓展math方法进行解决
- 注意:官方使用的Xlua版本为Lua5.3,如果使用低版本或者LuaJit需要注意业务语法是否支持。
-
步骤2:在Assets/Plugins/WebGL下创建一个Include文件,用来声明用到的lua库文件。这里我取名叫xlua_webgl.cpp,将第一步骤下的WebGLPlugins内的lua文件加到这个声明文件中(当然也可以包含自己写的C文件),逻辑代码如下:
-
extern "C" { #include "../../../WebGLPlugins/lapi.c" #include "../../../WebGLPlugins/lauxlib.c" 等等... #include "../../../WebGLPlugins/perflib.c" #include "../../../WebGLPlugins/xlua.c" }
-
2. 脚本编码格式问题
-
问题:
-
当你导出的WebGL工程运行起来遇到这个问题:unexpected symbol near '<\239>',意味着你遇到了编码格式的问题。最常见的原因是你的文件中包含了UTF-8的BOM(Byte Order Mark,字节顺序标记)。BOM是一个可选的字符,用于标记文件大端还是小端。它的二进制表示为 "11101111 10111011 10111111",转换为十六进制就是 "\xEF\xBB\xBF"。很多文本编辑器在保存UTF-8文件时会自动添加BOM,但是一些语言处理器(如Lua、PHP和JavaScript等)不识别BOM,它们会把BOM当成一个不可识别的字符而报错。
-
-
解决方法:
- 通过命令行快速的处理:
grep -r -i -l $'^\xEF\xBB\xBF' . | xargs sed -i 's/^\xEF\xBB\xBF//g'
,就是递归地在当前目录下删除所有文件中的BOM。
- 通过命令行快速的处理:
-
原因:
- 为什么在App中不需要关心字符编码,但是网页端却需要呢?原因在Web开发中,字符编码的设置非常关键,因为它直接决定了用户看到的网页内容是否会出现乱码。因此,开发者需要在网页的头部明确指定字符编码,这样浏览器才能正确地解析和显示网页。但是,如果服务器发送的字符编码和网页头部的字符编码设置不一致,或者文本中包含了某些特殊字符(如BOM),就可能会导致显示问题。
- 而App的开发和Web开发不同,编码问题相对要少一些,这主要是因为App中的字符串和文本通常都是开发者直接控制的,而且很多编程语言都默认支持UTF-8,并且能够处理一些特殊字符。另外,App的界面也不依赖浏览器去渲染,而是由操作系统直接绘制,因此它们不太可能因为字符编码的问题出现显示错误。
3. 禁止使用任何 .NET 网络类
- 问题:
- 当你在App上通过Socket与服务端建立连接的逻辑,在WebGL上会发现连握手都握不上
- 解决方法:
- 方法一:通过UnityWebRequest,官方有详细的使用方法
- 方法二:通过JSPlugin,这里我使用了第三方库UnityWebSocket。直接通过PackeManager可以方便的进行安装和拓展
- 原因:
- JavaScript 代码无法直接访问互联网协议 (IP) 套接字来实现网络连接。具体来说,WebGL 不支持
System.Net
命名空间内的任何 .NET 类
- JavaScript 代码无法直接访问互联网协议 (IP) 套接字来实现网络连接。具体来说,WebGL 不支持
4. 禁止使用阻塞代码
-
问题:
- 使用类似
while(!www.isDone()) {}
来等待业务完成,会导致WebGL工程直接卡死
- 使用类似
-
解决方法:
-
通过协程,配合yield等待来完成下载。可以继承CustomYieldInstruction封装一些自己的业务加载器。类似的代码:
public class CoroutineLoader : CustomYieldInstruction { private bool _keepWaiting; private string assetName; public override bool keepWaiting => _keepWaiting; public object asset; public CoroutineLoader(string assetName) { this.assetName = assetName; asset = null; _keepWaiting = true; AssetUtil.LoadAssetAsync(assetName, (cacheAsset) => { asset = cacheAsset; _keepWaiting = false; }); } }
-
-
原因:
- WebGL 是单线程执行,所以没有子线程去修改等待的变量的值,这将导致你的阻塞代码无法跳出循环,最终卡死
5. 资源同步加载
- 问题:
- WebGL不支持资源同步加载,以及ab文件流的异步加载,所有的ab文件都在云端存储上,需要通过UnityWebRequest进行请求
- 解决方法:
- 业务上所有的资源加载都通过异步加载进行,Lua上的异步封装可以参考我之前的文章Lua中优化的异步封装。如果必须使用同步接口进行加载,这边有两个解决方法:
- 通过Resources.Load进行加载,将资源放到Resources文件夹下。WebGL在打包的时候。会将这部分的资源以二进制源文件的形式,一起编译到WASM中,使得逻辑代码可以正常同步加载
- 通过提前加载ab文件,在确定资源所依赖的ab文件都在内存中,通过ab去加载资源,当然可以使用同步接口。项目在处理Lua文件的require接口,也是这样处理的。项目启动的时候,先将Lua所有的ab都加载到内存里面,然后在启动Lua虚拟机进行Lua的调用
- 业务上所有的资源加载都通过异步加载进行,Lua上的异步封装可以参考我之前的文章Lua中优化的异步封装。如果必须使用同步接口进行加载,这边有两个解决方法:
- 原因:
- Web端是没有文件IO系统,并且因为Web开箱即用的特性,所有的资源必须在远端服务器上
6. Lua虚拟机 GC线程问题
-
问题:
- WebGL平台对线程支持不完善,在Lua虚拟机GC的时候,会看到Web端有个报错:
ArgumentException: Destination array was not long enough. Check destIndex and length, and the array's lower bounds
- WebGL平台对线程支持不完善,在Lua虚拟机GC的时候,会看到Web端有个报错:
-
解决方法:
- XLua GitHub上有个兄弟有个解决方案:https://github.com/Tencent/xLua/issues/741,亲测有效
-
原因:
- 具体原因链接上有讨论,可以详细看一看
7. InputField在手机浏览器无法输入问题
- 问题:
- WebGL中无法识别InputField唤起键盘,进行输入
- 解决方法:
- 引入第三方库:WebGLInput,直接在挂载InputField的go上,添加WebGLInput即可
- 这边需要注意微信小游戏平台问题,添加宏定义,小游戏平台有自己的支持方式,在使用WebInput会冲突。可以通过宏定义进行不同平台的兼容处理
8. iOS浏览器内存限制
- 问题:
- iOS浏览器下运行会崩溃,但是Android平台下正常运行
- 解决方法:
- 检查ab大小和依赖关系,尽量能够单资源单ab,减少单ab大小和ab之间的依赖关系。避免加载资源的时候,需要加载大量的ab包
- 内存分析,看是否没有对无用资源进行内存卸载。需要严格控制运行时的运行内存
- 原因:
- iOS设备的浏览器内存限制取决于设备型号和操作系统版本。一般情清况下,iPhone和iPad浏览器的内存限制分别为256MB和512MB。这意味着当页面超出了内存限制时,浏览器将会崩溃。
总结
- 当你能够导出Web端运行时,并且在Web网页上正常运行,证明已经完成了微信小游戏适配70%以上的工作
- 特别关注iOS下,游戏运行的内存限制。可以通过Unity导出带Debug信息,结合XCode工具进行调试。当iOS性能优化成功,Android基本不会有大问题