本站文章均为原创,转载务必在明显处注明:(作者新浪微博:)
转载自 原文链接:
本博客最新动态!及时将最新博文通知您!
【前言点评】
此篇主要作者: 基于Cocos2d-x引擎进行封装的UI框架的扩展包。
此文章Himi已经仔细看过,总体来说是篇很好的文章,是给使用-x引擎的童鞋们的福利~。真的非常感谢作者的分享,近两年 Himi看到了越来越多的开发者们将自己的作品与劳动成果无私放在网上供给童鞋们交流与学习,真的是感觉天国越来越好了,有没有~ 哈哈。 (比多年前才开始进行J2me开发时的Himi来说,真的现在的童鞋们好幸福!!!)
【最后希望更多的开发者们参与无私分享,一起交流学习的行列中,
有好的文章也欢迎联系Himi进行投稿与推荐!】
废话不多说,各位看正文吧~ 博客原文地址:
版本管理及下载列表
Download
CocosBase-2.2beta-3c.zip
CocosBase-2.2.1beta-3c.zip – 修复了pushScene附带参数传递失败的BUG
CocosNet-2.2beta-3c.zip
CocosNet-2.2.1beta-3c.zip – 修复了组合收包时只处理1次的严重BUG
CocosWidget-2.2beta-3c.zip
Github
CocosBase
CocosNet
CocosWidget
Google.code
CocosBase
CocosNet
CocosWidget
Cocos2d-x-3c 设计之路
作者:李俊霖 jason.lee.c@foxmail.com
QQ群:261286285
此篇文章主要介绍以下几个框架 (Cocos2d-x-3c)
CocosBase — 场景管理及逻辑设计上的解决方案
CocosNet – TCP长连接解决方案
CocosWidget – GUI系统的解决方案
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-x3.0阵营的开发者,在选择上如何定义?GUI体系包含CCMenu,CocosBuilder,CocosStudio,扩展库里的GUI组件,(不算作者开发的CocosWidget)这些该被开发者如何选择,如何使用呢?作者见过的已经不在少数的开发者选择组合使用CCMenu,CocosBuilder,CocosStudio,还有扩展库里的CCScrollView、CCTableView等,这样的做法是大错特错的,这些GUI系统从设计理论上来讲是完全不能被组合的。所以作者希望的是真正的重构与设计,而不是一味的兼容老版本。
跨平台环境和工程搭建基本属于要哭的节奏,如果不是作者利用业余的大量时间来研究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本身有更深层次的思考与更广泛的了解
主循环:
每个游戏都有一个主循环,而Cocos2d-x的主循环位于CCDirector.cpp里的mainLoop函数,大致可以理解为以下结构
int main()
{
while(1)
{
mainLoop();
if(con)
break;
}
return 0;
};
mainLoop里做了这样几件事情,明确地按顺序来说明
– 帧开始
1:计算上一帧循环到本帧循环的时间差
2:执行所有注册了scheduler的对象和函数
3:访问当前运行的scene和所有scene树上的节点(CCNode),我们可以称为”渲染树”
4:访问CCNotificationNode节点
5:显示屏幕左下角的信息(FPS等等)
6:交换缓冲区,把本帧所完成的所有渲染操作呈现到屏幕上
7:将CCAutoReleasePool池对象pop一次
– 帧结束
基础单位
CCObject 用于抽象所有类型,并实现侵入式对象引用计数管理
CCNode 渲染节点(用于渲染树)
CCSprite 元素单位(精灵)
渲染树
渲染树的起点为CCScene,通过getRunningScene就可以得到当前渲染树的根节点,所有CCNode的derived class都具备可渲染(drawable)的功能。
CCNode的virtual draw函数用来覆盖后实现自定义的渲染方式。
CCNode的virtual visit函数实现了调用所有子节点的visit函数,并根据zOrder决定自己和所有子节点virtual draw函数的调用顺序。
请参考CCNode.cpp源码,注:在virtual draw或者virtual visit函数中调用addChild或者removeChild并不安全。
场景栈
渲染树的根节点被称之为”场景”(CCScene),同时只能存在一个正在运行的场景,也就是RunningScene,并且RunningScene是RunningSceneStack(场景运行栈)栈顶的那个,栈顶的场景永远是呈现在用户界面上的那个,所以改变这个栈的结构,也就意味着将会改变呈现给用户的场景,于是就有了场景切换的概念。栈的原则是FILO(first in last out 先进后出),CCDirector导演模块提供了几个接口对场景栈的管理。
pushScene与runWithScene,场景入栈
popScene 场景出栈
replaceScene 替换栈顶的场景为某个场景
popToRootScene 场景连续出栈直至栈底
popSceneStackLevel 场景连续出栈到一个等级,1代表栈底,0代表全部出栈(退出游戏)
注:到这里,作者有个不得不说的秘密,其实引擎并非只提供了一颗渲染树(CCScene渲染树),CCNotificationNode听说过吗,没错,它就是第二颗渲染树,但它只是一颗正在茁壮成长的小树苗,你可以发挥想象把它构建为一颗参天大树(作者项目中的飘字,提示框,加载进度等等都是用它来实现的)。
侵入式的对象引用计数管理
严格遵循一次new或retain,一次release或autorelease,对于一个对象来讲new或retain必须与release或autorelease成对出现
CCObject的derived class都具有引用计数管理的特性,当引用计数被减至0时此对象立即释放,假设class A : public CCObject
A* a = new A(); –引用计数默认为1
a->retain(); –引用计数加1,现在为2
a->release(); –引用计数减1,现在为1
a->retain(); –引用计数加1,现在为2
a->autorelease(); –引用计数在本帧即将结束前减1,到那时为1
a->autorelease(); –引用计数在本帧即将结束前再次减1,到那时为0,并且释放 (再次调用autorelease)
以下列出某些常用接口会对内容增加引用计数的操作,假设this为CCNode并且class A : public CCNode
A* a = new A(); –引用计数默认为1
this->addChild(a); –引用计数被addChild内部所加1,现在为2(当this析构函数执行时,会把所有Children的引用计数减1)
a->release(); –引用计数减1,现在为1 (目的是把a的计数管理权完全交给this的这个Node)
也可以这样写
A* a = new A(); –引用计数默认为1
a->autorelease(); –引用计数在本帧即将结束前减1
this->addChild(a); –引用计数被addChild内部加1,现在为2
this->removeChild(a); –引用计数被removeChild内部减1,现在为1,因为autorelease,本帧结束前a的引用计数将会被减至0
通常前两句代码会被包装为一个static create函数,例如我们经常看到的CCLayer::create(),那么就会有以下的写法
A* a = A::create();
this->addChild(a); –当this被释放时也就意味着a的生命周期结束
其他会改变引用计数的接口(列举其一)
CCArray::addObject –对被添加到容器里的元素加1 (3.0版本中该容器对应为Vector)
CCArray::removeObject –对被从容器里移除的元素减1
CCArrry::~CCArray –容器析构时所有元素计数减1
注:当需要释放对象时也可以通过delete运算符来完成操作,但一定要确保已经没有其它模块在引用这个对象了,否则可能会出现野指针
触摸事件
2.0版本提供的触摸优先级这个概念对开发者有设计上的障碍,比较好的方案是3.0版本的 new event dispatch 与 CocosWidget中的事件分发模型
但为了知根知底,作者还是要列举一下常用的形式。
所有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
定时器可以是每帧都执行的,也可以是隔一段时间执行一次的,也可以是只执行一次的,通常游戏里会有部分逻辑被放置定时器中。class CCNode自带定时器功能,但只有当这个Node处于Running的状态时,定时器才会执行。全局定时器则需要用过CCDirector提供的接口来实现
注:在注册的定时器执行函数里改变渲染树的结构是安全的(addChild,removeChild),销毁或注册其他定时器是安全的。
动作Action
class CCNode提供了一个用于运行动作的runAction接口,class CCAction的所有derived class可以被表示为一个动作。动作基本可以分为:基础动作 basic action,容器动作 action container,调用动作 callable action。以下列举部分动作
基础动作:CCMoveBy CCMoveTo CCFadeTo CCFadeOut CCFadeIn CCRotateBy CCRotateTo CCJumpTo等等,这些动作用来描述改变渲染节点状态的一个过程,如坐标,透明度,旋转等等。
容器动作:CCSequence CCRepeat CCRepeatForever CCSpawn等等,容器动作用来包含若干容器动作或基础动作。
调用动作:CCCallFunc CCCallFuncO CCCallFuncN CCCallFunND等等,调用动作可以绑定一个CCObject derived class对象的函数指针。
文件管理方面
读取或打开文件的方式请使用CCFileUtils::getFileData(),如果读者你认为使用fopen或者stl里的fstream那你就大错特错了,作者所在公司的项目上因为这个吃了点亏。
注:CCFileUtils::getFileData()返回的const char* data必须手动释放,例如delete[] data,如果读者有兴趣,可以到Cocos2d-x2.2左右版本的库代码里去搜寻这个函数,你会惊讶的发现有很多地方都遗漏了delete[],从而导致内存泄露,至少作者在Cocostudio的库里搜出来好几处。所以目前的解决方案只有用脑袋记着要释放掉。
GUI系统
正如作者所述,在选择GUI系统时,千万不要选择组合多种GUI体系来开发,如果读者您不打算考虑CocosWidget的话,作者建议您选择CocoStudio。CCMenu体系基本就是一个神来之笔,感觉它的出现更像是一个为了满足部分需求的临时性解决方案,作者强烈建议您抛弃CCMenu,因为它根本不在考虑范围内CocosBuilder体系是早期Cocos2d-iphone上的一个扩展库,CocosBuilder原作者早已放弃继续维护CocosBuilder,但是目前依然有很多开发者更倾向于使用CocosBuilder从结构上来分析,CocosBuilder的事件分发模型完全是照搬引擎本身那套带有优先级的概念模型,控件与控件之间基本没有任何关系,完全通过优先级来管制。但事实并非如它所愿,控件与控件,或者控件与容器组件之间必定是要有连带关系的,很显然CocosBuilder里提供的组件是做不到这点的。如果这点都做不到,那就没法再接着讨论事件是如何分发、如何管理的。但是CocosBuilder是目前唯一在mac os x 上支持的Cocos2d-x的GUI编辑器,那这下就可以理解选择它的理由了。作为发开者的您,选择CocosBuilder是基本没有问题的,但是作者更推荐您选择CocosWidget或者CocoStudio。CocoStuido已经从贫民阶段提升为屌丝阶段,也相信在不久的将来会发展到高富帅阶段,如果在您不考虑还没有编辑器支持的CocosWidget,那么作者一定会推荐您选择CocoStudio。CocosWidget将会在下文介绍
网络库
引擎提供curl与CCHttpClient来实现Http协议的网络交互,长连接的一般解决方案是ODSocket或BSDSocket,不过作者强烈推荐您使用CocosNet提供的长连接解决方案,CocosNet将会在下文介绍
线程
Cocos2d-x可以帮助你实现跨平台多线程,头文件统一引入<pthread>,在win32下有一份模拟实现pthread的代码。请参考引擎源码。
Cocos2d-x-3c CocosBase
那么接下来作者先介绍第一个框架 Cocos2d-x-3c CocosBase
CocosBase提供消息广播机制,资源异步预加载,模态对话框,场景缓存,双场景渲染树,还有比引擎更高级的场景管理方案。
1.双场景渲染树以及模态对话框
作者需要来详细的解释什么是双场景渲染树,以及为什么需要这样的结构。我们对引擎渲染树的概念来自class CCScene,它是作为引擎唯一渲染树的根
(如果不加以封装或是说不考虑CCNotificationNode的情况下),那么我们所编写或实现的所有带有描述渲染对象的结构或模块都只能是存在于CCScene树上的。(引擎也只允许这么做,除非你改变引擎的实现)例如游戏界面,场景,对话框,购买框,提示等等这些很复杂而又多变的元素,但有时候这并不是我们所想要的结果,让我来举个常见的例子,首先我们实现了一个RPG游戏的主场景(带有描述玩家本身或其他玩家等等复杂事物的场景),接下来需要加入一个背包功能,但对于背包界面的需求是,并不覆盖整个屏幕,并且在打开时,还能看见背包界面下层的游戏主场景。您可能想到的第一种方式就是,背包界面作为一个Layer,使用addChild接口加入到主场景上。此种方案虽然直观通用,但从设计上来讲简直是糟糕,请允许作者来分析一下此种方式的缺点,当使用addChild把背包界面直接加入到主场景上时,背包界面便于主场景产生了一个上下级节点的关系,上级节点有责任来维护下级节点的状态等(例如关闭打开背包界面或改变相应状态),当这种二级界面数量逐渐增加时,主场景上便产生了大量雷同的代码,用来维护不同的二级界面,而且还只是假设这种关系只有一层。当这种关系发生在多场景并且发生多级界面的情况下,那么这种管理方案就显的太吃力了。更别说ZOrder与触摸优先级的控制,因为那简直就是一场噩梦。
现在换个思路,我们可以把场景抽象为两种类型,分别是基础场景与UI场景,CocosBase为它们提供了一个共同的抽象类,class CCSceneExtension
基础场景与引擎的CCScene概念基本相同,通过一个RunningSceneStack(场景运行栈)来管理,CCSceneManager提供了几个与CCDirector功能相似的接口来管理基础场景的切换。
pushScene(scene, extra),runWithScene(scene extra) 场景入栈
replaceScene(scene, extra) 替换栈顶场景
popScene(extra) 使栈顶场景出栈
popToRootScene(extra) 连续出栈直至栈底
popSceneStackLevel(level, extra) 连续出栈至一个等级,0为退出游戏,1为栈底。
UI场景则是在基础场景之上的一个场景列表(并不是以栈来管理),CCSceneManager也同样提供接口来管理UI场景
runUIScene(scene, extra) 打开一个UI场景
popUIScene(scene) 关闭一个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 当场景初次被实例化时会调用,用于提前加载各种资源
onLoadResourcesCompleted 当资源加载完成后调用(在这里可以做的事情比如说,关闭正在加载的等待框等)
onLoadScene 当加载场景时调用,用于初始化所有元素
onEnterScene 当场景出现时调用
onExitScene 当场景离开时调用
在onLoadResources里可以做加载资源相关的事情,class CCSceneExtension提供了两个函数来描述加载一个图像资源,
addImage(file) 同步方式加载
addImageAsync(file) 异步方式加载
当某个场景正在切换但有异步加载的行为时,CCSceneManager会等待这个场景,直到资源全部加载完成后,才开始执行切换操作。
注:在同一帧之内使用两次或以上replaceScene与popScene有可能会出现未定义行为,所以尽可能保证在同一帧内,有且只有一次切换操作。
4.场景缓存
class CCSceneExtension提供setCachable(bool)函数来让您在场景初始化时描述此场景是否缓存。CCSceneManager提供下列接口来管理缓存场景
removeCachedScene(scene class name) 把指定的场景从缓存池中移除
removeAllCachedScenes() 移除缓存池中的所有场景
removeUnusedCachedScenes() 移除所有在缓存池中但是并没有运行的场景
5.消息广播
如果您说看到这个名词会想起Windows消息循环,那这里也有几分相似之处。CocosBase里所谓的消息广播概念,即是由CCMsgManager提供的PostMessage函数来发出广播消息,所有class CCMsgDelegate的derived class都具备接收广播消息的权利,通过overriding the virtual onMessage回调函数来实现。class CCSceneExtension是多继承于class CCMsgDelegate的,所以场景对象都是具备有接收广播消息的功能,但条件是这个场景处于Running状态(具体请参考源码)。但有时我们并不想让消息散播出去,而是让指定的消息代理来接收这个消息,那么使用PostMessage的重载函数,指定第一个参数为目标消息代理即可实现。
PostMessage(int, CCObject, wParam, lParam) 将消息广播出去
PostMessage(delegate, int, CCObject, wParam, lParam) 将消息发到指定消息代理
读者还需要额外的了解两个函数,这两个函数用来注册或取消注册消息代理。CCMsgManager提供:
registerMsgDelegate(delegate) 注册消息代理
unregisterMsgDelegate(delegate) 取消注册消息代理
注:作者希望与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来管理。
onMessageReceived(buffer) 当读到消息时发生调用
onConnected() 当连接成功时发生调用
onConnectTimeout() 当连接超时发生调用
onDisconnected() 当连接中断时发生调用
onExceptionCaught(status) 当连接出现某种异常时调用
USING_PACKAGE_HEAD_LENGTH宏用来描述整个收发过程,是否使用包头长度的方式,作者解释一下这个基础知识,包头长度的解释是在整个发送或接收的数据包头部,有一个4个字节的int型数字(是否是4字节由操作系统决定),这个数字代表整个数据包的包长(不包括这个表示长度的数字)。如果USING_PACKAGE_HEAD_LENGTH被定义为1,则在发送数据包时,由内部自动为发送的数据包头部加上一个长度数字,而读取数据包时,则会先读取长度,再根据长度读取整个包的内容,具体请参考CCNetDelegate.cpp源码。
Cocos2d-x-3c CocosWidget
CocosWidget是基于Cocos2d-x移动跨平台游戏引擎的一套免费开源、功能强大、高效率、简封装的GUI库。
示例项目配置及API说明:
CocosWidget目前的控件数量已经达到了26个,远远超过其它GUI解决方案
Widget 基础控件
WidgetWindow 控件树根
Button 按钮控件
CheckBox 选择框控件
ControlView 游戏操作杆
GridView 网格容器控件 竖向
GridPageView 网格页容器控件 横竖向
ImageView 纹理容器控件(精灵)
ImageViewScale9 九宫格纹理容器控件
Label 文字控件
LabelAtlas 文字图块控件
LabelBMFont 图像文字集控件
ListView 链表滑动容器控件
PageView 页面滑动容器控件
Layout 基础容器控件
PanelColor 颜色容器控件
ProgressBar 进度条控件
ScrollView 基础滑动容器控件
Slider 滑块控件
TableView 表格制滑动容器控件
ToggleView 开关控件
ColorView 颜色控件
GradientView 渐变控件
ExpandableListView 可伸展链表滑动容器控件
TextRich 富文本控件
TextArea 文本区控件
在3c版本中,CocosWidget提供了大家瞩目以待的富文本控件、可伸展滑动容器控件等。本次升级,CocosWidget改进了整体结构及性能,CPanel重命名为CLayout,CWidgetLayout重命名为CWidgetWindow。
1:CocosWidget是目前唯一完全支持Lua Binding与富文本的GUI库。
2:拥有完整的Cpp与Lua示例代码,Cpp与Lua示例效果相似度99%,代码简洁减少学习成本。
3:在控件数量上绝对不会输于任何一款GUI框架。
4:完美的控件事件分发模型,拓展性强。
5:支持多点触摸,同时多个控件响应触摸操作。
6:所有控件都是从CCNode继承而来,与引擎完美结合。
7:支持长点击事件,轻而易举实现拖拽功能。