Unity构建微信小游戏

目录

序言

Unity构建WebGL的简介

接入小游戏环境的流程

接入微信小游戏SDK

接入C插件,以XLua为例

以C源码形式(Unity 2018~2021.1)

以emsdk预编译形式(2021.2及以后版本)

WebGL/小游戏的环境限制和修改

不支持多线程

修改方法

不支持Socket

修改方法


序言

目前微信小游戏越发火爆,各大厂商纷纷试水,作为开发者也是值得一试的。

微信小游戏官方提供了适配Unity的SDK,可以很方便让Unity构建出微信小游戏。这次分享下博主的接入流程和其中遇到的问题。

首先说明下,虽然微信小游戏是基于Unity WebGL平台构建出来的,但小游戏平台和WebGL平台在接口上有很大不同。博主只有用Unity构建微信小游戏环境的经历,没有构建和运行纯正的WebGL平台环境,所以下面的接入内容不保证对纯WebGL平台上的扩展和兼容性。

内容算是抛砖引玉,有任何问题和疑问欢迎指正和讨论。

博主如果有新经历内容随时补充~

Unity构建WebGL的简介

WebGL,简单来说即一套API,在浏览器下提供了渲染能力。一般在Unity的语境里说构建网页游戏/WebGL游戏/Html游戏基本是指同一件事,简言之,就是运行在浏览器里的网页游戏。

首先大致介绍下,Unity如何提供WebGL环境的支持。

目前主流的浏览器里除了能运行JS代码,也支持了一种名为WebAssembly(简称为Wasm)的代码。Unity构建WebGL借助了Emscripten工具,通过将游戏工程里的代码转换成Wasm来运行在浏览器里。

Main — Emscripten 3.1.66-git (dev) documentation

Emscripten工具提供了很多能力,选几个主要的来讲,第一,可以把C/C++代码转换为浏览器环境用的WebAssembly代码。第二,提供了一部分的POSIX接口封装支持(POSIX是可移植操作系统接口的缩写,常见接口比如C++里用的文件读写、网络、线程等等)。第三,提供了交互能力,可以让C和JavaScript代码相互调用,比如C调用JS函数,JS调用C函数。

所以Unity构建WebGL时,工程里写的C#代码=>经过il2cpp转成C++代码=>经过Emscripten转成Wasm代码。Unity构建WebGL完成后,生成一个作为入口的html文件,这样就可以让浏览器打开html=>调用JS=>JS加载Wasm代码,来执行游戏了。

接入小游戏环境的流程

接入微信小游戏SDK

微信小游戏SDK现在作为一个Unity的Package由小游戏官方提供,直接引入即可。

SDK提供了封装好了小游戏的环境的C#版API,封装了Unity在WebGL上用到的JS层的一些API。

项目最需要改造的地方IO接口,最好是统一抽一层读写接口。小游戏平台上的IO需要使用小游戏提供的单独接口,这个可以支持向手机微信App的特定外部目录读写文件。而Unity WebGL会把C#的File.Open/Read/Write这种接口实现成通过浏览器IndexeDB做的虚拟文件系统,路径是形如『/idbfs/xxxxxx』这样的。所以如果小游戏在运行的时候日志里提示用idbfs/这种路径,那基本是某处File接口漏改了。

小游戏SDK参与Unity构建的时提供适配方式也很有意思。Unity构建WebGL时,会生成一份webgl.framework.js,这个JS文件里的函数是提供给Unity的C#代码用的。因为webgl.framework.js文件里很多JS代码都是Unity预设好的,没法提前修改,所以小游戏SDK在Unity构建完成后,还得用规则匹配的方式,把里面的所有JS代码做一遍替换,改成适配小游戏环境的接口,哈哈哈,也是辛苦小游戏官方了,每次Unity出新版本都可能改这些预设的JS接口,总能搞出点新花样让小游戏SDK来适配。

接入C插件,以XLua为例

项目因为性能或者功能等各种原因,会需要接入C插件,这里以接入XLua库为例

lua虚拟机代码是C代码,同时XLua会在此基础上接入一些C/C++的第三方库。对于这些代码,Unity WebGL支持以C源码形式或者预编译形式的插件。

Unity WebGL接入lua虚拟机的话有2种方式,第一种是C源码形式,第二种是预编译插件形式(类似Android xlua.so或者iOS的xlua.a)

以C源码形式(Unity 2018~2021.1)

对于比较早的Unity 2018~2021.1,可以使用C源码形式。参照 XLua作者的样例,把C源码放在Assets/Plugins/WebGL/目录下,让这些代码参与构建 chexiongsheng/build_xlua_with_libs: 为xLua集成几个常用库,方便使用 (github.com)

这个构建方式为了C代码依赖关系的正确引用,会把很多不同模块C源码文件放到同一个目录下。这样可能不太方便维护,因为通常情况下我们可能需要维护多个平台的XLua库构建,每次修改C代码要为WebGL平台单独处理一次目录结构会比较费力。

Unity提供了Emscripten的编译参数控制构建C源码,所以我们可以通过构建前添加 -D 参数来添加编译宏,通过-I 参数添加搜索路径,这样我们可以保持XLua库的代码结构,仅需要添加代码的搜索路径来让一个模块的源码目录被其他模块引用到。

下面就是一个修改的例子,在WebGLPlugin目录下保持了lua虚拟机源码的目录为lua-5.3.5/src/,然后把src目录全路径配置到搜索路径中,就可以让其他的模块引用lua虚拟机源码

C#代码如下:

// -D 配置宏定义,参照cmake配置需求,比如XLua开启字节码兼容
PlayerSettings.WebGL.emscriptenArgs += " -DLUAC_COMPATIBLE_FORMAT=1 ";
// -I 配置搜索路径,参照cmake配置需求,用了哪个模块目录就配哪个
PlayerSettings.WebGL.emscriptenArgs += " -IUser/admin/UnityProject/WebGLPlugins/lua-5.3.5/src ";
PlayerSettings.WebGL.emscriptenArgs += " -IUser/admin/UnityProject/WebGLPlugins/lua-rapidjson/include ";
// 其他需要的配置路径等等

注意:这种设置 -D/-I方式在新版Unity改用Bee构建系统之后就失效了,因为Bee构建流程把编译Plugins/WebGL/目录下的C源码改成了单独编译,我们自定义的编译参数是没法直接应用到C源码了。所以此时可以考虑换成以emsdk预编译的形式

以emsdk预编译形式(2021.2及以后版本)

Unity - Manual: Web native plug-ins for Emscripten (unity3d.com)

Unity从2021.2及以后版本的WebGL平台,支持了GNU .a文件,也就是我们可以用Emscripten SDK把C源码编译成.a插件放到Plugins/WebGL下。又因为emsdk是支持cmake文件的,所以这个方式可以做到和XLua的cmake构建流程基本一致。

用法上也很简单,参考Emscripten官方文档Building Projects — Emscripten 3.1.66-git (dev) documentation

我们加个用来构建WebGL插件的shell脚本在macos环境用,假设我们安装的是Unity 2022.3.38f1

# Unity装了一份Emscripten,可以直接用,注意做好目录备份,避免改坏了得重装Unity WebGL模块
UNITY_EM_TOOL=/Applications/Unity/Hub/Editor/2022.3.38f1/PlaybackEngines/WebGLSupport/BuildTools/Emscripten
# 给Emscripten设置环境
export EM_CONFIG=${UNITY_EM_TOOL}/.emscripten
# 把emcmake/emmake所在目录加入PATH,方便调用
export PATH=${UNITY_EM_TOOL}/emscripten:${PATH}

# 参照XLua的其他平台构建创建个类似目录
BUILD_PATH=build_webgl
mkdir -p ${BUILD_PATH} && cd ${BUILD_PATH}
cd ..

# 相当于在调用 cmake -H. -B${BUILD_PATH} -Dxxx -Dxxx
emcmake cmake -S . -B${BUILD_PATH} \
    -DLUAC_COMPATIBLE_FORMAT=ON \
    -DCMAKE_BUILD_TYPE=Release \
# 相当于在调用 cmake --build ${BUILD_PATH}
emmake make -C ${BUILD_PATH}

这样,就把libxlua.a文件就生成到build_webgl目录下了,然后把libxlua.a放到项目的Assets/Plugins/WebGL目录下即可,就不需要放C源码了。

WebGL/小游戏的环境限制和修改

WebGL环境有诸多限制包括并不限于受限支持多线程,不支持socket等等,这里讲下问题和解决方法

不支持多线程

先说结论,浏览器环境允许多线程,但不要太指望Unity的WebGL有多线程。

那么问题来了,哪些环节支持多线程能力?

第一说浏览器,JS代码本身是单线程运行的。但浏览器提供了Web Worker,可以实现多个Web Worker上执行各自的JS代码,所以浏览器是支持多线程的。

二说Emscripten工具,Emscripten本身提供了对POSIX的多线程封装,可以把C/C++多线程转换用Web Worker来实现,但Emscripten多线程依赖浏览器启用ShareArrayBuffer,要浏览器启用SharedArrayBuffer就要网站配置跨域策略。所以Emscripten是受限支持多线程,取决于网站服务是否支持。

三说Unity,对不起做不到,只有native层(C/C++)多线程可以转换。C#层多线程依然不能转换到WebGL,即使是最新的Unity 6000版本也不行。

四说小游戏,有且仅有一个预设的Web Worker,目前博主还没懂这个Web Worker能做什么。

为什么浏览器必须要求网站配置跨域策略才能开启ShareArrayBuffer?因为2016年发现了熔断漏洞和幽灵漏洞,代码借助SharedArrayBuffer和高精计数器能越权访问操作系统内核,Intel和AMD的CPU大面积受影响。所以浏览器只在网站配置跨域策略,确保安全时才开启用ShareArrayBuffer,提供高精度计时器

笑话一则,近年来声名鹊起的Godot游戏引擎,在4.2版本相当激进(太过超前)的要求WebGL包强制启用SharedArrayBuffer多线程,结果出的包在不配跨域策略的网站就不能用。后来Godot官方文章直呼『我们押错宝了!』(原文写的:Betting on the wrong horse),4.3版本光速改进增加WebGL用单线程版本的选项。

修改方法

小游戏环境下还请忍痛割爱,根据逻辑判断出小游戏平台,把多线程写法改用Unity协程或自己实现定时器分帧处理逻辑。千万注意,C#不仅System.Threading是多线程,System.Timers内部也是多线程,小游戏环境如果误用System.Timers是不会触发定时回调的。关于C#的这些命名空间里实现可以查阅微软官方文档 Reference Source (microsoft.com) 或者github上搜微软官方代码仓库。

不支持Socket

浏览器环境不支持传统的Socket,所以Unity里使用的C# System.Net.Sockets命名空间近乎不能用在WebGL和小游戏环境用的。为什么说是近乎,毕竟Sockets命名空间里还定义了一些枚举,这些枚举还是能用的。

修改方法

替换方案用WebSocket。注意浏览器的规范里,不提供发送控制帧能力接口,也就是说用WebSocket不能直接用Ping/Pong Frame,需要自行在业务逻辑约定客户端和服务器进行Ping/Pong。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值