到今天为止,项目已经上线一个多月了,目前稳定运行,各种 bug 也是有的。至少得到了苹果的两次推荐和 TapTap 一次首页推荐,也算是结项后第一时间对我们项目的一个肯定。
出于各种各样的可描述和不可描述之原因,我们现在需要把项目移植到 Web 端,第一次被告知这个需求时我直接给出了不可能的答复,之前从来没有考虑过这个平台的兼容性,现在项目算是做完了结果要这样折腾一番我觉得是需要消耗非常可怕的人力物力但未必能有很好的效果,性价比很低,但是最终我还是妥协了,硬着头皮接下来,也硬着头皮上,毕竟,技术依然是为了市场和商业服务。
既然接下了这个活儿(很不情愿的),那就得开始寻找方案,运营方提出使用 Unity 已经放弃维护的 WebPlayer,这个方案我们内部商讨后决定放弃,直接奔向 WebGL。
在记录这些文字时,我尚未完全解决所有的基础问题,也许最终无法通过或者受限于技术能力而夭折,但是通过这些记录也许能帮到后面想要踩这些坑的人。
查找了 Unity 的官方资料,我们如果需要使用 WebGL 需要面对以下几个挑战:
- Native Plugin:也就是说各种原生插件(C/C++等编译的本地机器码库),我们的挑战是使用了 SLua。
- 多线程:WebGL 端无法支持任何多线程代码,因为 JavaScript 没有多线程的实现,C# 端使用的类似 System.Threading 等库最终都不会被编译成相应的 js 代码。
- 网络模块:传统的 Socket 无法使用,必须使用 WebSocket 或者 xxx,System.Net,尤其是 UnityEngine.Net.Sockets 都未在 WebGL 端实现,所以将无法被正确编译转换;Unity 中可以使用 WWW 和 UnityWebRequest,或者使用新版支持 WebGL 的 Unity Networking API;或者直接在 JavaScript 中使用 WebSockets 和 WebRTC 来实现网络层功能。
- 渲染:WebGL 的图形 api 是基于 OpenGL ES 2.0;GI 只支持 Baked GI(我们没使用);Procedural Materials 不支持(我们没使用);Linear Rendering 不支持(我们没使用);MovieTextures 不支持(我们没使用);WebGL Shader code restrictions:目前理解为在 shader 代码中只支持使用常量,循环的索引值或者联合体来作为访问数组和矩阵的索引,唯一的例外是在 Vertex Shader 中访问 uniform 时可以使用任意的表达式,另外还有循环的限制,不可以使用 “计数循环-初始化一个变量时赋给一个常量值,每次循环时增加或减少一个常量值” 以外的方式,并且不支持 while 循环。(目前大致看来我们没有使用数组或矩阵的下标表达式,也没有使用复杂的循环,后期可能还需要仔细排查)。
- Audio 有几乎一大半的 api 不支持,后面需要做兼容修改,应该不少麻烦。
- 其它暂不考虑,以上几项直接决定我们是否可以先 Port 出来,效率问题都先不考虑。
先从 Native Plugin 入手,Lua 这是需要迈过的第一道坎儿。官方给了两个很好的文档:WebGL: Interacting with browser scripting 和 Unity WebGL中的底层插件,WebGL 是通过 IL2CPP 将所有的 C# 代码转换成 C++,这样便可以使用基于 LLVM 的 Emscripten 的工具链将所有的 C++ 代码编译成基于 asm.js 的 JavaScript 代码,这样便可以在支持 Html5 的浏览器上运行。
我们使用了 SLua 插件,所以我现在需要使用 Lua 的源代码来参与编译和打包过程即可。很庆幸我们项目在今年大家开会讨论后决定从原来的 LuaJit 升级为 Lua 5.3,如果是 LuaJit 项目本身的编译产生了大量针对目标平台的汇编代码来最终生成的,具有极大的平台特异性,所以就算是使用 LuaJit 的源码也是无法使用 WebGL 的,依然需要直接使用 Lua 5.1 或者 5.3 的源码。
新建一个空的 Unity 工程,导入 SLua 插件,切换到 WebGL 平台,在 Plugins 中新建文件夹 WebGL,新建一个 C 代码文件比如:lua.dll.c,然后将最新版 Lua5.3 源码解压到本地的一个目录:$(LuaSrcDir),所有代码都在 $(LuaSrcDir)/src 中,将 slua.c 也拷贝进来。
在 lua.dll.c 中加入以下内容:
#define LUA_COMPAT_5_1 #define LUA_COMPAT_5_2 // Lua source code only, relative . #include “$(LuaSrcRelativePath)/src/lapi.c” #include “$(LuaSrcRelativePath)/src/lauxlib” #include “$(LuaSrcRelativePath)/src/lbaselib.c” … // Add all lua source file *.c #include “$(LuaSrcRelativePath)/src/slua.c”
注意:以上所有内容都是添加 Lua 的源文件,不包括头文件,具体开头使用要使用哪些预编译宏,取决于你的项目。
另外由于 Lua 5.3 向下兼容的问题,如果定义了 LUA_COMPAT_5_1后,LUA_COMPAT_MODULE 会被定义,那么就会编译兼容实现:luaL_findtable,而 SLua 中为了兼容多写了一份,所以这时候可以删掉 slua.c 中的实现,否则编译会出现重定义的错误。
接下来在 Unity Player Setting 中加入预编译宏 LUA_5_3 将 SLua 切换到 5.3 的实现版本,然后就直接将某个示例场景添加的构建列表,BuildAndRun,就可以看到 SLua 的 Demo 场景正确的运行在浏览器上了。
至此,Lua 的 Native Plugin 部分已完成,可以往下走了。