ToLua框架下C#與Lua代碼的互調(diào)

Lua是目前國(guó)內(nèi)使用最多的熱更語(yǔ)言兄渺,基于Lua的熱更框架也非常多缝龄,最近學(xué)習(xí)了一下ToLua的熱更框架,主要使用的問(wèn)題在于C#和Lua之間的互調(diào)挂谍,因此做一下學(xué)習(xí)記錄以備后查叔壤。
所謂“互調(diào)”,當(dāng)然要包括兩個(gè)方面口叙,一是通過(guò)C#調(diào)用Lua代碼炼绘,二是通過(guò)Lua代碼調(diào)用C#腳本,第二點(diǎn)還包括注冊(cè)在C#腳本里的Unity物體妄田。

1. ToLua的簡(jiǎn)單實(shí)現(xiàn)原理

ToLua框架主要是通過(guò)靜態(tài)綁定來(lái)實(shí)現(xiàn)C#與Lua之間的交互的俺亮,基本原理是通過(guò)建立一個(gè)Lua虛擬機(jī)來(lái)映射C#腳本,然后再通過(guò)這個(gè)虛擬機(jī)來(lái)運(yùn)行Lua腳本疟呐,Lua腳本在運(yùn)行時(shí)可以通過(guò)虛擬機(jī)反過(guò)來(lái)調(diào)用C#腳本里注冊(cè)過(guò)的物體脚曾,這種方式的優(yōu)勢(shì)在于比起使用反射的uLua來(lái)說(shuō)效率更高,但要注意启具,在這個(gè)框架中映射的過(guò)程里進(jìn)行了大量的拆裝箱本讥,即使不算Lua本身的效率低下,這個(gè)巨大的拆裝箱的量也會(huì)對(duì)性能造成非常大的影響富纸,對(duì)于一些性能敏感的游戲最好還是選擇性能更好的開發(fā)方案囤踩。
ToLua框架下可以將實(shí)現(xiàn)分成三大部分:普通的Unity+C#部分、ToLua虛擬機(jī)部分和Lua腳本部分晓褪,結(jié)構(gòu)見下圖:

ToLua結(jié)構(gòu)

目前國(guó)內(nèi)需要熱更的手游一般都將主要的邏輯框架和組件功能用C#實(shí)現(xiàn),而具體功能和調(diào)用放在Lua中综慎,因?yàn)镃#是不能被打包進(jìn)AssetBundle中的涣仿,所以無(wú)法通過(guò)AssetBundle對(duì)代碼進(jìn)行改動(dòng),但是Lua是即時(shí)編譯型語(yǔ)言,并且可以被打包進(jìn)入AssetBundle中好港,在需要修改簡(jiǎn)單功能時(shí)愉镰,將Lua代碼通過(guò)AssetBundle進(jìn)行更新即可。

2. ToLua的下載的安裝

首先是下載地址:
ToLua
這是作者的github地址钧汹,進(jìn)入以后點(diǎn)擊下載Zip丈探,完成后解壓到自己需要的目錄,再用Unity打開即可拔莱。

點(diǎn)擊下載zip即可

第一次打開工程時(shí)會(huì)提示是否需要自動(dòng)生成注冊(cè)文件碗降,新手可以選擇直接生成,若選擇了取消塘秦,也可以在編輯器菜單中手動(dòng)注冊(cè)讼渊。——這是一個(gè)非常重要的操作尊剔,后文也會(huì)提到爪幻。


下面開始關(guān)于使用的正文。

3. ToLua的基本使用

前面有提到過(guò)ToLua的基本實(shí)現(xiàn)方式须误,這里可以再細(xì)化一點(diǎn):創(chuàng)建虛擬機(jī)——綁定數(shù)據(jù)——調(diào)用Lua代碼挨稿,這套步驟在框架自帶的Example里也非常清晰。
首先脫離Example實(shí)現(xiàn)一下這三個(gè)步驟京痢。

  • ToLua虛擬機(jī)的創(chuàng)建非常簡(jiǎn)單叶组,只需要new一個(gè)LuaState即可,我們建立一個(gè)C#腳本作為入口历造,引用LuaInterface命名空間甩十,輸入以下代碼,將文件掛載到場(chǎng)景中的一個(gè)空物體上即可吭产。
using LuaInterface;
using UnityEngine;

public class LuaScene : MonoBehaviour
{

    string luaString = @"
                        print('這是一個(gè)使用DoString的Lua程序')
                                        ";
    string luaFile = "LuaStudy";
    LuaState state;


    void Start()
    {

        state = new LuaState();//建立Lua虛擬機(jī)
        state.Start();//啟動(dòng)虛擬機(jī)

        //使用string調(diào)用Lua
        state.DoString(luaString);
        
        //使用文件調(diào)用Lua
        //手動(dòng)添加一個(gè)lua文件搜索地址
        string sceneFile = Application.dataPath + "/LuaStudy";
        state.AddSearchPath(sceneFile);
        state.DoFile(luaFile);
        state.Require(luaFile);

        state.Dispose();//使用完畢回收虛擬機(jī)
        Debug.LogFormat("當(dāng)前虛擬機(jī)狀態(tài):{0}", null == state);//驗(yàn)證虛擬機(jī)狀態(tài)
    }

}

  • 這里使用的Lua腳本也非常簡(jiǎn)單
print('這是一個(gè)使用DoFile的Lua程序')
Lua掛載
  • ToLua直接調(diào)用Lua代碼的方式有兩種侣监,一種是DoString,另一種是DoFile臣淤;此外還有一個(gè)Require方法橄霉,這個(gè)方法和前兩個(gè)方法不同的是,ToLua會(huì)將調(diào)用的Lua文件載入Lua棧中邑蒋,而前兩者只是運(yùn)行一次姓蜂,運(yùn)行之后保存在緩存中,雖然也可以后續(xù)調(diào)用医吊,但是會(huì)钱慢。
    在上述代碼中要注意,使用DoFileRequire方法時(shí)卿堂,要手動(dòng)給目標(biāo)文件添加一個(gè)文件搜索位置束莫。
    運(yùn)行結(jié)果如下:

    Lua運(yùn)行結(jié)果

  • 最后懒棉,使用完畢記得清理虛擬機(jī),我使用null==state來(lái)進(jìn)行判斷览绿,最后輸出“true”,說(shuō)明調(diào)用LuaState.Dispose()后策严,虛擬機(jī)已經(jīng)被清理。

4. C#中調(diào)用Lua變量/函數(shù)

我們上面實(shí)現(xiàn)了C#調(diào)用Lua文件和string饿敲,其實(shí)對(duì)于ToLua而且妻导,直接調(diào)用string和文件并沒(méi)有本質(zhì)區(qū)別,最后都會(huì)轉(zhuǎn)換成byte[]進(jìn)行載入怀各。
接下來(lái)實(shí)現(xiàn)一下ToLua調(diào)用指定Lua變量和函數(shù),這里通過(guò)文件導(dǎo)入Lua代碼倔韭。

  • 首先是我們的Lua代碼,這一段Lua代碼一共有一個(gè)普通變量渠啤、一個(gè)帶有函數(shù)的表狐肢,一個(gè)無(wú)參函數(shù),一個(gè)有參函數(shù)沥曹,功能非常簡(jiǎn)單份名,并且在這一段代碼中沒(méi)有調(diào)用。
num = 0
mytable={1,2,3,4}
mytable.tableFunc=function()
    print('調(diào)用TableFunc');
end
  
function Count()
    num=num+1
    print('計(jì)數(shù)器+1,當(dāng)前計(jì)數(shù)器為'..num)
    return num;
end

function InputValue( param)
    print('[lua中調(diào)用:]InputValue方法傳入?yún)?shù):'..tostring( param))
end
  • 然后是C#代碼妓美,還是一樣的套路僵腺,先創(chuàng)建虛擬機(jī),讀入Lua文件壶栋。下面依次說(shuō)明普通變量辰如、無(wú)參函數(shù)、有參函數(shù)和table的調(diào)用贵试。
    注意:如果帶有local標(biāo)識(shí)琉兜,那么C#中無(wú)法直接獲取
  1. 普通變量
    普通變量的調(diào)用非常簡(jiǎn)單,在載入文件后毙玻,通過(guò)LuaState[string]的形式就可以直接獲取到豌蟋,也可以通過(guò)這個(gè)表達(dá)式來(lái)直接賦值。
  2. 無(wú)參函數(shù)
    函數(shù)的調(diào)用有兩種方式桑滩,一是先緩存為LuaFunction類型后調(diào)用梧疲,二是直接能過(guò)Call方法調(diào)用。
  3. 有參函數(shù)
    有參函數(shù)和無(wú)參函數(shù)調(diào)用的區(qū)別在于參數(shù)的傳入运准,在ToLua中重載了非常多的傳參函數(shù)幌氮,與無(wú)參函數(shù)的調(diào)用方法相同,有參函數(shù)也有兩種調(diào)用方式胁澳,這里具體說(shuō)明一下傳入?yún)?shù)的不同方式该互。
    • 傳入?yún)⒑驼{(diào)用分離。
      這種方式一般需要先將函數(shù)緩存為LuaFunction听哭,然后使用BeginPcall方法標(biāo)記函數(shù)慢洋,再使用Push或者PushArgs方法將參數(shù)傳入函數(shù)塘雳,最后調(diào)用PCall陆盘,還可以調(diào)用EndPcall標(biāo)記結(jié)束普筹。
        //對(duì)方法傳入?yún)?shù)
        LuaFunction valueFunc = state.GetFunction("InputValue");
        valueFunc.BeginPCall();
        valueFunc.Push("--push方法從C#中傳入?yún)?shù)--");
        valueFunc.PCall();
    • 調(diào)用時(shí)直接傳入?yún)?shù)。
      這是最符合一般操作邏輯的方式隘马,但是查看實(shí)現(xiàn)代碼會(huì)發(fā)現(xiàn)太防,事實(shí)上只是LuaFunction中封裝的一套實(shí)現(xiàn),其本質(zhì)和上一種是一樣的酸员。

4.table
table是lua中的一個(gè)百寶箱蜒车,一切東西都可以往里裝,table里可以有普通的變量幔嗦,還可以有table酿愧,也可以有方法。
在ToLua里對(duì)table的數(shù)據(jù)結(jié)構(gòu)進(jìn)行了解析邀泉,實(shí)現(xiàn)了非常多的方法嬉挡,這里完全可以將table看一個(gè)LuaState來(lái)進(jìn)行操作,兩者沒(méi)有什么區(qū)別汇恤。

以下是完整的C#代碼,運(yùn)行結(jié)果后附庞钢。

using LuaInterface;
using UnityEngine;

public class LuaAccess : MonoBehaviour
{
    string luaFile = "LuaAccess";
    LuaState state;


    void Start()
    {
        state = new LuaState();
        state.Start();

        //使用文件調(diào)用Lua
        //手動(dòng)添加一個(gè)lua文件搜索地址
        string sceneFile = Application.dataPath + "/LuaStudy";
        state.AddSearchPath(sceneFile);

        state.Require(luaFile);//載入文件

        //獲取Lua變量
        Debug.Log("獲取文件中變量:" + state["num"]);
        state["num"] = 10;
        Debug.Log("設(shè)置文件中變量為:" + state["num"]);

        //調(diào)用Lua方法
        LuaFunction luaFunc = state.GetFunction("Count");
        luaFunc.Call();
        Debug.Log("C#調(diào)用LuaFunc,函數(shù)返回值:" + state["num"]);

        Debug.Log("C#直接調(diào)用Count方法。");
        state.Call("Count", false);


        //對(duì)方法傳入?yún)?shù)
        LuaFunction valueFunc = state.GetFunction("InputValue");
        valueFunc.BeginPCall();
        valueFunc.Push("--push方法從C#中傳入?yún)?shù)--");
        valueFunc.PCall();
        valueFunc.EndPCall();

        valueFunc.Call("--直接Call方法從C#傳入?yún)?shù)--");


        //獲取LuaTable
        LuaTable table = state.GetTable("mytable");
        table.Call("tableFunc");
        LuaFunction tableFunc = table.GetLuaFunction("tableFunc");
        Debug.Log("C#調(diào)用table中的func");
        tableFunc.Call();

        Debug.Log("獲取table中的num值:"+table["num"]);

        //通過(guò)下標(biāo)直接獲取
        for (int i = 0; i < table.Length; i++)
        {
            Debug.Log("獲取table的值:" + table[i]);
        }


        //轉(zhuǎn)換成LuaDictTable
        LuaDictTable dicTable = table.ToDictTable();
        foreach (var item in dicTable)
        {
            Debug.LogFormat("遍歷table:{0}--{1}", item.Key, item.Value);
        }


        state.Dispose();
    }
}
Lua訪問(wèn)變量

5. Lua中調(diào)用C#方法/變量

之前在 @羅夏L的文章里看過(guò)一篇他關(guān)于lua調(diào)用C#的筆記因谎,但總覺(jué)得少了點(diǎn)什么基括,所以在我自己記筆記的時(shí)候特別注意了一下具體的實(shí)現(xiàn)。
在@羅夏L的文章中财岔,將一個(gè)C#對(duì)象作為參數(shù)傳入列表中风皿,然后直接在Lua代碼里運(yùn)行對(duì)應(yīng)的方法名,其中少了幾個(gè)關(guān)鍵的步驟匠璧,如果只是進(jìn)行了這幾步桐款,是實(shí)現(xiàn)不了在Lua里引用的。

  • 首先還是從實(shí)現(xiàn)原理說(shuō)起患朱,在文章開始的第一節(jié)我提過(guò)ToLua的基本實(shí)現(xiàn)思路鲁僚,并且將這套系統(tǒng)分成了三部分,在這三部分之中裁厅,ToLua作為一個(gè)橋梁實(shí)現(xiàn)了溝通Lua腳本和C#的功能冰沙,我們知道Lua的實(shí)質(zhì)是通過(guò)字節(jié)碼對(duì)C進(jìn)行了一套封裝,具有即時(shí)編譯的特點(diǎn)执虹,從C#或者其他語(yǔ)言中來(lái)調(diào)用Lua都不算太困難拓挥,只需要提前約定特定方法名然后載入腳本即可,但C#是需要提前編譯的袋励,怎么通過(guò)一段解釋器來(lái)調(diào)用C#中的對(duì)象就是主要的難點(diǎn)了侥啤,ToLua實(shí)現(xiàn)的就是這兩方面的功能当叭。
    從這方面來(lái)分析,我覺(jué)得大多數(shù)人會(huì)想到的最直接的實(shí)現(xiàn)思路大概都是通過(guò)反射來(lái)實(shí)現(xiàn)盖灸,uLua也是通過(guò)反射來(lái)實(shí)現(xiàn)的蚁鳖,但是反射的效率非常低,雖然確實(shí)可以實(shí)現(xiàn)赁炎,但問(wèn)題還是非常明顯醉箕。

ToLua是通過(guò)方法名綁定的方式來(lái)實(shí)現(xiàn)這個(gè)映射的,首先構(gòu)造一個(gè)Lua虛擬機(jī)徙垫,在虛擬機(jī)啟動(dòng)后對(duì)所需的方法進(jìn)行綁定讥裤,在虛擬機(jī)運(yùn)行時(shí)可以在Lua中調(diào)用特定方法,虛擬機(jī)變相地實(shí)現(xiàn)了一個(gè)解釋器的功能姻报,在Lua調(diào)用特定方法和對(duì)象時(shí)己英,虛擬機(jī)會(huì)在已綁定的方法中找到對(duì)應(yīng)的C#方法和對(duì)象進(jìn)行操作,并且ToLua已經(jīng)自動(dòng)實(shí)現(xiàn)了一些綁定的方法 吴旋。

  • 基本原理大概了解以后损肛,我們就可以來(lái)看看它的具體實(shí)現(xiàn)了。

      1. 第一步還是建立虛擬機(jī)并且啟動(dòng)邮府,為了實(shí)現(xiàn)Lua對(duì)C#的調(diào)用荧关,首先我們要調(diào)用一下綁定方法,于是我們的代碼變成了下面這樣褂傀∪唐。可以看到,這里和之前的唯一區(qū)別是增加了LuaBinder.Bind(state)方法仙辟,這一個(gè)方法內(nèi)部其實(shí)是對(duì)許多定義好的方法的綁定同波,也就是上面說(shuō)的綁定方法。
using LuaInterface;
using UnityEngine;

public class CSharpAccess : MonoBehaviour
{
 private string luaFile = "LuaCall";
    LuaState state;

    void Start()
    {
        state = new LuaState();
        state.Start();

        string sceneFile = Application.dataPath + "/LuaStudy";
        state.AddSearchPath(sceneFile);

        //  注冊(cè)方法調(diào)用
        LuaBinder.Bind(state);

        state.Require(luaFile);//載入文件
    }
}
      1. 然后我們加入一個(gè)變量和一個(gè)方法叠国,我們要實(shí)現(xiàn)的是完成在Lua中對(duì)這個(gè)方法和變量的調(diào)用未檩。
    public string AccessVar = "++這是初始值++";
    public void PrintArg(string arg)
    {
        Debug.Log("C#輸出變量值:" + arg);
    }
      1. 在有了目標(biāo)方法之后,我們要將這個(gè)變量和方法綁定進(jìn)入虛擬機(jī)中粟焊。
        查看LuaState的實(shí)現(xiàn)代碼冤狡,可以發(fā)現(xiàn)綁定主要有RegFunctionRegVarRegConstant三個(gè)方法项棠,分別用于綁定函數(shù)/委托悲雳、變量和常量。在這里ToLua是通過(guò)一個(gè)委托來(lái)實(shí)現(xiàn)方法的映射香追,這個(gè)委托需要傳入一個(gè)luaState變量合瓢,類型是IntPtr,這個(gè)變量的實(shí)質(zhì)是一個(gè)句柄透典,在實(shí)際操作中晴楔,會(huì)將虛擬機(jī)作為變量傳入顿苇。
        public delegate int LuaCSFunction(IntPtr luaState);        

        public void RegFunction(string name, LuaCSFunction func);

        public void RegVar(string name, LuaCSFunction get, LuaCSFunction set);

        public void RegConstant(string name, double d);

        public void RegConstant(string name, bool flag);
      • 總結(jié)一下幾個(gè)方法的特點(diǎn):
  1. 這幾個(gè)方法都需要傳入一個(gè)string name,這個(gè)name就是之后在Lua中調(diào)用的變量或方法名税弃。
  2. RegConstant方法比較簡(jiǎn)單纪岁,傳入一個(gè)name再傳入一個(gè)常量即可;
  3. RegFunctionRegVar都是通過(guò)LuaCSFunction類型的委托實(shí)現(xiàn)钙皮;
  4. RegFunction需要一個(gè)LuaCSFunction委托蜂科,這個(gè)委托需要對(duì)原方法重新進(jìn)行一次實(shí)現(xiàn)顽决;
  5. RegVar除了name之外短条,還需要兩個(gè)LuaCSFunction委托,可以理解為一個(gè)變量的get/set方法才菠,如果只有g(shù)et或set茸时,另一個(gè)留null即可。
      1. 接下來(lái)我們對(duì)AccessVarPrintArg方法進(jìn)行一下LuaCSFunction形式的實(shí)現(xiàn)赋访。
    private int PrintCall(System.IntPtr L)
    {
        try
        {
            ToLua.CheckArgsCount(L, 2); //對(duì)參數(shù)進(jìn)行校驗(yàn)
            CSharpAccess obj = (CSharpAccess)ToLua.CheckObject(L, 1, typeof(CSharpAccess));//獲取目標(biāo)對(duì)象并轉(zhuǎn)換格式
            string arg0 = ToLua.CheckString(L, 2);//獲取特定值
            obj.PrintArg(arg0);//調(diào)用對(duì)象方法
            return 1;
        }
        catch (System.Exception e)
        {
            return LuaDLL.toluaL_exception(L, e);
        }
    }

    private int GetAccesVar(System.IntPtr L)
    {
        object o = null;

        try
        {
            o = ToLua.ToObject(L, 1); //獲得變量實(shí)例
            CSharpAccess obj = (CSharpAccess)o; //轉(zhuǎn)換目標(biāo)格式
            string ret = obj.AccessVar; //獲取目標(biāo)值
            ToLua.Push(L, ret);//將目標(biāo)對(duì)象傳入虛擬機(jī)
            return 1;
        }
        catch (System.Exception e)
        {
            return LuaDLL.toluaL_exception(L, e, o, "attempt to index AccessVar on a nil value");
        }
    }

    private int SetAccesVar(System.IntPtr L)
    {
        object o = null;

        try
        {
            o = ToLua.ToObject(L, 1);//獲得變量實(shí)例
            CSharpAccess obj = (CSharpAccess)o;//轉(zhuǎn)換目標(biāo)格式
            obj.AccessVar = ToLua.ToString(L, 2);//將要修改的值進(jìn)行設(shè)定
            return 1;
        }
        catch (System.Exception e)
        {
            return LuaDLL.toluaL_exception(L, e, o, "attempt to index AccessVar on a nil value");
        }
    }

可以看到這三個(gè)方法的格式都是一致的可都,通用的步驟如下:

  1. 使用ToLua中的方法對(duì)L句柄進(jìn)行校驗(yàn),出現(xiàn)異常則拋出蚓耽,本例中使用ToLua.CheckArgsCount方法渠牲;
  2. 獲得目標(biāo)類的實(shí)例,并轉(zhuǎn)換格式步悠,具體轉(zhuǎn)換方法較多签杈,可以根據(jù)需要在ToLua類中選擇,本例中使用了ToLua.CheckObjectToLua.ToObject等方法鼎兽;
  3. 調(diào)用對(duì)應(yīng)方法答姥,不同的方法調(diào)用略有區(qū)別。

值得注意的是谚咬,這里進(jìn)行了非常多的拆裝箱操作鹦付,每次lua調(diào)用 C#數(shù)據(jù)都會(huì)進(jìn)行一次拆箱轉(zhuǎn)換。

      1. 下一步將幾個(gè)方法注冊(cè)進(jìn)lua虛擬機(jī)择卦。
        注意這里有兩對(duì)方法敲长,分別是BeginModule\EndModuleBeginClass\EndClass,BeginModule\EndModule用于綁定命名空間,可以逐層嵌套秉继;而BeginClass\EndClass用于開啟具體的類型空間祈噪,具體的方法和變量綁定必須在這成對(duì)的方法之中,否則會(huì)導(dǎo)致ToLua崩潰(百試百靈秕噪,別問(wèn)我怎么知道的)钳降。
    private void Bind(LuaState L)
    {
        L.BeginModule(null);

        L.BeginClass(typeof(CSharpAccess), typeof(UnityEngine.MonoBehaviour));

        state.RegFunction("Debug", PrintCall);
        state.RegVar("AccessVar", GetAccesVar, SetAccesVar);

        L.EndClass();

        L.EndModule();
    }
      1. 最后是我們的Lua代碼,非常簡(jiǎn)單腌巾,注意DebugAccessVar調(diào)用的區(qū)別遂填。
print('--進(jìn)入Lua調(diào)用--')
local go = UnityEngine.GameObject.Find("LuaScene")
local access=go:GetComponent("CSharpAccess")
access:Debug("Lua調(diào)用C#方法")
access.AccessVar="--這是修改值--"
print('--Lua調(diào)用結(jié)束--')
      1. 完整C#代碼
using LuaInterface;
using UnityEngine;

public class CSharpAccess : MonoBehaviour
{

    private string luaFile = "LuaCall";
    LuaState state;

    void Start()
    {
        state = new LuaState();
        state.Start();


        string sceneFile = Application.dataPath + "/LuaStudy";
        state.AddSearchPath(sceneFile);

        //  注冊(cè)方法調(diào)用
        LuaBinder.Bind(state);
        Bind(state);


        Debug.Log("AccessVar初始值:" + AccessVar);

        state.Require(luaFile);//載入文件
        Debug.Log("C#查看:" + AccessVar);

        state.Dispose();
    }

    private void Bind(LuaState L)
    {
        L.BeginModule(null);
        L.BeginClass(typeof(CSharpAccess), typeof(UnityEngine.MonoBehaviour));
        state.RegFunction("Debug", PrintCall);
        state.RegVar("AccessVar", GetAccesVar, SetAccesVar);
        L.EndClass();
        L.EndModule();
    }

    private int PrintCall(System.IntPtr L)
    {
        try
        {
            ToLua.CheckArgsCount(L, 2); //對(duì)參數(shù)進(jìn)行校驗(yàn)
            CSharpAccess obj = (CSharpAccess)ToLua.CheckObject(L, 1, typeof(CSharpAccess));//獲取目標(biāo)對(duì)象并轉(zhuǎn)換格式
            string arg0 = ToLua.CheckString(L, 2);//獲取特定值
            obj.PrintArg(arg0);//調(diào)用對(duì)象方法
            return 1;
        }
        catch (System.Exception e)
        {
            return LuaDLL.toluaL_exception(L, e);
        }
    }

    public void PrintArg(string arg)
    {
        Debug.Log("C#輸出變量值:" + arg);
    }

    [System.NonSerialized]
    public string AccessVar = "++這是初始值++";
    private int GetAccesVar(System.IntPtr L)
    {
        object o = null;

        try
        {
            o = ToLua.ToObject(L, 1); //獲得變量實(shí)例
            CSharpAccess obj = (CSharpAccess)o; //轉(zhuǎn)換目標(biāo)格式
            string ret = obj.AccessVar; //獲取目標(biāo)值
            ToLua.Push(L, ret);//將目標(biāo)對(duì)象傳入虛擬機(jī)
            return 1;
        }
        catch (System.Exception e)
        {
            return LuaDLL.toluaL_exception(L, e, o, "attempt to index AccessVar on a nil value");
        }
    }
    private int SetAccesVar(System.IntPtr L)
    {
        object o = null;

        try
        {
            o = ToLua.ToObject(L, 1);//獲得變量實(shí)例
            CSharpAccess obj = (CSharpAccess)o;//轉(zhuǎn)換目標(biāo)格式
            obj.AccessVar = ToLua.ToString(L, 2);//將要修改的值進(jìn)行設(shè)定
            return 1;
        }
        catch (System.Exception e)
        {
            return LuaDLL.toluaL_exception(L, e, o, "attempt to index AccessVar on a nil value");
        }
    }
}
      1. 運(yùn)行結(jié)果


        Lua調(diào)用C#
  • 那么最后铲觉,我們回到本節(jié)開始, @羅夏L的文章里是哪里出現(xiàn)了問(wèn)題吓坚?
    我在lua中加入了一行access:PrintArg("PrintArg")調(diào)用方法撵幽,發(fā)現(xiàn)Unity報(bào)了這樣的錯(cuò)誤:

    直接調(diào)用方法名報(bào)錯(cuò).png

說(shuō)明單純這樣是做不到直接調(diào)用方法的,仔細(xì)看文章礁击,我發(fā)現(xiàn)他有提到這樣的內(nèi)容:

首先將自己寫的類 放到 CustomSettings 里 就是CallLuafunction
BindType[] customTypeList
放到這個(gè)數(shù)組里 注冊(cè)進(jìn)去供lua使用

這里是不是他說(shuō)得不夠詳細(xì)盐杂?我找到這個(gè)類,發(fā)現(xiàn)這個(gè)類里記錄了非常多的Unity自帶類哆窿,這讓我想起了第一次啟動(dòng)Lua時(shí)的提示链烈,心里生出了一個(gè)疑問(wèn):這些數(shù)據(jù)是不是用于自動(dòng)注冊(cè)生成類的呢?

 //在這里添加你要導(dǎo)出注冊(cè)到lua的類型列表
    public static BindType[] customTypeList =
    {                
        _GT(typeof(LuaInjectionStation)),
        _GT(typeof(InjectType)),
        _GT(typeof(Debugger)).SetNameSpace(null),          
...以下部分省略

沿著調(diào)用鏈挚躯,我找到了這個(gè)變量的引用强衡,果然,最這個(gè)數(shù)據(jù)是用于類型注冊(cè)的码荔。
我將這個(gè)類放到了數(shù)組的最后漩勤,點(diǎn)擊Clear wrap files,完成后立即彈出了數(shù)據(jù)自動(dòng)生成的對(duì)話框缩搅,點(diǎn)擊確認(rèn)越败,

重新生成注冊(cè)

自動(dòng)生成

接下來(lái)我重新運(yùn)行了lua腳本:

print('--進(jìn)入Lua調(diào)用--')
local go = UnityEngine.GameObject.Find("LuaScene")
local access=go:GetComponent("CSharpAccess")
access:Debug("Lua調(diào)用C#方法")
access.AccessVar="--這是修改值--"
print('--Lua調(diào)用結(jié)束--')
access:PrintArg("PrintArg")
成功運(yùn)行

成功運(yùn)行,說(shuō)明ToLua實(shí)現(xiàn)了一整套綁定方案硼瓣,只需要將所需要的內(nèi)容配置完成即可究飞。

6.總結(jié)

原本只是想簡(jiǎn)單寫一寫調(diào)用方式,最后又寫成了一篇長(zhǎng)文巨双,但是從有計(jì)劃開始到一整篇結(jié)束卻花掉了近一整天的時(shí)間噪猾。
雖然如此,收獲還是非常大的筑累,對(duì)這套工具的使用熟練度又上了一個(gè)層次袱蜡,以后也要加強(qiáng)總結(jié)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末慢宗,一起剝皮案震驚了整個(gè)濱河市坪蚁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌镜沽,老刑警劉巖敏晤,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異缅茉,居然都是意外死亡嘴脾,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)译打,“玉大人耗拓,你說(shuō)我怎么就攤上這事∽嗨荆” “怎么了乔询?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)韵洋。 經(jīng)常有香客問(wèn)我竿刁,道長(zhǎng),這世上最難降的妖魔是什么搪缨? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任食拜,我火速辦了婚禮,結(jié)果婚禮上勉吻,老公的妹妹穿的比我還像新娘监婶。我一直安慰自己,他們只是感情好齿桃,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著煮盼,像睡著了一般短纵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上僵控,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天香到,我揣著相機(jī)與錄音,去河邊找鬼报破。 笑死悠就,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的充易。 我是一名探鬼主播梗脾,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼盹靴!你這毒婦竟也來(lái)了炸茧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤稿静,失蹤者是張志新(化名)和其女友劉穎梭冠,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體改备,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡控漠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悬钳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盐捷。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柬脸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出毙驯,到底是詐尸還是另有隱情倒堕,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布爆价,位于F島的核電站垦巴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏铭段。R本人自食惡果不足惜骤宣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望序愚。 院中可真熱鬧憔披,春花似錦、人聲如沸爸吮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)形娇。三九已至锰霜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間桐早,已是汗流浹背癣缅。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哄酝,地道東北人友存。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像陶衅,于是被迫代替她去往敵國(guó)和親屡立。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容