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
对象上(调用了DontDestroyOnLoad
的GameObject
)。我们可以把实例化步骤也放到中间层脚本上,如此一来,只要场景中挂载了中间层脚本,LuaClient
也就会被自动初始化。
调用成员方法
对于只调用一次的Lua层方法,我们没必要在C#端去记录这个成员变量,比如Lua层面的Awake
和Start
方法。为了方便使用,我们在需要自动将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源码中。