xLua學(xué)習(xí)筆記(二) C#調(diào)用Lua代碼

獲取全局變量

只需要調(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),可選的方式有:classinterface,DictionaryListLuaTable

假設(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é)果

同樣地,還可以使用DictionaryLuaTable(速度比較慢)來映射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();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容