1. 程式人生 > >Unity WebGL 手遊移植 一

Unity WebGL 手遊移植 一

到今天為止,專案已經上線一個多月了,目前穩定執行,各種 bug 也是有的。至少得到了蘋果的兩次推薦和 TapTap 一次首頁推薦,也算是結項後第一時間對我們專案的一個肯定。   出於各種各樣的可描述和不可描述之原因,我們現在需要把專案移植到 Web 端,第一次被告知這個需求時我直接給出了不可能的答覆,之前從來沒有考慮過這個平臺的相容性,現在專案算是做完了結果要這樣折騰一番我覺得是需要消耗非常可怕的人力物力但未必能有很好的效果,價效比很低,但是最終我還是妥協了,硬著頭皮接下來,也硬著頭皮上,畢竟,技術依然是為了市場和商業服務。   既然接下了這個活兒(很不情願的),那就得開始尋找方案,運營方提出使用 Unity 已經放棄維護的 WebPlayer,這個方案我們內部商討後決定放棄,直接奔向 WebGL。   在記錄這些文字時,我尚未完全解決所有的基礎問題,也許最終無法通過或者受限於技術能力而夭折,但是通過這些記錄也許能幫到後面想要踩這些坑的人。   查找了 Unity 的官方資料,我們如果需要使用 WebGL 需要面對以下幾個挑戰:
  1. Native Plugin:也就是說各種原生外掛(C/C++等編譯的本地機器碼庫),我們的挑戰是使用了 SLua。
  2. 多執行緒:WebGL 端無法支援任何多執行緒程式碼,因為 JavaScript 沒有多執行緒的實現,C# 端使用的類似 System.Threading 等庫最終都不會被編譯成相應的 js 程式碼。
  3. 網路模組:傳統的 Socket 無法使用,必須使用 WebSocket 或者 xxx,System.Net,尤其是 UnityEngine.Net.Sockets 都未在 WebGL 端實現,所以將無法被正確編譯轉換;Unity 中可以使用 WWW 和 UnityWebRequest,或者使用新版支援 WebGL 的 Unity Networking
     API;或者直接在 JavaScript 中使用 WebSockets 和 WebRTC 來實現網路層功能。
  4. 渲染:WebGL 的圖形 api 是基於 OpenGL ES 2.0;GI 只支援 Baked GI(我們沒使用);Procedural Materials 不支援(我們沒使用);Linear Rendering 不支援(我們沒使用);MovieTextures 不支援(我們沒使用);WebGL Shader code restrictions:目前理解為在 shader 程式碼中只支援使用常量,迴圈的索引值或者聯合體來作為訪問陣列和矩陣的索引,唯一的例外是在 Vertex Shader 中訪問 uniform 時可以使用任意的表示式,另外還有迴圈的限制,不可以使用 “計數迴圈-初始化一個變數時賦給一個常量值,每次迴圈時增加或減少一個常量值” 以外的方式,並且不支援 while 迴圈。(目前大致看來我們沒有使用陣列或矩陣的下標表達式,也沒有使用複雜的迴圈,後期可能還需要仔細排查)。
  5. Audio 有幾乎一大半的 api 不支援,後面需要做相容修改,應該不少麻煩。
  6. 其它暫不考慮,以上幾項直接決定我們是否可以先 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),所有程式碼都在
(LuaSrcDir)/src 中,將 slua.c 也拷貝進來,但是要排除 lua.c,luac.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, exclude lua.c, luac.c.
//#include "$(LuaSrcRelativePath)/src/lua.c"
//#include "$(LuaSrcRelativePath)/src/luac.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 部分已完成,可以往下走了。