獲取全局變量
只需要調用LuaEnv對象Global屬性的Get方法即可
LuaTable Global;
- 描述:
代表lua全局環境的LuaTable
T Get<T>(string key);
- 描述:
獲取在key下,類型為T的value,如果不存在或者類型不匹配,返回null;
例如有如下Lua代碼
number = 1 string = "hello world" boolean = true
在C#中嘗試用上述方法輸出Lua中的全局變量number,string和boolean
Debug.Log("number = " + luaenv.Global.Get<int>("number")); Debug.Log("string = " + luaenv.Global.Get<string>("string")); Debug.Log("boolean = " + luaenv.Global.Get<bool>("boolean"));
得到結果
如果需要獲取Lua中Table的數據,則需要將Table映射為C#中相對應的數據結構,可選的方式有:class,interface,Dictionary,List和LuaTable
假設有如下的Table
table =
{
f1 = 1,
f2 = 2
}
如果要使用class做映射,只需要在C#中定義一個相對應的類即可,注意變量名稱要和Table中的名稱相同,xLua會依次在table中尋找是否有與class中變量名相同的key值,如果有則將其value值復制到對應的變量上,如果在Table的Key值中找不到與定義的class的變量,則變量會被賦於對應類型的初值
class Table { public int f1; public int f2; }
獲取變量的方法還是和之前一樣
Table table = luaenv.Global.Get<Table>("table"); Debug.Log("table.f1 = " + table.f1); Debug.Log("table.f2 = " + table.f2);
得到結果
同樣地,還可以使用Dictionary和LuaTable(速度比較慢)來映射Table,代碼如下
Dictionary<string, int> dict = luaenv.Global.Get<Dictionary<string, int>>("table"); Debug.Log("dict[f1] = " + dict["f1"]); Debug.Log("dict[f2] = " + dict["f2"]); LuaTable luaTable = luaenv.Global.Get<LuaTable>("table"); Debug.Log("luaTable.Get<int>(\"f1\") = " + luaTable.Get<int>("f1")); Debug.Log("luaTable.Get<int>(\"f2\") = " + luaTable.Get<int>("f2"));
得到結果
對於下面這種Table,可以使用List來做映射
table =
{
1,
2
}
List<int> list = luaenv.Global.Get<List<int>>("table"); Debug.Log("list[0] = " + list[0]); Debug.Log("list[1] = " + list[1]);
得到結果
如果Table中存在函數,例如
table =
{
f1 = 1, f2 = 2, add = function(self, num1, num2) return num1 + num2 end }
可以使用interface來映射,對於上述的Table,可以聲明如下ITable接口
使用這種方法讀取Table時需要生成代碼,所以必須要給接口加上一個Attribute:[CSharpCallLua]
[CSharpCallLua] interface ITable { int f1 { get; set; } int f2 { get; set; } int add(int num1, int num2); }
ITable iTable = luaenv.Global.Get<ITable>("table"); Debug.Log("table.f1 = " + iTable.f1); Debug.Log("table.f2 = " + iTable.f2); Debug.Log("table.add(1, 2) = " + iTable.add(1, 2));
得到結果
獲取全局函數
一般來說,全局函數的映射方式有兩種,一種是使用delegate(要生成代碼,性能好),另一種是使用LuaFunction(不用生成代碼,性能較差)。
假設有下列的全局函數
function action0() print ("action0() called!") end
因為使用delegate的方式映射全局函數需要生成代碼,所以必須要添加一個Attribute:[CSharpCallLua]
[CSharpCallLua] private delegate void Action0();
獲取的方式大同小異
Action0 action0 = luaenv.Global.Get<Action0>("action0"); action0();
得到結果
當全局函數有一個或多個返回值的時候,按照在函數中的返回順序,從左到右依次對應到delegate的返回值,ref/out參數
假設有下列的全局函數
function action2(param1, param2) print("param1:", param1, " param2:", param2) return 1, {f1 = 2} end
那么下面六種delegate的映射方式是等效的,
[CSharpCallLua] private delegate int Action2(int param1, int param2, out Table table); [CSharpCallLua] private delegate int Action2_(int param1, int param2, ref Table table); [CSharpCallLua] private delegate void Action2__(int param1, int param2, out int val ,out Table table); [CSharpCallLua] private delegate void Action2___(int param1, int param2, ref int val, ref Table table); [CSharpCallLua] private delegate void Action2____(int param1, int param2, ref int val, out Table table); [CSharpCallLua] private delegate void Action2_____(int param1, int param2, out int val, ref Table table);
delegate的返回值也可以是delegate,假設有下列的全局函數
function action0() print ("action0() called!") end function get_action0() print("get_action0() called!") return action0 end
先定義Action0,然后將GetAction0的返回值設為Action0即可
[CSharpCallLua] private delegate void Action0(); [CSharpCallLua] private delegate Action0 GetAction0();
GetAction0 getAction0 = luaenv.Global.Get<GetAction0>("get_action0"); (getAction0())();
運行得到結果
使用LuaFunction來映射全局函數的方法與使用delegate時基本相同,映射后如果要在C#中使用函數,只需要調用LuaFunction對象的Call方法即可,Call方法有下面兩種重載形式
object[] Call(params object[] args)
- 描述:
以可變參數調用Lua函數,並返回該調用的返回值。
object[] Call(object[] args, Type[] returnTypes)
- 描述:
調用Lua函數,並指明返回參數的類型,系統會自動按指定類型進行轉換。
需要注意的是,當函數有返回值的時候,最好指明返回參數的類型,如果沒有指明類型,在將object對象轉換具體類型的時候可能會InvalidCastException異常
假設有下面的全局函數
function action2(param1, param2) print("param1:", param1, " param2:", param2) return 1, {f1 = 2} end
下面的C#代碼使用LuaFunction調用該全局函數
LuaFunction luaFunction = luaenv.Global.Get<LuaFunction>("action2"); object[] vals = luaFunction.Call(new object[]{1, 2}, new Type[] { typeof(int), typeof(Table) }); Debug.Log("vals[0] = " + (int)vals[0]); Debug.Log("vals[1] = " + ((Table)vals[1]).f1);
得到結果
LuaFunction訪問Lua函數的過程涉及到了多次的裝箱與拆箱操作,雖然不用生成代碼,但是性能的消耗是比較大的
由於訪問Lua全局數據,特別是table以及function,代價比較大,推薦使用單獨的模塊負責管理其加載,而不是每次用之前去加載
資源釋放
下面這段這段C#代碼首先將Lua函數test映射到了委托action上,使用完成后調用了LuaEnv的Dispose方法回收了LuaEnv對象
public class SpecialCase : MonoBehaviour { [CSharpCallLua] public delegate void Action(); private LuaEnv luaenv; private Action action; void Start () { luaenv = new LuaEnv(); luaenv.DoString ( @" function test() print('test') end " ); action = luaenv.Global.Get<Action>("test"); luaenv.Dispose(); } }
運行上面這段代碼,發現拋出了如下的異常
查閱FAQ
調用LuaEnv.Dispose時,報“try to dispose a LuaEnv with C# callback!”錯是什么原因?
這是由於C#還存在指向lua虛擬機里頭某個函數的delegate,為了防止業務在虛擬機釋放后調用這些無效(因為其引用的lua函數所在虛擬機都釋放了)delegate導致的異常甚至崩潰,做了這個檢查
怎么解決?釋放這些delegate即可,所謂釋放,在C#中,就是沒有引用
你是在C#通過LuaTable.Get獲取並保存到對象成員,賦值該成員為null
你是在lua那把lua函數注冊到一些事件事件回調,反注冊這些回調
如果你是通過xlua.hotfix(class, method, func)注入到C#,則通過xlua.hotfix(class, method, nil)刪除
要注意以上操作在Dispose之前完成
因此,只需要在LuaEnv釋放之前,將Lua函數映射的委托變量action設置為null即可
action = luaenv.Global.Get<Action>("test"); action = null; luaenv.Dispose();
作者:BlauHimmel
鏈接:https://www.jianshu.com/p/8dde7be41d96
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
