lua搭建ui_cocos2dx-lua UI编辑器的设计思路

在目前的cocos2dx项目开发中,基本只有2个编辑器可选。一个是现在官方推荐的CocosCreator, 但它并不支持我们常用的lua脚本。另一个是CocosStudio, 官方已经不再对其维护,且使用也并不太方便。那么有没有什么方法能让编辑器更好的接入lua脚本,又能方便地自定义控件呢?

想要编辑器对lua脚本更好地支持,那么我们的编辑器可以用lua来开发,这样编辑器可以直接分析lua代码。这里推荐使用imgui来开发编辑器, 比直接用cocos-x更快,关于imgui请点击:cocos2dx上适合做工具的UI库ImGui。为了让实现编辑器中结点名与lua变量的自动绑定,我想到了让所有界面继承于BaseLayer,在基类中自动处理lua变量和事件的绑定。一个典型的游戏界面如下,由编辑器自动生成:

local XxxLayer = class("XxxLayer", require("common.BaseLayer"), function()returndisplay.newLayer()end)--UI结构定义

XxxLayer.uiTree ={}--自定义内容

--构造函数--[[params:

--]]

functionXxxLayer:ctor(params)--初始化Layer, 可传入参数详情参见BaseLayer

self.super.ctor(self)--初始化UI

self:initUI()end

--初始化UI

functionXxxLayer:initUI()

self:createUITree()--进行UI的额外调整

end

return XxxLayer

其中createUITree就是由BaseLayer提供的方法,主要创建uiTree中定义的控件。我们约定"-- 自定义内容"之前的交给编辑器,用户不能编辑(编辑器保存时会被重置)。uiTree中保存的是结点的树形结构,里面定义了结点的类型及各种创建参数,比如加入一个按钮后,会变成:

XxxLayer.uiTree = {[1]={["cType"]="Button",["children"]={},["name"]="closeBtn",["params"]={["image"]="c_13.png",["pos"]={["x"]=320,["y"]=568,},["scale"]=1,},},}

为了更好的支持控件的自定义,我们可以定义一种简单的控件格式,如:

UIWrap ={--[[return {

hint = "创建sprite",

params = {

{name = "image", type="file", hint = "图片名", required = "c_11.png"},

{name = "pos", type="ccp", hint = "位置", required = cc.p(100, 100)},

{name = "anchor", type="ccp", hint = "锚点"},

{name = "scale", type="number", hint = "缩放"},

},

}

--]]["Sprite"] = function(params)local retSprite = display.newSprite(params.image, params.pos and params.pos.x, params.pos andparams.pos.y)if params.anchor thenretSprite:setAnchorPoint(params.anchor)end

if params.scale thenretSprite:setScale(params.scale)end

returnretSpriteend,

}

所有控件都通过此格式来定义,编辑器打开时就可以解析这个lua文件,获得所有的控件类型。其中hint表示控件或参数提示,params中是此控件的所有参数,type主要用于参数设定的个性化(如ccp类型

, number类型

, file类型甚至可以直接给出文件选择

),required表示此参数必须设置并给出了缺省值。可以看到此控件格式最终返回的是一个function, 那么编辑器中创建Sprite结点时,实际上就是调用了一次此function, 而实际游戏代码中仍然也是调用此function来创建Sprite结点, 那么就实现了编辑中的所见及所得。

BaseLayer:createUITree方法里面主要是循环创建uiTree里面定义的结点,并将结点名和事件与layer进行绑定。大致流程如下:

functionBaseLayer:createUITree()--创建一个cocos2dx结点列表并添加到指定的父结点上

local functioncreateChildrenNode(children, parentNode)for _,item in ipairs(children) do

--使用编辑器中的参数创建结点

local newNode =UIWrap[item.cType](item.params)

parentNode:addChild(newNode)--[[默认的结点名以"untitled"开头,表示我们并不关心此结点

将需要访问的结点,直接绑定到self上

如结点名为"closeBtn", 代码中可以用self.closeBtn直接访问

--]]

if not string.find(item.name, "untitled") thenself[item.name]=newNodeend

--创建子结点

if next(item.children) thencreateChildrenNode(item.children, newNode)end

end

end

--从uiTree开始创建

createChildrenNode(self.uiTree, self)end

按钮事件绑定: 有些特殊控件(如button等)需要设定点击回调,在lua代码中对应的是function类型。为了实现编辑器中参数与lua代码中function的绑定,我们在编辑器中记录下绑定的lua函数名(通过分析Layer的function类型可获得所有函数名)。然后在创建结点之前,将回调参数与Layer中的函数进行绑定:

--结点创建之前,点击事件绑定

for key, value in pairs(item.params) do

--我们规定点击事件必须以"on"开头来过滤Layer中普通的函数名

if type(value) == "string" and string.find(value, "on") == 1 and self[value] thenitem.params[key]=handler(self, self[value])end

end

模版控件处理: 对于像ListView这样的控件,需要特殊处理。我们可以定义一个特殊控件”Layout“,它并不随Layer的创建而创建(在createChildrenNode中发现控件类型为Layout时,则仅记录其结点树数据,不创建其结点),只能代码调用BaseLayer:createLayoutNode来创建。createLayoutNode函数与createUITree类似,只是创建内容变成了Layout和它的子结点。填充ListView时,可以这样:

--填充ListView数据

for i=1,10 do

local childLayout = self:createLayoutNode("ListLayout")

self.goodsListView:pushBackCustomItem(childLayout)--下面对各childLayout设置表现差异

end

我们甚至可以在编辑器中创建多个不同的Layout,来丰富ListView上的显示效果(如ListView中最后显示一个"更多")。

在编辑器实现基本的布局、增删、修改功能后,控件类型的增加就已经和编辑器无关了。我们甚至可以创建一些复杂的控件类型,如英雄头像,英雄星级之类。比如

之间仅仅就是创建的starNum参数值不一样而已。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值