游戏开发解答Unity使用lua将table转为树结构,以多级折叠内容列表的UI形式展现(树结构 | UGUI | 折叠展开 | lua)
Posted 林新发
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了游戏开发解答Unity使用lua将table转为树结构,以多级折叠内容列表的UI形式展现(树结构 | UGUI | 折叠展开 | lua)相关的知识,希望对你有一定的参考价值。
文章目录
本文最终效果
一、前言
嗨,大家好,我是新发。
有粉丝问了我这个问题,
我以为他是拿到一个纯文本数据,他不知道如何去解析,如果单纯是想解析数据,完全不需要使用树。
看格式的话是lua
的table
,我们如果拿到一个纯文本数据,并且格式是lua
的table
的格式的话,可以使用loadstring
方法去执行纯文本得到一个table
对象,例:
local data_str = ' a = 1, b = 2, c=3, d = 4, e = x = 5 '
local func = loadstring('return ' .. data_str)
local tb = func()
-- TODO 解析tb这个table
这里需要注意,loadstring
方法在lua 5.2
及以上版本改为了load
,所以如果你的lua
版本是5.2
及以上版本需要注意,我们可以查看lua
源码的lbaselib.c
文件,
经过确认,他那边已经转化成了lua
的table
了,只是不知道怎么用树去构造出来递归遍历,
好了,现在就来讲讲具体怎么做吧~
注:上面提到的
红点
是指我之前写的另一篇文章:【游戏开发实战】手把手教你在Unity中使用lua实现红点系统(前缀树 | 数据结构 | 设计模式 | 算法 | 含工程源码)
二、Unity lua环境
因为要使用lua
,而Unity
默认用的是C#
,所以我们得搞个lua
框架进来,正好,我之前自己搭建了一个游戏框架UnityXFramework
,里面集成了tolua
框架,详细可以查看我之前写的这篇博客:【游戏开发框架】自制Unity通用游戏框架UnityXFramework,详细教程(Unity3D技能树 | tolua | 框架 | 热更新)
框架开源地址:https://gitcode.net/linxinfa/UnityXFramework
这里我就在框架的环境中去写lua
逻辑吧~
三、树节点
1、创建脚本:TreeNode.lua
先在LuaFramework/Lua/Logic
目录中新建一个Tree
目录,
在Tree
目录中新建一个TreeNode.lua
脚本,
2、封装节点
先思考一下,一个节点需要的信息,画个图,
现在我们写下代码,TreeNode.lua
脚本代码如下,
-- TreeNode.lua 树节点
TreeNode = TreeNode or
TreeNode.__index = TreeNode
function TreeNode.New(name)
local self =
-- 节点名
self.name = name
-- 值
self.value = nil
-- 父节点
self.parent = nil
-- 子节点
self.child = nil
-- 缩进
self.tab = 0
-- 是否展开
self.isopen = true
-- UI对象
self.uiObj = nil
setmetatable(self, TreeNode)
return self
end
四、树逻辑
1、创建脚本:TreeLogic.lua
我们再在Tree
目录中新建一个TreeLogic.lua
脚本,
先写个Init
方法,留个TODO
,如下
-- TreeLogic.lua 树逻辑
TreeLogic = TreeLogic or
local this = TreeLogic
-- 根节点
this.root = nil
-- 初始化
function TreeLogic.Init()
-- TODO
end
2、构造测试数据
我们要构造一棵树,得先有数据,根据需求,数据就是一个table
,简单写一个,
-- 测试数据
local data_table =
name = "林新发",
university = "华南理工大学",
major = '信息工程',
job = 'Unity3D游戏开发工程师',
blog = 'https://blog.csdn.net/linxinfa',
hobby = '吉他', '钢琴', '画画', '撸猫',
dream =
developer =
target = '成为一名优秀的独立游戏开发者',
style = 'ARPG', 'FPS', 'SLG', 'MOBA'
,
painter =
target = '成为一个独立画家',
magnum_opus = '暴走柯南', '皮皮猫', '光'
,
musician =
target = '成为一个独立音乐人',
magnum_opus = '尘土', '树与风'
3、构造树
接着我们用测试数据去构造一棵树,我们封装一个MakeTree
方法,其实构造过程我们只需要把注意力放在一个节点的构造上即可,设置节点的名称、值,设置节点的父子节点关系,然后递归执行。
代码如下
-- TreeLogic.lua
-- 构造树
-- tb: 数据table
-- parent: 父节点
function TreeLogic.MakeTree(tb, parent)
-- 遍历table
for k, v in pairs(tb) do
-- 新建一个节点
local node = TreeNode.New(k)
node.value = v
-- 设置父节点
node.parent = parent
-- 子节点缩进+1
node.tab = parent.tab + 1
-- 父节点的child塞入node
if nil == parent.child then
parent.child =
end
parent.child[k] = node
-- 如果v是table,则递归遍历
if type(v) == 'table' then
-- 有子节点,默认不展开
node.isopen = false
this.MakeTree(v, node)
end
end
return parent
end
接着,我们在Init
方法中调用MakeTree
方法,
-- 初始化
function TreeLogic.Init()
-- 测试数据 data_table =
-- ...
-- 根节点
this.root = TreeNode.New("Root")
-- 构造树
this.root = this.MakeTree(data_table, this.root)
end
到这里,我用了30
行左右的代码完成了节点的封装和树的构造,接下来就是树的UI
显示了。
4、打印树
在做UI
显示之前,我们不妨封装一个打印树的方法,验证一下我们的树结构,
-- 把树转为字符串
function TreeLogic.TreeToString(node, str)
if nil ~= node.value then
local tabspace = ''
for i = 1, node.tab do
tabspace = tabspace .. ' '
end
if 'table' == type(node.value) then
str = str .. string.format('%s▼ %s :\\n', tabspace, node.name)
else
str = str .. string.format('%s● %s : %s\\n', tabspace, node.name, tostring(node.value))
end
end
if nil ~= node.child then
for _, child_node in pairs(node.child) do
-- 递归
str = this.TreeToString(child_node, str)
end
end
return str
end
我们调用一下
-- 打印树
local str = ''
str = this.TreeToString(this.root, str)
log(str)
输出结果如下
输出正常,愉快地继续吧~
五、使用UGUI显示树
1、制作界面预设
制作一个TreePanel.prefab
界面预设,
如下
界面层级结构如下,其中我用了VerticalLayoutGroup
组件来做垂直布局,这样添加子节点的时候就会自动垂直排列了,
其中item
上挂一个Button
用于监听点击,子节点是一个Text
,缩进就通过Text
的坐标右移来实现即可,
2、创建界面脚本:TreePanel.lua
在LuaFramework/Lua/View
目录中创建一个Tree
文件夹,然后创建一个TreePanel.lua
脚本,编写界面代码,
界面代码我做了模板,可以参见我之前写的框架教程:【游戏开发框架】自制Unity通用游戏框架UnityXFramework,详细教程(Unity3D技能树 | tolua | 框架 | 热更新) 的8.2
小节:
下面我重点写下递归展开树节点和关闭节点的逻辑~
3、展开节点(递归)
封装一个张开节点的方法ExpanNode
,里面我用到了递归,代码我写了注释,这里就不多解释啦,
-- TreePanel.lua
-- 展开节点
function TreePanel.ExpanNode(node)
if nil == node.child then
return
end
local index = 1
for _, child_node in pairs(node.child) do
-- 创建节点的UI对象
local uiObj = LuaUtil.CloneObj(this.tiemForClone)
local text = uiObj.transform:GetChild(0):GetComponent("Text")
child_node.uiObj = uiObj
if not LuaUtil.IsNilOrNull(node.uiObj) then
-- 子节点塞在父节点下面
local siblingIndex = node.uiObj:GetComponent("RectTransform"):GetSiblingIndex()
child_node.uiObj:GetComponent("RectTransform"):SetSiblingIndex(siblingIndex + index)
index = index + 1
end
if type(child_node.value) == 'table' then
text.text = (child_node.isopen and '▼ ' or '► ') .. child_node.name
else
text.text = '● ' .. child_node.name .. ': ' .. child_node.value
end
-- 坐标缩进
text.transform.localPosition = text.transform.localPosition + Vector3.New((child_node.tab-1)*50, 0,0)
uiObj:GetComponent("Button").onClick:AddListener(function()
if not child_node.isopen then
child_node.isopen = true
-- 递归, 展开子节点
this.ExpanNode(child_node)
else
-- 关闭子节点
this.CloseNode(child_node)
end
if type(child_node.value) == 'table' then
text.text = ( child_node.isopen and '▼ ' or '► ') .. child_node.name
end
end)
end
end
我们需要传入树的根节点,我们给TreeLogic.lua
添加一个GetTree
方法,
-- TreeLogic.lua
function TreeLogic.GetTree()
return this.root
end
接着我们去调用ExpanNode
方法,如下
local tree = TreeLogic.GetTree()
this.ExpanNode(tree)
4、关闭节点(递归)
关闭节点也封装一个方法CloseNode
,依然使用了递归,如下
-- 关闭子节点
function TreePanel.CloseNode(node)
if LuaUtil.IsNilOrNull(node.child) then
return
end
node.isopen = false
for _, child in pairs(node.child) do
child.isopen = false
LuaUtil.SafeDestroyObj(child.uiObj)
if nil ~= child.child then
-- 递归关闭子节点
this.CloseNode(child)
end
end
end
六、测试
好啦,现在我们测试一下效果吧,
完美,收工~
代码我已经提交到框架上了,可下载工程进行查阅,https://gitcode.net/linxinfa/UnityXFramework
七、更新(2022/03/25)
文章发布后,这位同学自己顺利做出来了,
不过昨天他又发了消息问我如何保持上一次展开的节点或状态,
我改造了一下UI
层的代码,把节点的UI
对象缓存到table
里,展开时先从缓存中取UI
对象,关闭节点时隐藏UI
对象,保持子节点的isopen
数据状态,展开时进行递归,最后改版后的代码如下:
-- TreePanel.lua
-- ...
this.uiNodeTb = nil
function TreePanel:OnShow(parent)
-- ...
this.uiNodeTb =
end
-- ...
-- 展开节点
function TreePanel.ExpanNode(node)
if nil == node.child then
return
end
local index = 1
for _, child_node in pairs(node.child) do
local uiUnit =
if this.uiNodeTb[child_node] then
-- 从缓存中取ui对象
uiUnit = this.uiNodeTb[child_node]
-- 显示
LuaUtil.SafeActiveObj(uiUnit.obj, true)
if child_node.isopen then
-- 递归, 展开子节点
this.ExpanNode(child_node)
end
else
-- 创建节点的UI对象
uiUnit.obj = LuaUtil.CloneObj(this.tiemForClone)
uiUnit.text = uiUnit.obj.transform:GetChild(0):GetComponent("Text")
uiUnit.btn = uiUnit.obj:GetComponent("Button")
-- 坐标缩进
uiUnit.text.transform.localPosition = uiUnit.text.transform.localPosition +
Vector3.New((child_node.tab - 1) * 50, 0, 0)
child_node.uiObj = uiUnit.obj
if not LuaUtil.IsNilOrNull(node.uiObj) then
-- 子节点塞在父节点下面
local siblingIndex = node.uiObj:GetComponent("RectTransform"):GetSiblingIndex()
child_node.uiObj:GetComponent("RectTransform"):SetSiblingIndex(siblingIndex + index)
index = index + 1
end
uiUnit.btn.onClick:AddListener(function()
if not child_node.isopen then
child_node.isopen = true
-- 递归, 展开子节点
this.ExpanNode(child_node)
else
-- 关闭子节点
this.CloseNode(child_node, false)
end
if type(child_node.value) == 'table' then
uiUnit.text.text = (child_node.isopen and '▼ ' or '► ') .. child_node.name
end
end)
this.uiNodeTb[child_node] = uiUnit
end
-- 更新展开文本
if type(child_node.value) == 'table' then
uiUnit.text.text = (child_node.isopen and '▼ ' or '► ') .. child_node.name
else
uiUnit.text.text = '● ' .. child_node.name .. ': ' .. child_node.value
end
end
end
-- 关闭子节点
function TreePanel.CloseNode(node, onlyHide)
if LuaUtil.IsNilOrNull(node.child) then
return
end
if not onlyHide then
node.isopen = false
end
for _, child in pairs(node.child) do
LuaUtil.SafeActiveObj(child.uiObj, false)
if nil ~= child.child then
-- 递归关闭子节点
this.CloseNode(child, true)
end
end
end
运行效果如下:
展开全部节点和关闭全部节点的代码我没写,就当做一个小作业留给大家思考吧~
好了,我是林新发,https://blog.csdn.net/linxinfa
一个在小公司默默奋斗的Unity
开发者,希望可以帮助更多想学Unity
的人,共勉~
以上是关于游戏开发解答Unity使用lua将table转为树结构,以多级折叠内容列表的UI形式展现(树结构 | UGUI | 折叠展开 | lua)的主要内容,如果未能解决你的问题,请参考以下文章
游戏开发解答Unity使用lua将table转为树结构,以多级折叠内容列表的UI形式展现(树结构 | UGUI | 折叠展开 | lua)
游戏开发实战手把手教你在Unity中使用lua实现红点系统(前缀树 | 数据结构 | 设计模式 | 算法 | 含工程源码)
游戏开发实战手把手教你在Unity中使用lua实现红点系统(前缀树 | 数据结构 | 设计模式 | 算法 | 含工程源码)
游戏开发框架自制Unity通用游戏框架UnityXFramework,详细教程(Unity3D技能树 | tolua | 框架 | 热更新)
游戏开发框架自制Unity通用游戏框架UnityXFramework,详细教程(Unity3D技能树 | tolua | 框架 | 热更新)