xLua是Unity3D下Lua編程解決方案,自2016年初推廣以來,已經應用於十多款騰訊自研遊戲,因其良好性能、易用性、擴展性而廣受好評。如今,騰訊已經將xLua開源到GitHub。git
2016年12月末,xLua剛剛實現新的突破:全平臺支持用Lua修復C#代碼bug。github
目前Unity下的Lua熱更新方案大多都是要求要熱更新的部分一開始就要用Lua語言實現,不足之處在於:編程
xLua熱補丁技術支持在運行時把一個C#實現(函數,操做符,屬性,事件,或者整個類)替換成Lua實現,意味着你能夠:c#
xLua插件下載地址:https://github.com/Tencent/xLua數組
建立工程並導入xLua插件安全
經過xLua插件運行lua程序app
using System.Collections; using System.Collections.Generic; using UnityEngine; using XLua; public class MyHelloWorld : MonoBehaviour { void Start () { // 建立lua環境 LuaEnv luaenv = new LuaEnv(); // 運行Lua代碼 luaenv.DoString("print('Hello World')"); // 關閉Lua環境 luaenv.Dispose(); } }
能夠看到,輸出了打印,前綴有Lua的標識表示這是由Lua中的方法執行的函數
反過來,也可使用lua調用C#中的程序性能
using System.Collections; using System.Collections.Generic; using UnityEngine; using XLua; public class MyHelloWorld : MonoBehaviour { void Start () { // 建立lua環境 LuaEnv luaenv = new LuaEnv(); // 運行Lua代碼 //luaenv.DoString("print('Hello World')"); luaenv.DoString("CS.UnityEngine.Debug.Log('Hello World')"); // 關閉Lua環境 luaenv.Dispose(); } }
這個時候,打印前就沒有Lua標識符了,表示這是由C#中代碼執行的測試
上面是C#和Lua之間的簡單調用,可是在實際工做中,咱們不可能這麼寫。咱們的作法是寫好Lua文件後,在C#中加載這個文件,而後使用其中的函數功能。
首先咱們建立好一個Lua文件,而後在C#中加載後使用
using System.Collections; using System.Collections.Generic; using UnityEngine; using XLua; public class MyHello : MonoBehaviour { void Start () { TextAsset t = Resources.Load<TextAsset>("helloworld.lua"); LuaEnv luaenv = new LuaEnv(); luaenv.DoString(t.ToString()); luaenv.Dispose(); } }
注意:在加載的時候,咱們使用的是TextAsset文本格式,它默認識別的後綴爲.txt,因此咱們上面建立的lua文件後綴不是.lua,可是爲了讓咱們方便的看出它是一個lua文件,因此取名的時候使用.lua.txt。
除了上面的加載方法外,更經常使用的方法是使用require加載
using System.Collections; using System.Collections.Generic; using UnityEngine; using XLua; public class MyHello : MonoBehaviour { void Start () { LuaEnv luaenv = new LuaEnv(); luaenv.DoString("require 'helloworld'"); luaenv.Dispose(); } }
require其實是調一個個的loader去加載,有一個成功則再也不往下嘗試,所有失敗則報文件找不到。目前Lua除了原生的loader外,還添加了從Resources加載的loader,須要注意的是Resources只支持有限的後綴,放在Resources下的lua文件須要加上.txt後綴。
咱們發現上面的lua文件都是放在Resources文件夾下,由於原生的loader會在這個下面去加載。在咱們的項目中,可能咱們的lua文件放在自定義的文件夾下,這個時候就須要咱們自定義loader,在xLua加自定義loader是很簡單的,只涉及到一個接口:
public delegate byte[] CustomLoader(ref string filepath);
public void LuaEnv.AddLoader(CustomLoader loader)
經過AddLoader能夠註冊個回調,該回調參數是字符串,lua代碼裏頭調用require時,參數將會透傳給回調,回調中就能夠根據這個參數去加載指定文件,若是須要支持調試,須要把filepath修改成真實路徑傳出。該回調返回值是一個byte數組,若是爲空表示該loader找不到,不然則爲lua文件的內容。
using System.Collections; using System.Collections.Generic; using UnityEngine; using XLua; using System.IO; public class CreateNewLoader : MonoBehaviour { void Start () { LuaEnv luaenv = new LuaEnv(); // 自定義loader luaenv.AddLoader(MyLoader); luaenv.DoString("require 'newloaderText'"); luaenv.Dispose(); } private byte[] MyLoader(ref string filePath) { string absPath = Application.streamingAssetsPath + "/" + filePath + ".lua.txt"; return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(absPath)); } }
上面代碼中咱們定義的lua文件爲「newloaderText.lua」,該文件位於「StreamingAssets」文件夾下,該文件夾與Assets文件夾同級,因此在後面設置路徑的時候使用系統自帶的函數「Application.streamingAssetsPath」能夠找到該文件夾。固然,咱們也能夠自定義文件夾的位置,後面的路徑改一下就行。
上面的執行過程,註冊回調後,調用require的時候,將「newloaderText」傳遞給回調函數"MyLoader",在此回調函數中咱們加載到指定文件而後傳回來使用。
using System.Collections; using System.Collections.Generic; using UnityEngine; using XLua; public class CSharpCallLua : MonoBehaviour { void Start () { LuaEnv luaenv = new LuaEnv(); luaenv.DoString("require 'CSharpCallLua'"); // 獲取lua中的全局變量 int num = luaenv.Global.Get<int>("num"); string name = luaenv.Global.Get<string>("name"); bool isPause = luaenv.Global.Get<bool>("isPause"); Debug.Log("num:" + num); Debug.Log("name:" + name); Debug.Log("isPause:" + isPause); luaenv.Dispose(); } }
使用函數LuaEnv.Global就能訪問,其中,luaenv.Global.Get<int>("num")中,<int>指的是要轉換成的類型,"num"是在lua中定義的變量名
注意:lua的table中的字段名和C#的class中的字段名要一一對應(名字也要相同),不然取不到值。此種方式爲值拷貝,修改class的字段值不會同步到table,反過來也不會。使用此種方式,不能訪問lua的函數。
using System.Collections; using System.Collections.Generic; using UnityEngine; using XLua; public class CSharpCallLua : MonoBehaviour { void Start () { LuaEnv luaenv = new LuaEnv(); luaenv.DoString("require 'CSharpCallLua'"); // 獲取lua中的全局table Person p = luaenv.Global.Get<Person>("Person"); Debug.Log("name:" + p.name); Debug.Log("age:" + p.age); luaenv.Dispose(); } class Person { public string name; public int age; } }
using System.Collections; using System.Collections.Generic; using UnityEngine; using XLua; public class CSharpCallLua : MonoBehaviour { void Start () { LuaEnv luaenv = new LuaEnv(); luaenv.DoString("require 'CSharpCallLua'"); // 獲取lua中的全局table(映射到interface) Person_1 p1 = luaenv.Global.Get<Person_1>("Person"); Debug.Log("name:" + p1.name); Debug.Log("age:" + p1.age); p1.eat("apple"); luaenv.Dispose(); } [CSharpCallLua] interface Person_1 { string name { get;set;} int age { get; set; } void eat(string str); } }
注意:在lua中定義函數的時候,第一個參數是arg,須要寫上,名字隨意取都行,這裏寫的self。在C#中定義接口的時候,要加上標籤[CSharpCallLua]
using System.Collections; using System.Collections.Generic; using UnityEngine; using XLua; public class CSharpCallLua : MonoBehaviour { void Start () { LuaEnv luaenv = new LuaEnv(); luaenv.DoString("require 'CSharpCallLua'"); // 獲取lua中的全局table(經過Dictionary) Dictionary<string, object> dict = luaenv.Global.Get<Dictionary<string, object>>("Person"); foreach(string key in dict.Keys) { print("key:" + key + " value:" + dict[key]); } luaenv.Dispose(); } }
注意:映射到Dictionary<>的時候,只映射了Lua中鍵值對的形式,普通的值沒有映射過來
using System.Collections; using System.Collections.Generic; using UnityEngine; using XLua; public class CSharpCallLua : MonoBehaviour { void Start () { LuaEnv luaenv = new LuaEnv(); luaenv.DoString("require 'CSharpCallLua'"); // 獲取lua中的全局table(經過List) List<object> list = luaenv.Global.Get<List<object>>("Person"); foreach(object o in list) { print(o); } luaenv.Dispose(); } }
注意:映射到List<>的時候,只映射了Lua中值的形式,鍵值對的形式沒有映射過來
映射到LuaTable類:這種方式不經常使用,也不建議使用
using System.Collections; using System.Collections.Generic; using UnityEngine; using XLua; public class CSharpCallLua : MonoBehaviour { void Start () { LuaEnv luaenv = new LuaEnv(); luaenv.DoString("require 'CSharpCallLua'"); // 訪問lua中的全局函數(映射到delegate) Add add = luaenv.Global.Get<Add>("add"); int res1 = 0; int res2 = 0; int res = add(3, 4, out res1, out res2); print("res:" + res); print("res1:" + res1); print("res2:" + res2); add = null; luaenv.Dispose(); } [CSharpCallLua] delegate int Add(int a, int b, out int res1, out int res2); }
注意:使用delegate須要添加特性[CSharpCallLua],若是lua中函數返回多值,在C#中只能接收一個值,其它值從左往右映射到c#的輸出參數,輸出參數包括返回值,out參數,ref參數。
在C#這樣new一個對象:
var newGameObj = new UnityEngine.GameObject();
對應到Lua是這樣:
local newGameObj = CS.UnityEngine.GameObject()
using System.Collections; using System.Collections.Generic; using UnityEngine; using XLua; public class LuaCallCSharp : MonoBehaviour { void Start () { LuaEnv luaenv = new LuaEnv(); luaenv.DoString("require 'LuaCallCS'"); luaenv.Dispose(); } }
若是須要常常訪問的類,能夠先用局部變量引用後訪問,除了減小敲代碼的時間,還能提升性能
讀成員屬性
testobj.DMF
寫成員屬性
testobj.DMF = 1024