昨天在cocos2dx的一个群里,遇到一位匿名为x的朋友询问的问题,是关于ui的.他使用c++写了不少的ui封装节点,用来实现游戏中的各种不同效果.然后现在想改用lua,于是尝试使用最小代价去复用自己的代码.当然这个是可以做到的,相信很多人都是知道方法的.今天的这篇文章就来谈谈ui部分的处理以及个人的见解.

我们都知道,cocos2dx引擎提供了ui工具cocostudio.后来改名为cocos engine.这些就不赘述了,很多人都会使用这款工具.新版本的工具我没有使用过,不过我承认是方便了很多.可是很多时候我们期望有着自己一套的资源管理方式,尤其是项目琐碎ui资源,效果资源等等一大堆的时候,后期优化app包大小的时候也是个问题. 所以我个人倾向的方案是提供基于项目的ui库.当然对于lua这样的语言不存在库的说法,我在fw目录下面提供了一个ui模块,为了解决自己提供的ui方式,首先得想明白我们如何去使用自己的ui模块.我期望的方式是这个样子的:

  1. local component_cfg =
  2. {
  3. {
  4. name = "bg_img",
  5. creator = ui_parser.parsers.img, -- 基于cocos2dx ui
  6. pos = cc.p(,),
  7. -- ...
  8. parent = self,
  9. },
  10. {
  11. name = "loading_bar",
  12. creator = ui_parser.parsers.widget_loading_bar, -- 基于自己扩展的ui
  13. -- ...
  14. parent = "bg_img", -- 节点内部相互添加也可以支持
  15. },
  16.  
  17. onfinsh = function(components)
  18. components["loading_bar"]:set_percent(), -- 初始化以后的操作
  19. end,
  20. }
  21.  
  22. local components = ui_parser.parse(component_cfg)

看上面我提供的实例代码,我期望的是创建一个ui模块,只需要按照lua table的方式一样配置就好.这样有一个好处,可以复用很多其他模块的代码.需要的其他功能我都在注释中写出来了.接下来继续分析一下需求,我们可能会有自己的ui封装,所以得提供一个基类,当然这是仿照oop的说法,所以我就提供了一个ui_widget文件用来实现这个功能:

  1. --小岩<757011285@qq.com>
  2. --2015-5-30 13:25
  3. local ui_widget = {}
  4.  
  5. function ui_widget:ctor(cfg)
  6. self.type_ = "ui_widget"
  7. self.cfg_ = cfg
  8. self:init_widget()
  9. end
  10.  
  11. function ui_widget:init_widget()
  12. error(self.__cname .. ":init_widget() method not implemented!")
  13. end
  14.  
  15. function ui_widget:get_root_node()
  16. error(self.__cname .. ":get_root_node() method not implemented!")
  17. end
  18.  
  19. function ui_widget:add_to_node(parent)
  20. if parent.type_ and parent.type_ == "ui_widget" then
  21. parent:get_root_node():addChild(self:get_root_node())
  22. else
  23. parent:addChild(self:get_root_node())
  24. end
  25. end
  26.  
  27. function ui_widget:add_child(child)
  28. if child.type_ and child.type_ = "ui_widget" then
  29. self:get_root_node():addChild(child:get_root_node())
  30. else
  31. self:get_root_node():addChild(child)
  32. end
  33. end
  34.  
  35. function ui_widget:set_ap(ap)
  36. self:get_root_node():setAnchorPoint(ap)
  37. end
  38.  
  39. function ui_widget:get_ap()
  40. return self:get_root_node():getAnchorPoint()
  41. end
  42.  
  43. function ui_widget:set_pos(pos)
  44. self:get_root_node():setPosition(pos)
  45. end
  46.  
  47. function ui_widget:get_pos()
  48. local posx, posy = self:get_root_node():getPosition()
  49. return cc.p(posx, posy)
  50. end
  51.  
  52. return ui_widget

好了,基本的东西已经提供好了,下面就去处理.首先是处理creator,因为需求中明确的表示,第一我们要兼容cocos ui创建的api,另外我们还会有自己的ui封装.所以我将这些节点的创建方式都放在一起,方便管理.所以就有了ui_creator文件:

  1. --小岩<757011285@qq.com>
  2. --2015-5-30 12:45
  3. local ui_creator = {}
  4.  
  5. ui_creator.creators_ =
  6. {
  7. ccsprite =
  8. {
  9. function(...)
  10. return cc.Sprite:create(...)
  11. end,
  12. function(...)
  13. return cc.Sprite:createWithSpriteFrameName(...)
  14. end
  15. },
  16. }
  17.  
  18. function ui_creator.new_ccsprite(mode, ...)
  19. return ui_creator.creators_.ccsprite[mode](...)
  20. end
  21.  
  22. return ui_creator

可能很多人会奇怪这里面的mode参数是做什么用的,这个是用作资源管理用的,在开发初期的时候我们不会对资源进行合并处理,大部分都是散乱的放在各个文件夹中,而后期的时候我们会对资源进行合并.例如打包成为png大图等.通过浏览cocos2dx源码可以知道,cocos2dx的资源管理方式分为local和cache,所以我就提供了两种不同的创建接口.但是又消除了api的不同,对于使用者而言,仅仅是指定一下mode参数而已.对于api消除不同性接下来还要说.看ui_widget可以知道我提供的api和cocos2dx提供的api不同.所以我要消除这部分的不同性.不过要先来看一下ui_parser的实现:

  1. --小岩<757011285@qq.com>
  2. --2015-5-30 12:45
  3. local ui_creator = require "fw.ui.ui_creator"
  4. local checker = require "fw.ui.check"
  5. local api_adapter= require "fw.ui.api_adapter"
  6.  
  7. local ui_parser = {}
  8.  
  9. -- 解析ui控件函数
  10. ui_parser.parsers =
  11. {
  12. img = function(cfg)
  13. checker(cfg.mode, "number", string.format("ui_parser.parsers_.img() -- wrong mode %d", tonumber(cfg.mode)))
  14. checker(cfg.img, "string", string.format("ui_parser.parsers_.img() -- wrong img %s", tostring(cfg.img)))
  15. local img_ins = ui_creator.new_ccsprite(cfg.mode, cfg.img)
  16. api_adapter.adapter_cc(img_ins)
  17. return img_ins
  18. end,
  19. }
  20.  
  21. local function _strict_loop_with_given_list(list, callback)
  22. for index, item in ipairs(list) do
  23. callback(index, item)
  24. end
  25. end
  26.  
  27. --解析ui,生成ui控件
  28. function ui_parser.parse(component_cfg)
  29. local ui_components_ = {}
  30. local ui_cbs_ = {}
  31. local unspports_ = {}
  32.  
  33. --解析callback
  34. local function parse_cb(ui_cb)
  35. table.insert(ui_cbs_, ui_cb)
  36. end
  37.  
  38. --解析ui控件
  39. local function parse_ui(ui_cfg)
  40. checker(ui_cfg.name, "string", string.format("ui_parser.parse.parse_ui() -- cfg name error!"))
  41. checker(ui_cfg.creator, "function", string.format("ui_parser.parse.parse_ui() -- cfg creator error!"))
  42.  
  43. --处理节点相互添加
  44. if ui_cfg.parent and type(ui_cfg.parent) == "string" then
  45. parse_cb(function(ui_components)
  46. local ui_component = ui_components[ui_cfg.name]
  47. local ui_component_type = ui_component.type_
  48. local ui_component_parent = ui_componens[ui_cfg.parent]
  49. local ui_component_parent_type = ui_component_parent.type_
  50. if ui_component_type and ui_component_type == "ui_widget" then
  51. ui_component:add_to_node(ui_component_parent)
  52. else
  53. if ui_component_parent_type and ui_component_parent_type == "ui_widget" then
  54. ui_component_parent:add_child(ui_component)
  55. end
  56. end
  57. end)
  58. end
  59.  
  60. ui_components_[ui_cfg.name] = ui_cfg.creator(ui_cfg)
  61. end
  62.  
  63. --保存unspport
  64. local function parse_unspports(unspport)
  65. table.insert(unspports_, unspport)
  66. end
  67.  
  68. _strict_loop_with_given_list(component_cfg, function(_, item)
  69. local item_type = type(item)
  70. if item_type == "table" then
  71. parse_ui(item)
  72. elseif item_type == "function" then
  73. parse_cb(item)
  74. else
  75. parse_unspports(item)
  76. end
  77. end)
  78.  
  79. _strict_loop_with_given_list(ui_cbs_, function(_, cb)
  80. cb(ui_components_)
  81. end)
  82.  
  83. ui_components_["unspport"] = unspports_
  84.  
  85. return ui_components_
  86. end
  87.  
  88. return ui_parser

其中我做了很多的事情,例如很多时候我们在做UI布局的时候需要做相对适配,所以坐标不好指定,所以我在解析compoennt_cfg的时候认为如果提供的节点是function类型的话,那么就等所有的节点都解析完了之后统一操作,这样就可以处理相对布局的东西了.另外cocos2dx node派生的节点,和我们事先的ui_widget派生的节点如何相互添加的操作也做了.好了,创建的节点我在ui_parser.parsers中封装了,下面我们就来处理api的不同性,并消除掉。

  1. --小岩<757011285@qq.com>
  2. --2015-5-30 14:17
  3. local api_adapter = {}
  4.  
  5. api_adapter.cc =
  6. {
  7. parent = function(node, parent)
  8. if type(parent) ~= "string" then
  9. if parent.type_ and parent.type_ == "ui_widget" then
  10. parent:add_child(node)
  11. else
  12. parent:addChild(node)
  13. end
  14. end
  15. end,
  16. pos = function(node, pos)
  17. node:setPosition(pos)
  18. end,
  19. ap = function(node, ap)
  20. node:setAnchorPoint(ap)
  21. end,
  22. show = function(node, show)
  23. node:setVisible(show)
  24. end,
  25. content_size = function(node, content_size)
  26. node:setContentSize(content_size)
  27. end,
  28. rotation = function(node, rotation)
  29. node:setRotation(rotation)
  30. end,
  31. scale = function(node, scale)
  32. node:setScale(scale)
  33. end,
  34.  
  35. scalex = function(node, scalex)
  36. node:setScaleX(scalex)
  37. end,
  38. scaley = function(node, scaley)
  39. node:setScaleY(scaley)
  40. end,
  41. rsx = function(node, rsx)
  42. node:setRotationSkewX(rsx)
  43. end,
  44. rsy = function(node, rsy)
  45. node:setRotationSkewY(rsy)
  46. end,
  47. }
  48.  
  49. api_adapter.widget =
  50. {
  51. parent = function(node, parent)
  52. if type(parent) ~= "string" then
  53. node:add_to_parent(parent)
  54. end
  55. end,
  56. pos = function(node, pos)
  57. node:set_pos(pos)
  58. end,
  59. ap = function(node, ap)
  60. node:set_ap(ap)
  61. end,
  62. show = function(node, show)
  63. node:get_root_node():setVisible(show)
  64. end,
  65. content_size = function(node, content_size)
  66. node:set_content_size(content_size)
  67. end,
  68. }
  69.  
  70. function api_adapter.adapter_cc(node, cfg)
  71. if cfg.parent then api_adapter.cc.parent(node, cfg.parent) end
  72. if cfg.pos then api_adapter.cc.pos(node, cfg.pos) end
  73. if cfg.ap then api_adapter.cc.ap(node, cfg.ap) end
  74. if cfg.show or type(cfg.show) == "boolean" then api_adapter.cc.show(node, cfg.show) end
  75. if cfg.content_size then api_adapter.cc.content_size(node, cfg.content_size) end
  76. if cfg.rotation then api_adapter.cc.rotation(node, cfg.rotation) end
  77. if cfg.scale then api_adapter.cc.scale(node, cfg.scale) end
  78. if cfg.scalex then api_adapter.cc.scalex(node, cfg.scalex) end
  79. if cfg.scaley then api_adapter.cc.scaley(node, cfg.scaley) end
  80. if cfg.rsx then api_adapter.cc.rsx(node, cfg.rsx) end
  81. if cfg.rsy then api_adapter.cc.rsy(node, cfg.rsy) end
  82. end
  83.  
  84. function api_adapter.adapter_widget(node, cfg)
  85. if cfg.parent then api_adapter.widget.parent(node, cfg.parent) end
  86. if cfg.pos then api_adapter.widget.pos(node, cfg.pos) end
  87. if cfg.ap then api_adapter.widget.ap(node, cfg.ap) end
  88. if cfg.show then api_adapter.widget.show(node, cfg.show) end
  89. if cfg.content_size then api_adapter.widget.content_size(node, cfg.content_size) end
  90. end
  91.  
  92. return api_adapter

我按部就班的提供了上面的工具. 这样就消除了api不同性. 如果添加了我们自己的ui封装,响应的在creator中添加创建函数,在parser中添加响应的解析,在api_adaper中添加相应的函数.这样就做到了我前面预期的事情了。

在这里要说一句抱歉,因为我并没有添加测试用例的功能,所以不能给大家提供直观的测试表现,很快我就会考虑添加的.这篇文章就到这里啦.希望对大家有用。

`cocos2dx 非完整` UI解析模块的更多相关文章

  1. `cocos2dx非完整` 日志模块 增量更新

    在上一篇文章中,说到了"流程"的由来,以及我对流程的使用. 这一片就是对流程的应用.前一篇文章中说到了三条流程 check_log_measure, check_env_measu ...

  2. `cocos2dx非完整` 开始自己的FW模块

    上一篇的文章中说到了一些个人习惯的东西以及一些简单的项目配置,这一篇文章我们来进一步完善一些东西.首先,打开编译以后的客户端执行,会看到一大堆的fileutils加载luac文件的提示,在终端显示一大 ...

  3. `cocos2dx非完整` 添加xxtea加密模块

    在上一篇文章中,我已经开始着手写自己的模块,也就是fw部分.其中上一篇文章中完成的是lua部分的配置解析部分,涉及一点点平台方面的封装.这一片文章我来说明一下我是如何处理cocos2dx资源加密的.首 ...

  4. `cocos2dx非完整`开篇

    相信每个人都有一些自己的项目开发习惯,在·开篇·中我主要是会提到一些项目的配置问题.无论做一款什么样的手游项目,我们总是会从需求的角度出发去选择开发引擎,开发工具等一些列的工具去完善我们的开发环境.当 ...

  5. `cocos2dx非完整` 游戏架构缩影 添加启动流程

    这期的话题可能不是很好, 我没有想到很好的词句去更好的表达. 我一直都是很固执的认为, 同一类型的游戏,在开发做的前期工作上面其实都是可以复用的,也就是大同小异的.从游戏启动,启动日志,启动检查,检查 ...

  6. `fw服务端非完整` 工程开发初期的工作

    前面写到了一些关于cocos2dx在开发中的一些模块以及一些解决方法,那些都属于本人的个人简介和个人倾向的解决方案.最近这几天我完善了一下ui解析的部分,当然也只是抽出一点点时间去做的这件事情.我添加 ...

  7. 第三十六课:如何书写一个完整的ajax模块

    本课主要教大家如何书写一个完整的ajax模块,讲解的代码主要跟ajax有关,而jQuery的ajax模块添加了Deferred异步编程的机制,因此对ajax的理解难度增大,还是忽略掉.但是我要讲解的代 ...

  8. 浩哥解析MyBatis源码(十一)——Parsing解析模块之通用标记解析器(GenericTokenParser)与标记处理器(TokenHandler)

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6724223.html 1.回顾 上面的几篇解析了类型模块,在MyBatis中类型模块包含的 ...

  9. python命令行参数解析模块argparse和docopt

    http://blog.csdn.net/pipisorry/article/details/53046471 还有其他两个模块实现这一功能,getopt(等同于C语言中的getopt())和弃用的o ...

随机推荐

  1. Atitit.如何避免公司破产倒闭的业务魔咒

    Atitit.如何避免公司破产倒闭的业务魔咒 1. 大型公司的衰落或者倒闭破产案例1 1.1. 摩托罗拉1 1.2. 诺基亚2 1.3. sun2 2. 为什么他们会倒闭?? 常见的一些倒闭元素2 2 ...

  2. paip.java 多线程参数以及返回值Future FutureTask 的使用.

    paip.java 多线程参数以及返回值Future FutureTask 的使用. 在并发编程时,一般使用runnable,然后扔给线程池完事,这种情况下不需要线程的结果. 所以run的返回值是vo ...

  3. atitit.文件上传带进度条的实现原理and组件选型and最佳实践总结O7

    atitit.文件上传带进度条的实现原理and组件选型and最佳实践总结O7 1. 实现原理 1 2. 大的文件上传原理::使用applet 1 3. 新的bp 2 1. 性能提升---分割小文件上传 ...

  4. wicket基础应用(2)--wicket表单控件的使用

    该文可以转载,但转载必须注明作者,出处: 作者:lhx1026 出处:http://lhx1026.iteye.com/ 这一章介绍wicket表单控件的简单应用 1.Label控件 这个应该说是最常 ...

  5. 用自己的算法实现startsWith和endsWith功能

    package hanqi; import java.util.Random; import java.util.Scanner; public class zuoye { public static ...

  6. 【由VerySky原创】CX51、CX52 ——数据表

    今天通过DEBUG  CX52 得出所保存的数据表是ECMCA:

  7. Entity Framework一对多关系添加数据的两种方式

    当使用Entity Framework添加一对多关系数据的时候,通常先添加一的数据,然后再添加多的数据.类似这样: //添加一的数据 var category = new Category{Name= ...

  8. 报错:ASP.NET Web API中找不到与请求匹配的HTTP资源

    当发出GET请求: GET http://localhost:54176/api/Products 报如下错: {  "message": "找不到与请求 URI“htt ...

  9. Simple-RTMP-Server 服务器搭建

    Simple-RTMP-Server 服务器搭建 1. 服务器镜像获取 github源码地址 git clone https://github.com/winlinvip/simple-rtmp-se ...

  10. 在Servlet中出现一个输出中文乱码的问题(已经解)。

    在Servlet中出现一个输出中文乱码的问题,已经解. @Override public void doPost(HttpServletRequest reqeust, HttpServletResp ...