ULUA的简洁用法(二)

《ULUA的简洁用法(二)》
作者: 游蓝海
文章链接:http://blog.csdn.net/you_lan_hai/article/details/70554237
转载请注明出处

写上一篇文章《ULUA简洁用法》的时候,我对ULUA的认识还不是很深,经过一段时间的摸索后,我又整理了另一种更简单的方式,不需要修改ULUA源码,一个代码文件就能容纳下所有需要的内容。

1.原理

给GameObject添加上一个C#脚本组件作为中间层,在中间层上绑定上一个LUA脚本,将Unity的所有回调接口通过中间层传递到LUA。同时,LUA脚本也可以通过中间层操作GameObject。

2.实现中间层LuaBehaviour

中间层从MonoBehaviour派生

添加public变量ScriptName,用于在编辑器属性界面中输入Lua的模块名。当中间层Awake的时候,根据ScriptName加载Lua文件,并实例化出Lua类对象。

实例化LuaClient

LuaClient在整个工程中应当只存在一个实例对象,我们可以把他绑定到一个不随关卡销毁的GameObject对象上(调用了DontDestroyOnLoadGameObject)。我们可以把实例化步骤也放到中间层脚本上,如此一来,只要场景中挂载了中间层脚本,LuaClient也就会被自动初始化。

调用成员方法

对于只调用一次的Lua层方法,我们没必要在C#端去记录这个成员变量,比如Lua层面的AwakeStart方法。为了方便使用,我们在需要自动将self作为第一个参数,传递给Lua方法。

object[] CallMethod(string func, params object[] args)

中间层关键代码

using UnityEngine;
using System;
using System.Collections;
using LuaInterface;

public class LuaBehaviour : MonoBehaviour
{
    public string           ScriptName;

    protected LuaState      luaState_;
    protected LuaTable      self_;

    public LuaTable script
    {
        get{ return self_; }
    }

    protected void Awake()
    {
        // 这一步会触发LuaClient实例化。
        luaState_ = GetMainLuaState();

        if (ScriptName != null && ScriptName != null)
        {
            loadScript(ScriptName);

            //尝试调用脚本对象的Awake函数
            CallMethod("Awake");
        }
    }

    // 根据Lua模块名,加载Lua脚本
    public bool loadScript(string scriptName)
    {
        if (scriptName == null)
        {
            Debug.LogError("The ScriptName must be set.");
            return false;
        }
        ScriptName = scriptName;

        // require lua文件,得到返回的类
        LuaTable metatable = (LuaTable)require(ScriptName);
        if (metatable == null)
        {
            Debug.LogError("Invalid script file '" + ScriptName + "', metatable needed as a result.");
            return false;
        }

        // 从类中找到New函数
        LuaFunction lnew = (LuaFunction)metatable["New"];
        if (lnew == null)
        {
            Debug.LogError("Invalid metatable of script '" + ScriptName + "', function 'New' needed.");
            return false;
        }

        //执行New函数生成脚本对象
        object[] results = lnew.Call(metatable);
        if (results == null || results.Length == 0)
        {
            Debug.LogError("Invalid 'New' method of script '" + ScriptName + "', a return value needed.");
            return false;
        }

        //存贮脚本对象
        bindScript((LuaTable)results[0]);
        return true;
    }

    // 将一个已经存在的Lua对象绑定到当前组件中。
    // 用于在脚本中动态创建对象,并手动挂接Lua对象。
    public void bindScript(LuaTable script)
    {
        self_ = script;

        //给脚本对象设置上常用的属性
        self_["transform"] = transform;
        self_["gameObject"] = gameObject;
        self_["behaviour"] = this;
    }

    // 调用Lua端的成员方法。会自动将self作为第一个参数,传递到Lua。
    protected object[] CallMethod(string func, params object[] args)
    {
        if (self_ == null)
        {
            return null;
        }

        LuaFunction lfunc = (LuaFunction)self_[func];
        if (lfunc == null)
        {
            return null;
        }

        //等价于lua语句: self:func(...)
        int oldTop = lfunc.BeginPCall();
        lfunc.Push(self_);
        lfunc.PushArgs(args);
        lfunc.PCall();
        object[] objs = luaState_.CheckObjects(oldTop);
        lfunc.EndPCall();
        return objs;
    }

    // 自己实现一个lua require函数,可以得到require的返回值。
    public object require(string fileName);

    // 这个函数是实例化LuaClient的入口。代码中获取LuaClient都调用这个静态函数。
    public static LuaClient GetLuaClient()
    {
        if (LuaClient.Instance == null)
        {
            Debug.Log("LuaBehaviour create LuaClient");

            GameObject obj = new GameObject();
            obj.name = "LuaClient";

            // 绑定LuaClient组件
            obj.AddComponent<LuaClient>();

            // 让obj常驻内存,不自动卸载
            DontDestroyOnLoad(obj);
        }
        return LuaClient.Instance;
    }

    public static LuaState GetMainLuaState()
    {
        GetLuaClient();
        return LuaClient.GetMainState();
    }
}

完整代码看这里

3.添加LUA脚本层

在Assets/Lua目录下新建LUA脚本Test.lua,核心代码如下:

--构造Test类
local Test = {}
Test.__index = Test --让实例对象的__get方法指向Test类

--给Test类实例化一个对象
function Test.New(cls)
    local self = {}
    setmetatable(self, cls)
    return self
end

--Awake方法
function Test:Awake()
    print("Test:Awake", self)
end

--将类Test返回。通过require函数的返回值就可以获取到此值了。
return Test

4.测试

完整代码地址:https://github.com/youlanhai/tolua

下载代码后,用Unity打开Test2.unity工程,执行一次菜单Lua/Generate All。然后点击运行,会看到一个上下运动的立方体。

5.总结

相比于上一篇文章,这一次只手动实现了一个C#类,不需要额外的辅助代码,也不需要修改ToLua的源码。代码更容易集成到最新的ToLua源码中。

原文地址:https://www.cnblogs.com/ygxsk/p/7693970.html