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源码中。