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()
    return display.newLayer()
end)
-- UI结构定义
XxxLayer.uiTree = {}
-- 自定义内容

-- 构造函数
--[[
    params:
--]]
function XxxLayer:ctor(params)
    -- 初始化Layer, 可传入参数详情参见BaseLayer
    self.super.ctor(self)
    -- 初始化UI
    self:initUI()
end
-- 初始化UI
function XxxLayer: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 and params.pos.y)
        if params.anchor then
            retSprite:setAnchorPoint(params.anchor)
        end
        if params.scale then
            retSprite:setScale(params.scale)
        end
        return retSprite
    end,
}

 

所有控件都通过此格式来定义,编辑器打开时就可以解析这个lua文件,获得所有的控件类型。其中hint表示控件或参数提示,params中是此控件的所有参数,type主要用于参数设定的个性化(如ccp类型, number类型, file类型甚至可以直接给出文件选择),required表示此参数必须设置并给出了缺省值。可以看到此控件格式最终返回的是一个function, 那么编辑器中创建Sprite结点时,实际上就是调用了一次此function, 而实际游戏代码中仍然也是调用此function来创建Sprite结点, 那么就实现了编辑中的所见及所得。

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

function BaseLayer:createUITree()
    -- 创建一个cocos2dx结点列表并添加到指定的父结点上
    local function createChildrenNode(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") then
                self[item.name] = newNode
            end
            -- 创建子结点
            if next(item.children) then
                createChildrenNode(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] then
        item.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参数值不一样而已。

 

posted @ 2018-09-19 11:22  hghhe  阅读(1253)  评论(0编辑  收藏  举报