基于Cocos2dx引擎UI扩展引擎包[cocos2d-x-3c]
【前言点评】 此篇主要作者:jason-lee-lijunlin 基于Cocos2d-x引擎进行封装的UI框架的扩展包。 此文章Himi已经仔细看过,总体来说是篇很好的文章,是给使用-x引擎的童鞋们的福利~。真的非常感谢作者的分享,近两年 Himi看到了越来越多的开发者们将自己的作品与劳动成果无私放在网上供给童鞋们交流与学习,真的是感觉天国越来越好了,有没有~ 哈哈。 (比多年前才开始进行J2me开发时的Himi来说,真的现在的童鞋们好幸福!!!) 【最后希望更多的开发者们参与无私分享,一起交流学习的行列中,有好的文章也欢迎联系Himi进行投稿与推荐!】废话不多说,各位看正文吧~ 博客原文地址:http://www.52php.cn/article/p-sovukrrg-bar.html 版本管理及下载列表 Download Github Cocos2d-x-3c 设计之路 作者:李俊霖 jason.lee.c@foxmail.com QQ群:261286285 此篇文章主要介绍以下几个框架 (Cocos2d-x-3c) Cocos2d-x
出于对Cocos2d-x引擎的热爱,作者也需要来稍加点评,在优点上不想多做评论,正是因为它有着诸多优点才能让我们喜欢并使用它,以下主要来阐述一下作者个人对Cocos2d-x引擎缺点的看法与观点。作者属于爱较真的人,所阐述的缺点可能在各位读者眼中不能作为缺点而存在,确实Cocos2d-x其实算是一个比较完美的跨平台引擎了。由于3.0版本的发布,部分缺点已经得到了改善,那就先来说说3.0版本的问题。首先作者认为3.0版本定位不明确,通俗讲就是目标不明确,3.0版本在发布时明确表示出可以脱离cocos2d-iphone的老路,沿用新的技术 新的架构,不好的模块可以直接舍去或者重构,可是在3.0版本里却随处可见2.0版本的老设计,CCMenu体系,CCArray(被Vector所替换),CCDictionary(被Map所替换),2.0版本的触摸优先级等等,可否曾想过 跨平台环境和工程搭建基本属于要哭的节奏,如果不是作者利用业余的大量时间来研究Cocos2d-x,那么作者所在公司的项目进度还处在攀岩的速度,这也是为什么作者说它起步难的原因,虽然python模板解决了各个平台工程的生成问题,但是对于一个即将使用Cocos2d-x来开发游戏的公司和即将部署多平台项目的编程人员来讲,它们需要分别了解学习ios、android、win32 (暂时不考虑其他平台)的平台项目,其中属Android最为复杂,要部署一个Android可调试项目对于一个没有Android相关工作经验的人来说简直是噩梦。当然了,在开发跨平台项目的过程中了解这些是无可厚非的,但是我们是否可以想办法让这个过程变得更加傻瓜化,当需要了解具体内容时,再进一步跟进深入,这样会招来更多人的喜欢。 使设计更模块化(在3.0上稍有体现),使引擎本身更标准化,作者觉得引擎团队是否可以在对于引擎本身的设计上或者需求采纳上更为严谨,这点可以向c++标准委员会及标准库看齐这样可以使开发者置身于非常标准环境下去工作(这句话理解起来比较抽象,可以自行理解)。 读者拿到引擎首先就注意到的是,引擎是否能满足需求,那就取决于游戏本身的类型等等,那引擎到底能不能满足需求,作者只能这样答复,暂时还不能完全满足到此为止,每家公司或者每个游戏架构制定者会有不同的做法来实现满足自己的需求,我们可以称为“二次封装”,这种所谓的“二次封装”可能分为很多不同的形式,千变万化。有的基于Cocos2d-x的一个稳定版本开了一个单独的分支,从此维护自己的版本,有的则加入各种各样的逻辑架构、资源管理架构等等。作者只能感叹民间高手多啊。那么作者为引擎团队提供的建议还是”标准化”的概念,那么问题就很明显了,目前的标准化还不够标准,还不够强大。 滤镜系统在Cocos2d-x里不适用,想让整个场景执行去色操作?想让整个场景执行高斯模糊?那么抱歉,目前引擎还没有实现,这是一个大问题,作者已经将此需求向引擎团队提交,目前能实现滤镜的方式之一就是继承CCSprite,overriding the draw function,实现自己的滤镜效果,那么缺点也随之凸显,一个精灵并不能执行多个滤镜效果,并且精灵与这个效果强耦合,这并不是我们所预期的效果。 基础回顾 在介绍Cocos2d-x-3c前,作者需要再对Cocosd2-x的基础设计上做一个简单明了的介绍,目的是让读者对Cocos2d-x本身有更深层次的思考与更广泛的了解 主循环: if(con) } return 0; mainLoop里做了这样几件事情,明确地按顺序来说明 基础单位 渲染树 场景栈 注:到这里,作者有个不得不说的秘密,其实引擎并非只提供了一颗渲染树(CCScene渲染树),CCNotificationNode听说过吗,没错,它就是第二颗渲染树,但它只是一颗正在茁壮成长的小树苗,你可以发挥想象把它构建为一颗参天大树(作者项目中的飘字,提示框,加载进度等等都是用它来实现的)。 侵入式的对象引用计数管理 严格遵循一次new或retain,一次release或autorelease,对于一个对象来讲new或retain必须与release或autorelease成对出现 以下列出某些常用接口会对内容增加引用计数的操作,假设this为CCNode并且class A : public CCNode 也可以这样写 通常前两句代码会被包装为一个static create函数,例如我们经常看到的CCLayer::create(),那么就会有以下的写法 其他会改变引用计数的接口(列举其一) 注:当需要释放对象时也可以通过delete运算符来完成操作,但一定要确保已经没有其它模块在引用这个对象了,否则可能会出现野指针 触摸事件 所有class CCTouchDelegate的derived class都有接收触摸事件的能力。通过addTargetDelegate(delegate,priority,true)与removeDelegate(delegate)来注册或取消注册事件接收。这两个函数通常成对出现在onEnter与onExit中,切记在onExit时需要移除触摸事件的接收,除非你有特殊需求。引擎对于触摸事件的分发属于”优先级队列询问模式”,从最优先的delegate向下依次询问,delegate则通过ccTouchBegan的返回值来向引擎表达自己是否关心后续事件(moved ended cancelled)返回true表示需要关心并处理后续事件,那么之后的触摸事件只会分发给这个delegate,这也就是大家所说的事件吞噬的概念。返回false则表示并不关心后续事件,则引擎继续向下询问。 注:在触摸事件的处理函数中改变渲染树的结构(addChild,removeChild)是安全的,注册与销毁定时器是安全的。 定时器Scheduler 注:在注册的定时器执行函数里改变渲染树的结构是安全的(addChild,removeChild),销毁或注册其他定时器是安全的。 动作Action 文件管理方面 注:CCFileUtils::getFileData()返回的const char* data必须手动释放,例如delete[] data,如果读者有兴趣,可以到Cocos2d-x2.2左右版本的库代码里去搜寻这个函数,你会惊讶的发现有很多地方都遗漏了delete[],从而导致内存泄露,至少作者在Cocostudio的库里搜出来好几处。所以目前的解决方案只有用脑袋记着要释放掉。 GUI系统 网络库 线程 Cocos2d-x-3c CocosBase 那么接下来作者先介绍第一个框架 Cocos2d-x-3c CocosBase CocosBase提供消息广播机制,资源异步预加载,模态对话框,场景缓存,双场景渲染树,还有比引擎更高级的场景管理方案。 1.双场景渲染树以及模态对话框 作者需要来详细的解释什么是双场景渲染树,以及为什么需要这样的结构。我们对引擎渲染树的概念来自class CCScene,它是作为引擎唯一渲染树的根 现在换个思路,我们可以把场景抽象为两种类型,分别是基础场景与UI场景,CocosBase为它们提供了一个共同的抽象类,class CCSceneExtension 基础场景与引擎的CCScene概念基本相同,通过一个RunningSceneStack(场景运行栈)来管理,CCSceneManager提供了几个与CCDirector功能相似的接口来管理基础场景的切换。 UI场景则是在基础场景之上的一个场景列表(并不是以栈来管理),CCSceneManager也同样提供接口来管理UI场景 注:以上的extra可以是CCObject的任何一个derived class对象,这个参数是场景切换时所附加的参数,可以被下一个出现的场景所获取。CocosBase提供了一个捆绑数据的类名为CCBundle,它通常是用来作为场景切换时需要传递的参数。 那么再回到上例所说明的情况下,我们可以把游戏的主场景作为一个基础场景,背包界面则可以作为一个UI场景,只通过两个接口则可以控制背包界面的打开与关闭,并且做到了结构上的解偶。那么读者您可能还比较关心的是ZOrder与Touch Priority。不用担心,CCSceneManager内部会根据打开UI场景的顺序来控制显示(同时打开多个UI场景的情况下)。我们需要重点讨论的是Touch Priority以及如何实现一个模态场景或者说是模态对话框(看你自己的理解方式)。当背包界面被打开时,我们通常希望由它来处理触摸事件,并不希望这些事件被传递到到主场景或者是下层场景(可能背包界面之下还有其他UI场景),那么CCSceneManager提供这样的解决方案,使用名为getTouchPriority的函数来获得一个值,这个值代表触摸优先级,它是由内部为你计算的一个优先权最高的动态优先级,计算方式很简单,每次调用getTouchPriority函数时,返回的值都比上次小1(值越小越优先,别忘了这一点),有了这个法宝,那我们可以在一个时机内把此场景和场景内具有优先级特征的元素(例如CCMenu,作者已经在上文提出将它抛弃,在这里只是用它来做个示范)的优先级置为这个值,关于这个时机,我们可以选择场景的onSceneEnter回调函数(具体在下文详细介绍),这个函数您可先暂时理解为当场景出现或再次出现时被调用。通过以上若干步骤,就可以保证呈现在最上面的场景是最优先接触摸收事件的。那么剩最后一个问题就是模态与非模态,很简单,使本场景的ccTouchBegan函数永远返回true则可以实现模态,反之则继续向下传递。 如果读者您觉得以上描述非常复杂难以被理解,那么来换一种思路吧,这下作者来介绍另一种非常简单直白的优先级与模态处理方式,那就是与CocosWidget结合使用,修改CCBaseMacros.h的USING_COCOSWIDGET宏定义为1并包含cocos-widget.h头文件,使CCSceneExtension继承于CWidgetWindow,通过调用setModalable(bool)函数来设置此场景是否为模态。那么触摸优先级呢?读者您可以完全抛弃这个概念了。 注:如果读者您决定使用与CocosWidget结合的方式来管理,那么请您一定要抛弃CCMenu或者CCTableView这样不健全的组件,并且也不能与CocoStuido和CocosBuilder结合使用。全部使用CocosWidget内部提供的组件,并且最好以CLayout代替CCLayer。具体关于CocosWidget下文将详细讲解。 2.场景对象从哪来 场景对象从哪来?这是一个值得探讨的问题,也是考验您对于场景设计概念上最基础的问题之一。CCSceneManager提供了专门为您管理场景实例的功能。对于class CCSceneExtension的说明是,您最好不要让自己以任何形式实例化它,例如create、new等(因为这完全是由内部来做的事情)。开发者需要做的事情只是定义一个注册表,用来告知CCSceneManager目前你的游戏中包含了哪些场景,CocosBase提供了一个全局宏,名为REGISTER_SCENE_CLASS(scene),使用它来注册一个场景,其中参数scene为场景的类名。一般我们将这样的注册代码统一写在AppDelegate.cpp的初始化函数中(具体请参考示例代码),CCSceneManager提供两个函数,或使用两个宏来获取或实例化一个场景对象,这两个宏分别为LoadScene(scene class name) 通过场景类名字符串来实例化这个场景,但是否真的实例化取决于这个场景是否已经存在于场景缓存池中,若存在则直接返回。SeekScene(scene class name) 通过场景类名字符串来搜索这个场景,如果这个场景出现在场景缓存池、或者RunningSceneStack或UIScenesPool中。如果搜索未果,返回NULL。不知读者您是否已经理解上文的含义。通过这两个宏,我们则可以在程序执行时的任何角落(注册之后)里,通过类名字符串就可以达到实例化或搜索场景的功能了。这种方法不仅方便,并且解决了场景与场景实现的源文件之间复杂的头文件包含关系,这些头文件应该统一被appDelegate.cpp所包含(用来实现注册)。 注:LoadScene会根据场景是否设置过缓存属性来决定,实例化后是否把它加入场景缓存池。 3.场景生命周期以及资源预加载 介绍class CCSceneExtension的几个virtual callback function。 在onLoadResources里可以做加载资源相关的事情,class CCSceneExtension提供了两个函数来描述加载一个图像资源, 当某个场景正在切换但有异步加载的行为时,CCSceneManager会等待这个场景,直到资源全部加载完成后,才开始执行切换操作。 注:在同一帧之内使用两次或以上replaceScene与popScene有可能会出现未定义行为,所以尽可能保证在同一帧内,有且只有一次切换操作。 4.场景缓存 class CCSceneExtension提供setCachable(bool)函数来让您在场景初始化时描述此场景是否缓存。CCSceneManager提供下列接口来管理缓存场景 5.消息广播 如果您说看到这个名词会想起Windows消息循环,那这里也有几分相似之处。CocosBase里所谓的消息广播概念,即是由CCMsgManager提供的PostMessage函数来发出广播消息,所有class CCMsgDelegate的derived class都具备接收广播消息的权利,通过overriding the virtual onMessage回调函数来实现。class CCSceneExtension是多继承于class CCMsgDelegate的,所以场景对象都是具备有接收广播消息的功能,但条件是这个场景处于Running状态(具体请参考源码)。但有时我们并不想让消息散播出去,而是让指定的消息代理来接收这个消息,那么使用PostMessage的重载函数,指定第一个参数为目标消息代理即可实现。 读者还需要额外的了解两个函数,这两个函数用来注册或取消注册消息代理。CCMsgManager提供: 注:作者希望与SeekScene宏结合来实现把消息发到指定场景身上。 6.场景切换特效 基础场景的切换(push,replace)操作可以通过切换特效来实现,CCSceneExTransition.h中定义了所有场景切换的方式(与引擎本身提供的完全一致),具体使用方式请参考示例源码。让您遗憾的是,作者未能提供UI场景的切换特效。 Cocos2d-x-3c CocosNet 在长连接游戏的开发中,CocosNet有着出色的性能,良好的设计,以及易用性。 1.良好的设计及易用性 CocosNet内部采用select无阻塞通信模型,其带来的好处就是不用开启另一个线程来专门维护Socket,所有读写操作全部在主线程内进行,更准确的说就是在mainLoop内的某个Scheduler对象处理函数内进行。一个CCNetDelegate对象用来描述一个连接,其内部维护了一个发送队列以及一个接收缓冲区,在每帧内会把发送队列里的数据按顺序发出去,并且读取所有已经在缓冲区的数据。那么读者担心的可能是在接收大数据及发送大数据的时候,会不会由于持续时间长而导致卡帧。CocosNet提供了两种数据发送及接收的方式,一种是所有收发操作全部在一帧内完成(大数据会导致卡帧),另一种是收发操作在每帧内只进行一次,具体内容参见HANDLE_ON_SINGLE_FRAME宏定义。 2.出色的性能 CocosNet内提供C数组实现的超高效缓冲容器CCBuffer,并几乎包含所有内置数据类型的流操作,具体请参考CCBuffer.h内声明的函数, 3.易用性 开发者使用CocosNet时,需要继承自CCNetDelegate来管理一个独立的连接,连接状态的生命周期则由几个virtual function来管理。 USING_PACKAGE_HEAD_LENGTH宏用来描述整个收发过程,是否使用包头长度的方式,作者解释一下这个基础知识,包头长度的解释是在整个发送或接收的数据包头部,有一个4个字节的int型数字(是否是4字节由操作系统决定),这个数字代表整个数据包的包长(不包括这个表示长度的数字)。如果USING_PACKAGE_HEAD_LENGTH被定义为1,则在发送数据包时,由内部自动为发送的数据包头部加上一个长度数字,而读取数据包时,则会先读取长度,再根据长度读取整个包的内容,具体请参考CCNetDelegate.cpp源码。 Cocos2d-x-3c CocosWidget CocosWidget是基于Cocos2d-x移动跨平台游戏引擎的一套免费开源、功能强大、高效率、简封装的GUI库。 CocosWidget目前的控件数量已经达到了26个,远远超过其它GUI解决方案 Widget 基础控件 在3c版本中,CocosWidget提供了大家瞩目以待的富文本控件、可伸展滑动容器控件等。本次升级,CocosWidget改进了整体结构及性能,CPanel重命名为CLayout,CWidgetLayout重命名为CWidgetWindow。 1:CocosWidget是目前唯一完全支持Lua Binding与富文本的GUI库。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |