手游熱更新方案xLua開源:Unity3D下Lua編程解決方案
本文來自於騰訊Bugly公眾號(weixinBugly),未經作者同意,請勿轉載,原文地址:手游熱更新方案xLua開源:Unity3D下Lua編程解決方案
導語
xLua是Unity3D下Lua編程解決方案,自2016年初推廣以來,已經應用於十多款騰訊自研遊戲,憑藉其出色的性能,易用性,擴展性而廣受好評。
而就在前天,騰訊宣布xLua開源到github上,引起了遊戲開發圈的轟動,今天精神哥就來給大家介紹——手游熱更新方案 xLua。
xLua項目1月3日起正式對外開源
官方開源地址:
Tencent/xLua
xLua的幾項突破
xLua在功能、性能、易用性都有不少突破,這幾方面分別最具代表性的是:
- Unity3D全平台熱補丁技術,可以運行時把C#實現(方法,操作符,屬性,事件,構造函數,析構函數,支持泛化)替換成lua實現;
- 自定義struct,枚舉在Lua和C#間傳遞無C# gc alloc;
- 編輯器下無需生成代碼,開發更輕量;
初探xLua
xLua設計原則是保證了運行效率的前提下,盡量的保證開發效率。是不是簡單易用,看了下面的例子你說了算,性能好不好,實際測試說了算。
三行代碼跑lua腳本
一個完整的例子僅需3行代碼:
下載xLua後解壓到Unity工程Assets目錄下,建一個MonoBehaviour拖到場景,在Start裡頭加上這麼三行:
XLua.LuaEnv luaenv = new XLua.LuaEnv(); luaenv.DoString("CS.UnityEngine.Debug.Log(hello world)"); luaenv.Dispose();
運行就可以看到Console列印的hello world。
- 第一和第三行分別LuaEnv的創建以及銷毀,所謂LuaEnv可以理解為lua虛擬機,往往整個工程一個虛擬機即可;
- DoString裡頭可以是任意合法的lua代碼,例子中調用了UnityEngine.Debug.Log介面列印了一個log(C#的靜態函數在CS下直接可用);
C#調用lua系統函數math.max
xLua支持把一個Lua函數綁定到C# delegate。
我們先聲明一個delegate,並為它加上CSharpCallLua標籤: [XLua.CSharpCallLua] public delegate double LuaMax(double a, double b);
然後在上面那例子加上這麼兩行(luaenv銷毀前):
var max = luaenv.Global.GetInPath<LuaMax>("math.max"); Debug.Log("max:" + max(32, 12));
就那麼簡單,把lua的math.max綁定到C#的max變數後,調用就和一個C#函數調用差不多了,而且,最最重要的是,執行了」XLua/GenerateCode」後,max(32, 12)調用是不產生(C#)gc alloc的,既優雅,又高效!
更詳細的可以看XLuaDoc下的文檔。
熱補丁技術
xLua支持熱補丁,這意味著你可以:
- 開發只用C#;
- 運行也是C#,性能可以秒殺lua;
- 出問題了才用Lua來改掉C#出問題的部位,下次整體更新時換回正確的C#,能做到用戶不重啟程序fix bug;
把易用性進行到底
xLua的易用不僅僅體現在編程,還體現在方方面面的細節考慮,甚至考慮到團隊配合工作流。
xLua菜單就兩個,分別是生成代碼和清除生成代碼。
還能更簡單些么?還能!
上面兩個菜單你開發期間甚至都不用管,要build手機版本前執行一下「GenerateCode」就可以了(這也有API可集成到項目的自動化打包流程)。
這就是xLua的特色功能之一:編輯器下無需生成代碼支持所有特性。
之所以做這個功能,是因為有的項目反饋,「生成代碼」對於策劃美術太高大上了一點,教了很久還是老忘;還有個大項目反饋說由於代碼很多,每次生成代碼後,Unity菊花都要轉很久。
想項目所想,急項目所急本是我輩風範,就做了,儘管挺麻煩的。
無縫支持生成代碼及反射
生成代碼固然重要,已然是各大主流方案的標配。
反射有的方案明確不支持,但從項目的反饋來說,也是至關重要的:有的項目代碼很多,已經接近蘋果的80M Text段的限制,對他們來說,代碼量大小關乎到能否發布,反射方式性能不如生成代碼,但對安裝包影響小。
這的無縫有兩個含義:
- 兩者在支持的特性以及特性的使用方式都是一致的,兩者方式間切換,業務邏輯代碼不用修改,改改配置就可以了;
- 兩者無縫配合,比如一個繼承鏈上,任意一個類都可以選擇生成代碼或者反射,比如子類選擇生成代碼,父類由於不常用選擇了反射,還是可以在子類對象上調用父類的方法;
對於il2cpp的stripping,xLua也考慮到了,只要你對一個類配置了ReflectionUse,會自動生成Unity的link.xml配置文件,將該類型列為不剪裁。
關於性能
作為一個基礎庫,性能是至關重要的,其中又有一項指標是大家極為關注的:C#側的gc alloc,xLua在這塊做了不少創新。
正如前面例子所示,xLua支持把一個Lua函數綁定到一個C# delegate,這可以避免值類型在參數傳遞時產生的gc。
另外,在複雜值類型表達方面,xLua也取得相當突破。只要一個struct只包含值類型,配置了GCOptimize後,其參數傳遞,數組訪問無gc。
所有枚舉,配置了GCOptimize後無gc;
更多無GC的用法可以參見配套的例子(XLuaExamples 5_NoGc)。
不僅僅GC優化這塊,Lua和C#間相互調用性能也可圈可點,具體可以關注我們發出的性能測試報告。
PS:我們的性能測試沒有用網上流傳較廣的那套用例。我們認為其並不合理:
- 測試Lua調用C#部分用例選擇了Vector3,這其實是錯誤的,市面上大多方案的Vector3是完全在Lua測重新實現,完全沒有達到測試「Lua調用C#」的目的;
- 測試全部用Unity API做測試,這並不合理,Unity API本身的開銷會影響到測試結果。舉個例子:方案A的Lua調用C#函數開銷是1ms,方案B是2ms,那麼結論應該是A方案性能是B方案兩倍,但如果被調用C#函數本身耗時100ms,那結論就是兩個方案性能差不多,甚至有時會因為誤差得出B方案性能更好的結論。
更合理的做法是用空負荷的類型(函數是空的,property,event等也不要有運算開銷),xLua所有測試用例都是基於這個原則。
擴展性
開發中我們往往要用到很多東西,比如用PB和後台交互,解析json格式的配置文件等等。雖說我們都可以在C#那找到相應的庫,然後通過xLua去使用這些庫,但這效率不高,最好能有相應Lua的庫。
不少方案是直接集成一些常用的Lua庫,但這帶來些新問題:
- 這些庫不一定用到,卻增大安裝包;
- 集成的庫也不一定符合項目習慣:json解析有人喜歡rapidjson,有人愛用cjson,所謂眾口難調;
- 對於某些項目,這些庫還是不夠,還是得自己去想辦法加;
我們的原則是授之以魚,不如授之以漁,xLua在這方面的支持是:
- 提供了介面,教程,讓大家可以不修改xLua代碼就可以加入自己喜歡的庫;
- xLua用cmake實現跨平台編譯,大家可以選擇伴隨xLua一起編譯,修改一個makefile文件,搞定各平台編譯。
除了很方便加入第三方Lua插件,xLua的生成引擎支持二次開發,可以編寫生成插件,生成自己所需的一些代碼,配置。
後續持續支持
用開源,最怕碰到情況是:作者已「死」,有事燒香。「死」主要是說不維護了,可能是作者太忙,或者沒興趣了,或者寫著寫著感覺進入一個死胡同,重構(做)了個完全不一樣的版本,原來版本直接拋棄等等。
用xLua就沒這方面的擔心,大公司支持,開發,測試,答疑都有全職的團隊。騰訊自己都有很多項目在使用呢。
版本開發流程很正規,較大版本會做一次主流機型的適配測試,用著就放心。
總結一下
xLua推廣以來,用心傾聽應用項目的需求,在易用性,性能,擴展性等方面得到長足的進步,開源後也將會繼續秉承這個這原則,做一款有誠意的庫。
更多精彩內容歡迎關注騰訊 Bugly的微信公眾賬號:
騰訊 Bugly是一款專為移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智能合併功能幫助開發同學把每天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精準定位功能幫助開發同學定位到出問題的代碼行,實時上報可以在發布後快速的了解應用的質量情況,適配最新的 iOS, Android 官方操作系統,鵝廠的工程師都在使用,快來加入我們吧!
推薦閱讀:
※為什麼很多編程語言用 end 作為區塊結束符,而放棄花括弧?
※Lua 語言有哪些不足?
※從零開始製作2048遊戲
※為什麼Lua不支持大多數編程語言都有的continue,卻非得支持一般情況下用得很少的 repeat until ?