根据ToLua框架自带的例子,小白的学习之旅

小白学Lua

学习中我坚信要知其然,也要知其所以然,不能只当API的搬运工。

文章不带任何教学性,只是记录我的学习过程。

因为我是写了快3年C#工作写了1年半,学习1年半,最近工作需要开始触碰Lua,在这里是根据Git上的ToLua框架来进行学习。
直接开始,先看下下来的第一个例子:

01_HelloWorld

public class HelloWorld : MonoBehaviour
{
    void Awake()
    {
        LuaState lua = new LuaState();
        lua.Start();
        string hello =
            @"                
                print('hello tolua#')                                  
            ";
        
        lua.DoString(hello);
        lua.CheckTop();
        lua.Dispose();
        lua = null;
    }
}

很简单的几行句子,简单梳理一下整合出来:
1.new一个虚拟机
2.开启状态机
3.DoString()直接把代码扔进去就OK
4.检查一下状态机状态,暂且可以理解为我F12点进去之后看了下,应该是检查了一些东西,可能是方法之类的然后返回,如果不为0就会给一个警告
5.关闭虚拟机
6设为Null等待回收

2.ScriptsFromFile

点开后就看到代码上写好了展示require与dofile的区别
去掉一些GUI展示代码后

if (GUI.Button(new Rect(50, 50, 120, 45), "DoFile"))
{
    strLog = "";
    lua.DoFile("ScriptsFromFile.lua");                        
}
else if (GUI.Button(new Rect(50, 150, 120, 45), "Require"))
{
    strLog = "";            
    lua.Require("ScriptsFromFile");            
}

区别只是这里的Dofile后面跟上了后缀,Require后面没有。
然后我F12进去看了一下,
DoFile
dofile是直接就对文件进行了操作,跟进进去

public void DoFile(string fileName)
{
    byte[] buffer = LoadFileBuffer(fileName);
    fileName = LuaChunkName(fileName);
    LuaLoadBuffer(buffer, fileName);
}

首先看到了Load读取,继续跟进

        byte[] LoadFileBuffer(string fileName)
        {
#if UNITY_EDITOR
            if (!beStart)
            {
                throw new LuaException("you must call Start() first to initialize LuaState");
            }
#endif
            byte[] buffer = LuaFileUtils.Instance.ReadFile(fileName);

            if (buffer == null)
            {
                string error = string.Format("cannot open {0}: No such file or directory", fileName);
                error += LuaFileUtils.Instance.FindFileError(fileName);
                throw new LuaException(error);
            }

            return buffer;
        }

有一行ReadFile就可以看到了,OK他会读取这个文件内的东西。
然后Check检查,检查没有问题,就会执行,具体执行的内部代码就不说了,我简单看了一下,因为也不懂这些,也只能懂大概。

Require
同样跟进,

public void Require(string fileName)
{
    int top = LuaGetTop();
    int ret = LuaRequire(fileName);

    if (ret != 0)
    {                
        string err = LuaToString(-1);
        LuaSetTop(top);
        throw new LuaException(err, LuaException.GetLastError());
    }

    LuaSetTop(top);            
}

发现他会先得到当前状态,然后找文件

        public int LuaRequire(string fileName)
        {
#if UNITY_EDITOR
            string str = Path.GetExtension(fileName);

            if (str == ".lua")
            {
                throw new LuaException("Require not need file extension: " + str);
            }
#endif
            return LuaDLL.tolua_require(L, fileName);
        }

我发现这里他会先判断一下后缀是不是.lua,如果不是就会报错。
最后返回把他给设置到状态机上等待调用。

总结:Dofile与Require有个很大的区别就是Dofile不论什么文件都可以读而Require则需要你的后缀一定是.Lua不然就会报错至于中间的流程因为本人学疏才浅不得而知,希望有大佬能告诉我,最后的执行步骤都是一致的。

3.CallLuaFunction

Lua代码就不谈了很简单

    private string script =
        @"  function luaFunc(num)                        
                return num + 1
            end

            test = {}
            test.luaFunc = luaFunc
        ";

造一个叫luaFunc的方法,
声明一个table test,
为test.luaFunc赋值为方法luaFunc。
往下看代码

    LuaFunction luaFunc = null;
    LuaState lua = null;
    string tips = null;

除了LuaState还多了你个LuaFunction
除开GUI代码

        new LuaResLoader();
        lua = new LuaState();
        lua.Start();
        DelegateFactory.Init();        
        lua.DoString(script);

        //Get the function object
        luaFunc = lua.GetFunction("test.luaFunc");

        if (luaFunc != null)
        {
            int num = luaFunc.Invoke<int, int>(123456);
            Debugger.Log("generic call return: {0}", num);

            num = CallFunc();
            Debugger.Log("expansion call return: {0}", num);

            Func<int, int> Func = luaFunc.ToDelegate<Func<int, int>>();
            num = Func(123456);
            Debugger.Log("Delegate call return: {0}", num);
            
            num = lua.Invoke<int, int>("test.luaFunc", 123456, true);
            Debugger.Log("luastate call return: {0}", num);
        }

        lua.CheckTop();

第一行这个代码就很让我疑惑干啥用的?F12进去之后也没看明白。那就注释掉看看吧。
也没问题?
那就不管继续看,创建虚拟机,开启,执行代码。
然后luaFunc这里等于 lua.GetFunction(我们的类名.方法名)
然后分别看

第一个 Invoke

	int num = luaFunc.Invoke<int, int>(123456);

F12跟进

        public R1 Invoke<T1, R1>(T1 arg1)
        {
            BeginPCall();
            PushGeneric(arg1);
            PCall();
            R1 ret1 = CheckValue<R1>();
            EndPCall();
            return ret1;
        }

因为这里代码有点复杂我也讲不出来,但自个也能理解,简单就我看来就是先压栈,然后返回一个引用出来,然后把我们这个方进去,然后调用,然后检查栈里的值,最后出栈结束调用返回一个值。

第二个 CallFunc
F12

    int CallFunc()
    {        
        luaFunc.BeginPCall();                
        luaFunc.Push(123456);
        luaFunc.PCall();        
        int num = (int)luaFunc.CheckNumber();
        luaFunc.EndPCall();
        return num;                
    }

手动调用了刚刚的代码Begin,Push,Call,Check,End,返回

第三个 Func
第一眼看过去就知道了委托,同样F12

public T ToDelegate<T>() where T : class
{
    return DelegateTraits<T>.Create(this) as T;
}

可以看到这里是自己造了一个委托,详细的就不跟进了。

第四个 lua.Invoke
乍一看没啥不一样的,仔细一看是从lua虚拟机调用的,不是Func,F12

        public R1 Invoke<T1, R1>(string name, T1 arg1, bool beLogMiss)
        {
            int top = LuaDLL.lua_gettop(L);

            try
            {
                if (BeginCall(name, top, beLogMiss))
                {
                    PushGeneric(arg1);
                    Call(1, top + 1, top);
                    R1 ret1 = CheckValue<R1>(top + 2);
                    LuaDLL.lua_settop(L, top);
                    return ret1;
                }

                return default(R1);
            }
            catch (Exception e)
            {
                LuaDLL.lua_settop(L, top);
                throw e;
            }
        }

继续跟进发现代码和Func都差不多,所以也就不详谈了。

总结:
所有的方法调用都会经历一个Begin,Push,Call,Check,End 这里我认为如果没有返回值就不会有Check,不过没测试,以后应该是有机会的。
在调用上没有太大的差异,
luaFunc.Invoke为泛型会有返回值
CallFunc是最基本的调用方法
luaFunc.ToDelegate作为泛型委托
lua.Invoke则是直接从Lua虚拟机调用而不是LuaFunc

就先到这里要吃晚饭了,晚上学习Socket编程了,明天这里再继续吧。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值