关于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<LuaTable>(luaFile);
if (funcTable != null)
{
var luaAwake = funcTable.Get<Action<LuaTable>>("Awake");
if (luaAwake != null)
{
luaAwake(dataTable);
}
}
}
}
上面代码部分借鉴了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组件。