[Unity]SLua的使用與源碼淺析

SLua學(xué)習(xí)前提:
1.熟悉Lua基本語法
2.熟悉Lua與C交互(C#交互類似)
3.熟悉Unity基本組件以及在

簡單使用

1.Slua下載配置
2.導(dǎo)出Lua接口

SLua菜單

All->導(dǎo)出/清空全部接口
Unity->導(dǎo)出UnityEngine或者UnityEngine.UI下類的接口寻馏。
Custom->自定義導(dǎo)出類
3rdDll->導(dǎo)出動(dòng)態(tài)鏈接庫

  • test one 創(chuàng)建一個(gè)cube
    在C#中如何創(chuàng)建呢?
    void Start () {
    GameObject.CreatePrimitive(PrimitiveType.Cube);
    }

Lua中創(chuàng)建方式

import "UnityEngine" --導(dǎo)包
UnityEngine.GameObject.CreatePrimitive(UnityEngine.PrimitiveType.Cube)

同樣很簡單霞赫。
那么C#該如何調(diào)用Lua文件呢?
首先回憶一下C中調(diào)用方式

    //初始化全局L  
    LuaState L = luaL_newstate();  
    //打開庫  
    luaL_openlibs(L);  
    if (luaL_loadfile(L,fileName))  
    {  
        printf("error\n");  
    }  

在C#中也類似調(diào)用

    LuaSvr lua_svr = new LuaSvr ();
    lua.init(null, () =>
        {
            lua.start("test");
        });

LuaSvr將調(diào)用方式稍微做了封裝,原理類似。

  • test two 修改text文字
    C#寫法:
    void Start () {
        Text textComponent= GameObject.Find("Canvas/Text").GetComponent<Text>();
        textComponent.text = "slua";
    }

Lua寫法:

import "UnityEngine"
local textComponent= GameObject.Find("Canvas/Text"):GetComponent("Text")
textComponent.text="123"

原理淺析

  • step one:跟蹤C(jī)#接口導(dǎo)出
       //LuaCodeGen.cs
       [MenuItem("SLua/Unity/Make UnityEngine")]
        //導(dǎo)出需要的lua文件接口(UnityEngine下的)
        static public void Generate()
        {
            if (IsCompiling) {
                return;
            }
            //反射UnityEngine類
            Assembly assembly = Assembly.Load("UnityEngine");
            //獲取此程序集中定義的公共類型丰捷,這些公共類型在程序集外可見。
            Type[] types = assembly.GetExportedTypes();
            
            List<string> uselist;
            List<string> noUseList;
            
            CustomExport.OnGetNoUseList(out noUseList);
            CustomExport.OnGetUseList(out uselist);

            // Get use and nouse list from custom export.
            object[] aCustomExport = new object[1];
            InvokeEditorMethod<ICustomExportPost>("OnGetUseList", ref aCustomExport);
            if (null != aCustomExport[0])
            {
                if (null != uselist)
                {
                    uselist.AddRange((List<string>)aCustomExport[0]);
                }
                else
                {
                    uselist = (List<string>)aCustomExport[0];
                }
            }

            aCustomExport[0] = null;
            InvokeEditorMethod<ICustomExportPost>("OnGetNoUseList", ref aCustomExport);
            if (null != aCustomExport[0])
            {
                if ((null != noUseList))
                {
                    noUseList.AddRange((List<string>)aCustomExport[0]);
                }
                else
                {
                    noUseList = (List<string>)aCustomExport[0];
                }
            }

            List<Type> exports = new List<Type>();
            string path = GenPath + "Unity/";
            foreach (Type t in types)
            {
                              //Generate(t, path)遍歷導(dǎo)出每個(gè)類 后面跟蹤一下
                if (filterType(t, noUseList, uselist) && Generate(t, path))
                    exports.Add(t);
            }
            //以上代碼導(dǎo)出所有系統(tǒng)類
            //調(diào)用static void GenerateBind(List<Type> list, string name, int order,string path)跟蹤一下
            GenerateBind(exports, "BindUnity", 0, path);
            if(autoRefresh)
                AssetDatabase.Refresh();
            Debug.Log("Generate engine interface finished");
        }
            //跟蹤一
        static bool Generate(Type t, string path)
        {
            return Generate(t, null, path);
        }
        
        static bool Generate(Type t, string ns, string path)
        {
            if (t.IsInterface)
                return false;
            
            CodeGenerator cg = new CodeGenerator();
            cg.givenNamespace = ns;
            cg.path = path;
            return cg.Generate(t);
        }

              //最終調(diào)用
        public bool Generate(Type t)
        {
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            
            if (!t.IsGenericTypeDefinition && (!IsObsolete(t) && t != typeof(YieldInstruction) && t != typeof(Coroutine))
                || (t.BaseType != null && t.BaseType == typeof(System.MulticastDelegate)))
            {
                if (t.IsEnum)
                {
                    StreamWriter file = Begin(t);
                    WriteHead(t, file);
                    RegEnumFunction(t, file);
                    End(file);
                }
                else if (t.BaseType == typeof(System.MulticastDelegate))
                {
                    if (t.ContainsGenericParameters)
                        return false;

                    string f = DelegateExportFilename(path, t);
                    
                    StreamWriter file = new StreamWriter(f, false, Encoding.UTF8);
                    file.NewLine = NewLine;
                    WriteDelegate(t, file);//跟蹤一下
                    file.Close();
                    return false;
                }
                else
                {
                    funcname.Clear();
                    propname.Clear();
                    directfunc.Clear();
                    
                    StreamWriter file = Begin(t);
                    WriteHead(t, file);
                    WriteConstructor(t, file);
                    WriteFunction(t, file,false);
                    WriteFunction(t, file, true);
                    WriteField(t, file);
                    RegFunction(t, file);
                    End(file);
                    
                    if (t.BaseType != null && t.BaseType.Name.Contains("UnityEvent`"))
                    {
                        string basename = "LuaUnityEvent_" + _Name(GenericName(t.BaseType)) + ".cs";
                        string f = path + basename;
                        string checkf = LuaCodeGen.GenPath + "Unity/" + basename;
                        if (!File.Exists(checkf)) // if had exported
                        {
                            file = new StreamWriter(f, false, Encoding.UTF8);
                            file.NewLine = NewLine;
                            WriteEvent(t, file);
                            file.Close();
                        }
                    }
                }
                
                return true;
            }
            return false;
        }

//有點(diǎn)長寂汇,可以大概過一下
         void WriteDelegate(Type t, StreamWriter file)
        {
            string temp = @"
           using System;
           using System.Collections.Generic;
           using LuaInterface;

          namespace SLua
          {
             public partial class LuaDelegation : LuaObject
           {
                static internal int checkDelegate(IntPtr l,int p,out $FN ua) {
                int op = extractFunction(l,p);
            if(LuaDLL.lua_isnil(l,p)) {
                ua=null;
                return op;
            }
            else if (LuaDLL.lua_isuserdata(l, p)==1)
            {
                ua = ($FN)checkObj(l, p);
                return op;
            }
            LuaDelegate ld;
            checkType(l, -1, out ld);
            if(ld.d!=null)
            {
                ua = ($FN)ld.d;
                return op;
            }
            LuaDLL.lua_pop(l,1);
            
            l = LuaState.get(l).L;
            ua = ($ARGS) =>
            {
                int error = pushTry(l);
";
            
            temp = temp.Replace("$TN", t.Name);
            temp = temp.Replace("$FN", SimpleType(t));
            MethodInfo mi = t.GetMethod("Invoke");
            List<int> outindex = new List<int>();
            List<int> refindex = new List<int>();
            temp = temp.Replace("$ARGS", ArgsList(mi, ref outindex, ref refindex));
            Write(file, temp);
            
            this.indent = 4;
            
            for (int n = 0; n < mi.GetParameters().Length; n++)
            {
                if (!outindex.Contains(n))
                    Write(file, "pushValue(l,a{0});", n + 1);
            }
            
            Write(file, "ld.pcall({0}, error);", mi.GetParameters().Length - outindex.Count);

            int offset = 0;
            if (mi.ReturnType != typeof(void))
            {
                offset = 1;
                WriteValueCheck(file, mi.ReturnType, offset, "ret", "error+");
            }
            
            foreach (int i in outindex)
            {
                string a = string.Format("a{0}", i + 1);
                WriteCheckType(file, mi.GetParameters()[i].ParameterType, i + offset, a, "error+");
            }
            
            foreach (int i in refindex)
            {
                string a = string.Format("a{0}", i + 1);
                WriteCheckType(file, mi.GetParameters()[i].ParameterType, i + offset, a, "error+");
            }
            
            
            Write(file, "LuaDLL.lua_settop(l, error-1);");
            if (mi.ReturnType != typeof(void))
                Write(file, "return ret;");
            
            Write(file, "};");
            Write(file, "ld.d=ua;");
            Write(file, "return op;");
            Write(file, "}");
            Write(file, "}");
            Write(file, "}");
        }

最后生成了每一個(gè)類的導(dǎo)出文件

            //跟蹤二

        static void GenerateBind(List<Type> list, string name, int order,string path)
        {
            CodeGenerator cg = new CodeGenerator();
            cg.path = path;
            cg.GenerateBind(list, name, order);
        }
                // class CodeGenerator
        public void GenerateBind(List<Type> list, string name, int order)
        {
            HashSet<Type> exported = new HashSet<Type>();
            string f = System.IO.Path.Combine(path , name + ".cs");
            StreamWriter file = new StreamWriter(f, false, Encoding.UTF8);
            file.NewLine = NewLine;
            Write(file, "using System;");
            Write(file, "using System.Collections.Generic;");
            Write(file, "namespace SLua {");
            Write(file, "[LuaBinder({0})]", order);
            Write(file, "public class {0} {{", name);
            Write(file, "public static Action<IntPtr>[] GetBindList() {");
            Write(file, "Action<IntPtr>[] list= {");
            foreach (Type t in list)
            {
                WriteBindType(file, t, list, exported);
            }
            Write(file, "};");
            Write(file, "return list;");
            Write(file, "}");
            Write(file, "}");
            Write(file, "}");
            file.Close();
        }
              

上面步驟導(dǎo)出了BindUnity.cs 生成了GetBindList() 獲取所有系統(tǒng)類導(dǎo)出清單

using System;
using System.Collections.Generic;
namespace SLua {
    [LuaBinder(0)]
    public class BindUnity {
        public static Action<IntPtr>[] GetBindList() {
            Action<IntPtr>[] list= {
                Lua_UnityEngine_AsyncOperation.reg,
                Lua_UnityEngine_AssetBundleCreateRequest.reg,
                Lua_UnityEngine_AssetBundleRequest.reg,
                Lua_UnityEngine_Object.reg,
                Lua_UnityEngine_AssetBundle.reg,
                Lua_UnityEngine_AssetBundleManifest.reg,
                Lua_UnityEngine_SendMessageOptions.reg,
                Lua_UnityEngine_PrimitiveType.reg,
                Lua_UnityEngine_Space.reg,
                Lua_UnityEngine_RuntimePlatform.reg,
    //省略
    }

那么生成的List是怎么使用的呢病往?

private void doBind(object state)
        {
            IntPtr L = (IntPtr)state;
            List<Action<IntPtr>> list = new List<Action<IntPtr>>();
            //省略
            var assemblyName = "Assembly-CSharp";
            Assembly assembly = Assembly.Load(assemblyName);
            list.AddRange(getBindList(assembly,"SLua.BindUnity"));
            list.AddRange(getBindList(assembly,"SLua.BindUnityUI"));
            list.AddRange(getBindList(assembly,"SLua.BindDll"));
            list.AddRange(getBindList(assembly,"SLua.BindCustom"));
          //...省略

當(dāng)LuaSvr將會(huì)把所有類加載到lua環(huán)境中,所以在lua代碼中import后可以調(diào)用該類骄瓣。
Slua導(dǎo)出類生成規(guī)則停巷。以O(shè)bject類為例:

static public void reg(IntPtr l) {
        getTypeTable(l,"UnityEngine.Object");
        //方法導(dǎo)出
        addMember(l,GetInstanceID);
        addMember(l,Destroy_s);
        addMember(l,DestroyImmediate_s);
        addMember(l,FindObjectsOfType_s);
        addMember(l,DontDestroyOnLoad_s);
        addMember(l,DestroyObject_s);
        addMember(l,Instantiate_s);
        addMember(l,FindObjectOfType_s);
        addMember(l,op_Equality);
        addMember(l,op_Inequality);
        //變量導(dǎo)出(可參照api)
        addMember(l,"name",get_name,set_name,true);
        addMember(l,"hideFlags",get_hideFlags,set_hideFlags,true);
        createTypeMetatable(l,constructor, typeof(UnityEngine.Object));
    }

方法導(dǎo)出

    [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
    static public int GetInstanceID(IntPtr l) {
        try {
            UnityEngine.Object self=(UnityEngine.Object)checkSelf(l);
            //調(diào)用C#中方法
            var ret=self.GetInstanceID();
           //返回值壓棧
            pushValue(l,true);
            pushValue(l,ret);
            return 2;
        }
        catch(Exception e) {
            return error(l,e);
        }
    }

變量導(dǎo)出
由于Lua調(diào)用C以方法的形式調(diào)用,所以稍微包裝了一下

    [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
    static public int get_name(IntPtr l) {
        try {
            UnityEngine.Object self=(UnityEngine.Object)checkSelf(l);
            pushValue(l,true);
            pushValue(l,self.name);
            return 2;
        }
        catch(Exception e) {
            return error(l,e);
        }
    }
    [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
    static public int set_name(IntPtr l) {
        try {
            UnityEngine.Object self=(UnityEngine.Object)checkSelf(l);
            string v;
            checkType(l,2,out v);
            self.name=v;
            pushValue(l,true);
            return 1;
        }
        catch(Exception e) {
            return error(l,e);
        }
    }

最后addMember方法

        // LuaObject.cs
        protected static void addMember(IntPtr l, LuaCSFunction func)
        {
            checkMethodValid(func);

            pushValue(l, func);
            string name = func.Method.Name;
            if (name.EndsWith("_s"))
            {
                name = name.Substring(0, name.Length - 2);
                LuaDLL.lua_setfield(l, -3, name);
            }
            else
                LuaDLL.lua_setfield(l, -2, name);
        }

        protected static void addMember(IntPtr l, LuaCSFunction func, bool instance)
        {
            checkMethodValid(func);

            pushValue(l, func);
            string name = func.Method.Name;
            LuaDLL.lua_setfield(l, instance ? -2 : -3, name);
        }

        protected static void addMember(IntPtr l, string name, LuaCSFunction get, LuaCSFunction set, bool instance)
        {
            checkMethodValid(get);
            checkMethodValid(set);

            int t = instance ? -2 : -3;

            LuaDLL.lua_createtable(l, 2, 0);
            if (get == null)
                LuaDLL.lua_pushnil(l);
            else
                pushValue(l, get);
            LuaDLL.lua_rawseti(l, -2, 1);

            if (set == null)
                LuaDLL.lua_pushnil(l);
            else
                pushValue(l, set);
            LuaDLL.lua_rawseti(l, -2, 2);

            LuaDLL.lua_setfield(l, t, name);
        }

        protected static void addMember(IntPtr l, int v, string name)
        {
            LuaDLL.lua_pushinteger(l, v);
            LuaDLL.lua_setfield(l, -2, name);
        }

所以在lua中調(diào)用C#方法形式為 :

local text_ui= GameObject.Find("Canvas/Text")--調(diào)用靜態(tài)方法
local textComponent= text_ui:GetComponent("Text")--調(diào)用成員方法
textComponent.text="123"--設(shè)置變量等價(jià)于textComponent["text"] = "123"

Slua的分析告一段落榕栏,是不是覺得用Lua控制組件比較麻煩畔勤,不能像C#腳本一樣直接可以把想要控制的組件掛到對(duì)應(yīng)的腳本下,可以思考一下怎么實(shí)現(xiàn)扒磁。參考鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末庆揪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子妨托,更是在濱河造成了極大的恐慌缸榛,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兰伤,死亡現(xiàn)場離奇詭異内颗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)敦腔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門均澳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人会烙,你說我怎么就攤上這事负懦。” “怎么了柏腻?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵纸厉,是天一觀的道長。 經(jīng)常有香客問我五嫂,道長颗品,這世上最難降的妖魔是什么肯尺? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮躯枢,結(jié)果婚禮上则吟,老公的妹妹穿的比我還像新娘。我一直安慰自己锄蹂,他們只是感情好氓仲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著得糜,像睡著了一般敬扛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上朝抖,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天啥箭,我揣著相機(jī)與錄音,去河邊找鬼治宣。 笑死急侥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的侮邀。 我是一名探鬼主播坏怪,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绊茧!你這毒婦竟也來了陕悬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤按傅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后胧卤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唯绍,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年枝誊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了况芒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡叶撒,死狀恐怖绝骚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情祠够,我是刑警寧澤压汪,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站古瓤,受9級(jí)特大地震影響止剖,放射性物質(zhì)發(fā)生泄漏腺阳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一穿香、第九天 我趴在偏房一處隱蔽的房頂上張望亭引。 院中可真熱鬧,春花似錦皮获、人聲如沸焙蚓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽购公。三九已至,卻和暖如春待德,著一層夾襖步出監(jiān)牢的瞬間君丁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工将宪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绘闷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓较坛,卻偏偏與公主長得像印蔗,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子丑勤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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