关于Lua的Unity UI面向过程编程模板
发表于2017-07-27
关于Lua的Unity UI面向过程编程模板
该模板基于xLua实现,对xLua不熟悉的可以去了解下。改用别的Lua热更方案也容易。
先发下示例地址
https://github.com/skylecn/xLua/tree/master/Assets/XLua/Examples/UIFrame
窗口如下
窗口左侧是一个列表,选择列表中的项,右边会显示当前选择项的内容。
下面是实现窗口功能的Lua文件。
require 'item' --列表项 local ue = CS.UnityEngine --窗口 Panel = { --Awake事件 --data是一个table,其中包含该UI的gameObject和注册的UI组件,也可以增加自己的变量 Awake = function (data) data.list = {}; --保存生成的Item的data local go = ue.Resources.Load('Item') for index=1,3 do local item = ue.Object.Instantiate(go) item.transform.parent = data.leftView.transform item.transform.localScale = ue.Vector3.one data.list[index] = item:GetComponent('FuncLuaBehavior').luaData Item.Init(data.list[index], data, index) end end; --选择某项 SelectItem = function (data, num) for index= 1, #data.list do data.num:GetComponent('Text').text=num Item.SetLight(data.list[index], index==num and true or false) end end; }
在以上文件中只定义了两个函数,数据data作为函数参数被处理,这就是面向过程的编程方式。那data具体是什么?又是如何被定义的呢?看下面的代码。
[LuaCallCSharp] public class FuncLuaBehavior : MonoBehaviour { public string luaFile; public Injection[] injections; internal static LuaEnv luaEnv = new LuaEnv(); //all lua behaviour shared one luaenv only! LuaTable dataTable; // Lua table,包括C#创建的和Lua中创建的数据 LuaTable funcTable; // Lua实现的事件响应函数和功能函数 public LuaTable luaData { get { return dataTable; } } void Awake() { //加载Lua文件 luaEnv.DoString(string.Format("require '{0}'", luaFile )); //定义dataTable dataTable = luaEnv.NewTable(); //插入需要处理的UI组件到dataTable中 dataTable.Set("gameObject", gameObject); foreach (var injection in injections) { dataTable.Set(injection.name, injection.value); } //获取Lua文件中函数table的引用 funcTable = luaEnv.Global.Get(luaFile); if (funcTable != null) { var luaAwake = funcTable.Get<action >("Awake"); if (luaAwake != null) { luaAwake(dataTable); } } } }</action
上面代码部分借鉴了xLua中LuaBehaviour的实现,面向过程的支持主要是dataTable和funcTable。dataTable即该窗口的“数据”,在Awake中创建并插入需要处理的UI组件。funcTable引用在Lua文件定义的table,也就是我们先前的Lua文件。然后我们触发Lua中的Awake事件,事件的参数就是dataTable。
这就是整个模板的结构。
以下是该示例另一部分Lua文件,结合代码中的注释来理解整个实现。
local ue = CS.UnityEngine --列表项 Item = { --Awake事件 Awake = function (data) data.button:GetComponent("Button").onClick:AddListener(function() Panel.SelectItem(data.panel, data._num) end) end; --初始化 Init = function (data, panel, num) data.panel = panel --保存父窗口引用 data._num = num --保存编号 data.num:GetComponent("Text").text = num end; --设置选中状态 SetLight = function (data, light) data.light:SetActive(light) end; }
为什么不面向对象,而要面向过程?
- 面向过程比面向对象简单清晰,容易上手,而UI功能也比较简单,就是事件响应,用面向过程就可以解决问题
- Lua的面向对象基于metatable实现,写一个类声明就需要好多行代码,相比高级语言的class声明还复杂
- Lua中定义的变量的作用域有全局和当前文件,但只能对应一个C#对象,如果C#的多个对象,就需要定义多个变量与之对应,实现背包这样的功能比较麻烦。使用面向过程,数据和功能分离,Lua中实现功能,数据由C#定义和管理。数据和UI组件的生命周期对应,Awake时定义,OnDestroy时销毁。数据作为参数递到Lua事件,在Lua事件响应中处理对应的UI组件。