手游熱更新方案xLua開源:Unity3D下Lua編程解決方案

本文來自於騰訊Bugly公眾號(weixinBugly),未經作者同意,請勿轉載,原文地址:手游熱更新方案xLua開源:Unity3D下Lua編程解決方案

導語

xLua是Unity3D下Lua編程解決方案,自2016年初推廣以來,已經應用於十多款騰訊自研遊戲,憑藉其出色的性能,易用性,擴展性而廣受好評。

而就在前天,騰訊宣布xLua開源到github上,引起了遊戲開發圈的轟動,今天精神哥就來給大家介紹——手游熱更新方案 xLua。

xLua項目1月3日起正式對外開源

官方開源地址:

Tencent/xLua

xLua的幾項突破

xLua在功能、性能、易用性都有不少突破,這幾方面分別最具代表性的是:

  1. Unity3D全平台熱補丁技術,可以運行時把C#實現(方法,操作符,屬性,事件,構造函數,析構函數,支持泛化)替換成lua實現;
  2. 自定義struct,枚舉在Lua和C#間傳遞無C# gc alloc;
  3. 編輯器下無需生成代碼,開發更輕量;

初探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。

  1. 第一和第三行分別LuaEnv的創建以及銷毀,所謂LuaEnv可以理解為lua虛擬機,往往整個工程一個虛擬機即可;
  2. 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支持熱補丁,這意味著你可以:

  1. 開發只用C#;
  2. 運行也是C#,性能可以秒殺lua;
  3. 出問題了才用Lua來改掉C#出問題的部位,下次整體更新時換回正確的C#,能做到用戶不重啟程序fix bug;

把易用性進行到底

xLua的易用不僅僅體現在編程,還體現在方方面面的細節考慮,甚至考慮到團隊配合工作流。

xLua菜單就兩個,分別是生成代碼和清除生成代碼。

還能更簡單些么?還能!

上面兩個菜單你開發期間甚至都不用管,要build手機版本前執行一下「GenerateCode」就可以了(這也有API可集成到項目的自動化打包流程)。

這就是xLua的特色功能之一:編輯器下無需生成代碼支持所有特性。

之所以做這個功能,是因為有的項目反饋,「生成代碼」對於策劃美術太高大上了一點,教了很久還是老忘;還有個大項目反饋說由於代碼很多,每次生成代碼後,Unity菊花都要轉很久。

想項目所想,急項目所急本是我輩風範,就做了,儘管挺麻煩的。

無縫支持生成代碼及反射

生成代碼固然重要,已然是各大主流方案的標配。

反射有的方案明確不支持,但從項目的反饋來說,也是至關重要的:有的項目代碼很多,已經接近蘋果的80M Text段的限制,對他們來說,代碼量大小關乎到能否發布,反射方式性能不如生成代碼,但對安裝包影響小。

這的無縫有兩個含義:

  1. 兩者在支持的特性以及特性的使用方式都是一致的,兩者方式間切換,業務邏輯代碼不用修改,改改配置就可以了;
  2. 兩者無縫配合,比如一個繼承鏈上,任意一個類都可以選擇生成代碼或者反射,比如子類選擇生成代碼,父類由於不常用選擇了反射,還是可以在子類對象上調用父類的方法;

對於il2cpp的stripping,xLua也考慮到了,只要你對一個類配置了ReflectionUse,會自動生成Unity的link.xml配置文件,將該類型列為不剪裁。

關於性能

作為一個基礎庫,性能是至關重要的,其中又有一項指標是大家極為關注的:C#側的gc alloc,xLua在這塊做了不少創新。

正如前面例子所示,xLua支持把一個Lua函數綁定到一個C# delegate,這可以避免值類型在參數傳遞時產生的gc。

另外,在複雜值類型表達方面,xLua也取得相當突破。只要一個struct只包含值類型,配置了GCOptimize後,其參數傳遞,數組訪問無gc。

所有枚舉,配置了GCOptimize後無gc;

更多無GC的用法可以參見配套的例子(XLuaExamples5_NoGc)。

不僅僅GC優化這塊,Lua和C#間相互調用性能也可圈可點,具體可以關注我們發出的性能測試報告。

PS:我們的性能測試沒有用網上流傳較廣的那套用例。我們認為其並不合理:

  1. 測試Lua調用C#部分用例選擇了Vector3,這其實是錯誤的,市面上大多方案的Vector3是完全在Lua測重新實現,完全沒有達到測試「Lua調用C#」的目的;
  2. 測試全部用Unity API做測試,這並不合理,Unity API本身的開銷會影響到測試結果。舉個例子:方案A的Lua調用C#函數開銷是1ms,方案B是2ms,那麼結論應該是A方案性能是B方案兩倍,但如果被調用C#函數本身耗時100ms,那結論就是兩個方案性能差不多,甚至有時會因為誤差得出B方案性能更好的結論。

更合理的做法是用空負荷的類型(函數是空的,property,event等也不要有運算開銷),xLua所有測試用例都是基於這個原則。

擴展性

開發中我們往往要用到很多東西,比如用PB和後台交互,解析json格式的配置文件等等。雖說我們都可以在C#那找到相應的庫,然後通過xLua去使用這些庫,但這效率不高,最好能有相應Lua的庫。

不少方案是直接集成一些常用的Lua庫,但這帶來些新問題:

  1. 這些庫不一定用到,卻增大安裝包;
  2. 集成的庫也不一定符合項目習慣:json解析有人喜歡rapidjson,有人愛用cjson,所謂眾口難調;
  3. 對於某些項目,這些庫還是不夠,還是得自己去想辦法加;

我們的原則是授之以魚,不如授之以漁,xLua在這方面的支持是:

  1. 提供了介面,教程,讓大家可以不修改xLua代碼就可以加入自己喜歡的庫;
  2. xLua用cmake實現跨平台編譯,大家可以選擇伴隨xLua一起編譯,修改一個makefile文件,搞定各平台編譯。

除了很方便加入第三方Lua插件,xLua的生成引擎支持二次開發,可以編寫生成插件,生成自己所需的一些代碼,配置。

後續持續支持

用開源,最怕碰到情況是:作者已「死」,有事燒香。「死」主要是說不維護了,可能是作者太忙,或者沒興趣了,或者寫著寫著感覺進入一個死胡同,重構(做)了個完全不一樣的版本,原來版本直接拋棄等等。

用xLua就沒這方面的擔心,大公司支持,開發,測試,答疑都有全職的團隊。騰訊自己都有很多項目在使用呢。

版本開發流程很正規,較大版本會做一次主流機型的適配測試,用著就放心。

總結一下

xLua推廣以來,用心傾聽應用項目的需求,在易用性,性能,擴展性等方面得到長足的進步,開源後也將會繼續秉承這個這原則,做一款有誠意的庫。

更多精彩內容歡迎關注騰訊 Bugly的微信公眾賬號:

騰訊 Bugly是一款專為移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智能合併功能幫助開發同學把每天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精準定位功能幫助開發同學定位到出問題的代碼行,實時上報可以在發布後快速的了解應用的質量情況,適配最新的 iOS, Android 官方操作系統,鵝廠的工程師都在使用,快來加入我們吧!


推薦閱讀:

為什麼很多編程語言用 end 作為區塊結束符,而放棄花括弧?
Lua 語言有哪些不足?
從零開始製作2048遊戲
為什麼Lua不支持大多數編程語言都有的continue,卻非得支持一般情況下用得很少的 repeat until ?

TAG:Unity游戏引擎 | Lua | 游戏开发 |