獲取全局變量
只需要調(diào)用LuaEnv對象Global屬性的Get方法即可
LuaTable Global;
- 描述:
代表lua全局環(huán)境的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"));
得到結(jié)果
如果需要獲取Lua中Table的數(shù)據(jù),則需要將Table映射為C#中相對應(yīng)的數(shù)據(jù)結(jié)構(gòu),可選的方式有:class,interface,Dictionary,List和LuaTable
假設(shè)有如下的Table
table =
{
f1 = 1,
f2 = 2
}
如果要使用class做映射,只需要在C#中定義一個(gè)相對應(yīng)的類即可,注意變量名稱要和Table中的名稱相同,xLua會(huì)依次在table中尋找是否有與class中變量名相同的key值,如果有則將其value值復(fù)制到對應(yīng)的變量上,如果在Table的Key值中找不到與定義的class的變量,則變量會(huì)被賦于對應(yīng)類型的初值
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);
得到結(jié)果
同樣地,還可以使用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"));
得到結(jié)果
對于下面這種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]);
得到結(jié)果
如果Table中存在函數(shù),例如
table =
{
f1 = 1,
f2 = 2,
add = function(self, num1, num2)
return num1 + num2
end
}
可以使用interface來映射,對于上述的Table,可以聲明如下ITable接口
使用這種方法讀取Table時(shí)需要生成代碼,所以必須要給接口加上一個(gè)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));
得到結(jié)果
獲取全局函數(shù)
一般來說,全局函數(shù)的映射方式有兩種,一種是使用delegate(要生成代碼,性能好),另一種是使用LuaFunction(不用生成代碼,性能較差)。
假設(shè)有下列的全局函數(shù)
function action0()
print ("action0() called!")
end
因?yàn)槭褂?strong>delegate的方式映射全局函數(shù)需要生成代碼,所以必須要添加一個(gè)Attribute:[CSharpCallLua]
[CSharpCallLua]
private delegate void Action0();
獲取的方式大同小異
Action0 action0 = luaenv.Global.Get<Action0>("action0");
action0();
得到結(jié)果
當(dāng)全局函數(shù)有一個(gè)或多個(gè)返回值的時(shí)候,按照在函數(shù)中的返回順序,從左到右依次對應(yīng)到delegate的返回值,ref/out參數(shù)
假設(shè)有下列的全局函數(shù)
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,假設(shè)有下列的全局函數(shù)
function action0()
print ("action0() called!")
end
function get_action0()
print("get_action0() called!")
return action0
end
先定義Action0,然后將GetAction0的返回值設(shè)為Action0即可
[CSharpCallLua]
private delegate void Action0();
[CSharpCallLua]
private delegate Action0 GetAction0();
GetAction0 getAction0 = luaenv.Global.Get<GetAction0>("get_action0");
(getAction0())();
運(yùn)行得到結(jié)果
使用LuaFunction來映射全局函數(shù)的方法與使用delegate時(shí)基本相同,映射后如果要在C#中使用函數(shù),只需要調(diào)用LuaFunction對象的Call方法即可,Call方法有下面兩種重載形式
object[] Call(params object[] args)
- 描述:
以可變參數(shù)調(diào)用Lua函數(shù),并返回該調(diào)用的返回值。
object[] Call(object[] args, Type[] returnTypes)
- 描述:
調(diào)用Lua函數(shù),并指明返回參數(shù)的類型,系統(tǒng)會(huì)自動(dòng)按指定類型進(jìn)行轉(zhuǎn)換。
需要注意的是,當(dāng)函數(shù)有返回值的時(shí)候,最好指明返回參數(shù)的類型,如果沒有指明類型,在將object對象轉(zhuǎn)換具體類型的時(shí)候可能會(huì)InvalidCastException異常
假設(shè)有下面的全局函數(shù)
function action2(param1, param2)
print("param1:", param1, " param2:", param2)
return 1, {f1 = 2}
end
下面的C#代碼使用LuaFunction調(diào)用該全局函數(shù)
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);
得到結(jié)果
LuaFunction訪問Lua函數(shù)的過程涉及到了多次的裝箱與拆箱操作,雖然不用生成代碼,但是性能的消耗是比較大的
由于訪問Lua全局?jǐn)?shù)據(jù),特別是table以及function,代價(jià)比較大,推薦使用單獨(dú)的模塊負(fù)責(zé)管理其加載,而不是每次用之前去加載
資源釋放
下面這段這段C#代碼首先將Lua函數(shù)test映射到了委托action上,使用完成后調(diào)用了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();
}
}
運(yùn)行上面這段代碼,發(fā)現(xiàn)拋出了如下的異常
查閱FAQ
調(diào)用LuaEnv.Dispose時(shí),報(bào)“try to dispose a LuaEnv with C# callback!”錯(cuò)是什么原因?
這是由于C#還存在指向lua虛擬機(jī)里頭某個(gè)函數(shù)的delegate,為了防止業(yè)務(wù)在虛擬機(jī)釋放后調(diào)用這些無效(因?yàn)槠湟玫膌ua函數(shù)所在虛擬機(jī)都釋放了)delegate導(dǎo)致的異常甚至崩潰,做了這個(gè)檢查
怎么解決?釋放這些delegate即可,所謂釋放,在C#中,就是沒有引用
你是在C#通過LuaTable.Get獲取并保存到對象成員,賦值該成員為null
你是在lua那把lua函數(shù)注冊到一些事件事件回調(diào),反注冊這些回調(diào)
如果你是通過xlua.hotfix(class, method, func)注入到C#,則通過xlua.hotfix(class, method, nil)刪除
要注意以上操作在Dispose之前完成
因此,只需要在LuaEnv釋放之前,將Lua函數(shù)映射的委托變量action設(shè)置為null即可
action = luaenv.Global.Get<Action>("test");
action = null;
luaenv.Dispose();