基本概念
为了全面掌握cocos2d-x的开发,我们首先需要了解该引擎的几个基本概念。实际上,这些基本概念是所有游戏开发所必须的,并非cocos2d-x专有。任何游戏都是通过这些概念所针对的对象组建起来的,游戏的复杂程度决定了这些对象实现的复杂程度。
场景(CCScene)
我们假设一个只有两关的游戏:第一关(2个小鬼,1个小BOSS);第二关(5个小鬼,1个大BOSS)。通常情况下,我们会这样设计整个游戏的流程(workflow):
开场动画,可以有几个目的:
简单介绍一下游戏操作
讲述故事背景
公司或者工作室动画LOGO
进入主菜单后可以引导用户:
开始新游戏
读取进度
设置游戏 声音,文字,游戏内容设置……
高分排名 通常都是列表,按分数排列。
帮助 操作简介
退出
接下来,玩家可以有多种选择,无论开始新游戏还是读取进度,游戏都会进入到预设关卡。游戏过程中,第一关胜利就进入第二关,第二关胜利则进入结尾的胜利画面(播放视频或者在背景图上显示文字),确认后进入排名画面看看本次得了多少分。如果失败了,那就会进入失败结束的画面,确定后跳转到主菜单,重新开始。
可以看出,玩家玩游戏的过程就是在我们预设的画面之间进行跳转,根据玩家操作的结果(选择菜单项、消灭敌人或者被敌人杀死)跳转到不同的画面。
这些构成游戏流程的画面就是我们所说的场景,图中用黑色线框表示的部分。显然,不同的场景提供不同的操作,大致可以分为以下几类场景:
展示类场景:播放视频或简单的在图像上输出文字,来实现游戏的开场介绍、胜利/失败提示、帮助说明等。
选项类场景:主菜单、游戏参数设置等。
游戏场景:这是游戏的主要内容,除了这个场景之外,其他类别的场景基本都是通用框架实现的。
那么不同的场景是如何实现不同功能的呢?每个场景都是通过不同的层的叠加和组合来实现不同的功能的。因此,通常一个场景是由一个或多个层组成的。
层(CCLayer)
层是我们写游戏的重点,我们99%以上的时间是在层上实现我们的游戏内容。如下图所示,一个简单的主菜单画面是由3个层叠加实现的:
细心的读者可能已经注意到,为了让不同的层可以组合产生统一的效果,这些层基本上都是透明或者半透明的。(否则我们就只能看到最上面的一个层了)
层的叠加是有顺序的,如图所示,编号为1的背景层在最下层,2号在中间,3号在最上面。cocos2d-x也是按照这个次序来叠加画面的,处于最上层的不透明的部分会将下面的内容覆盖。
这个次序同样用于编程模型中的事件响应机制。即编号3的层最先接收到系统事件(手指单击屏幕事件),然后是编号2,最后是编号1的层。在事件的传递过程中,如果有一个层处理了该事件,那么排在后面的层将不再接收到该事件。
我们可以简单地把层理解为我们在Windows编程中的窗口(hWnd或者WinForm,还有Delphi中的TForm)。
为了方便大家进行游戏开发,cocos2d-x从技术实现角度提供一些公用层:处理菜单用的菜单层,处理颜色显示的颜色层等。
每一层又可以包含各式各样的内容要素:文本、链接、精灵、地图等等。其中,精灵是游戏的重点。
精灵(CCSprite)
精灵是整个游戏开发处理的主要对象。敌方的飞机、坦克是系统控制的精灵,玩家控制的我方飞机也是精灵,甚至随机飞过的一片云、一只鸟都是精灵。
从技术上讲,精灵就是一个可以不断变化的图片。这些变化包括:位移、旋转、缩放以及图片帧的切换。
所谓游戏,就是玩家操作一个或多个精灵与系统控制的精灵进行互动:近身肉搏、远程射击、对话等等。
导演(CCDirector)
我们已经大概了解了一个游戏的整体架构,不同的场景由不同的层组成,每个层又包含精灵在上面运动。玩家玩游戏的过程就是在操作层上的精灵或者菜单项,从而在不同的场景中切换。
好了,有些OO编程基础的读者已经猜到导演对象的作用了。是的,按照面向对象的设计原则和反向依赖原则:精灵不应该依赖层,层不应该依赖场景,场景不应该依赖整个流程。导演对象就是整个流程的代表,他负责游戏过程中的场景切换。
导演通常只有一个,因此这个对象是单例(singleton)。cocos2d-x框架已经预定义了该实例,无需额外创建,我们直接使用就可以。
导演对象接收层对象/场景对象的请求,按照预先设计好的流程来切换场景。至此,我们可以勾勒出一个游戏的整体框架和cocos2d-x关键对象与之的对应关系:
需要特别说明的是:任何时间,只有一个CCScene对象实例处于激活状态。该对象可以作为当前游戏内容的对象的容器,对于菜单对象来说,通常属于当前场景的主层。以上就是一个游戏的主要架构。
实际上,针对每一个游戏场景而言,不同场景(关卡)、每一个层(静态、动态)、每一个对象(敌人、我方、中立方)其实都很复杂。万里长征,这才第一步呢。
cocos2d-x的实现类
下面,我们首先逐一介绍cocos2d-x对应上述基本概念的对象,以及他们之间的程序关联。
CCDirector(导演类)
CCDirector对象的作用类似于Windows编程中的主窗口对象(不同之处在于该对象并不可见),它负责创建、管理应用程序/游戏的主窗口,在特定的条件下显示某个场景。
针对CCDirector的调用代码(下面这段代码是主程序启动时的标准步骤,在AppDelegate的applicationDidFinishLaun
1 bool AppDelegate::applicationDidFinishLaunching()
2 {
3 // initialize director
4 CCDirector *pDirector = CCDirector::sharedDirector();
5 pDirector->setOpenGLView(&CCEGLView::sharedOpenGLView());
6
7 // enable High Resource Mode(2x, such as iphone4) and maintains low resource on other devices.
8 // pDirector->enableRetinaDisplay(true);
9
10 // turn on display FPS
11 pDirector->setDisplayFPS(true);
12
13 // set FPS. the default value is 1.0/60 if you don't call this
14 pDirector->setAnimationInterval(1.0 / 60);
15
16 // create a scene. it's an autorelease object
17 CCScene *pScene = HelloWorld::scene();
18
19 // run
20 pDirector->runWithScene(pScene);
21 return true;
22 }
显然,我们看到可以通过CCDirector对象完成以下两大类任务:
设置主程序窗口的显示属性。
依次设定了以下内容:
1.初始化CCDirector对象。
2.设置是否开启Retina显示模式。
3.设置是否显示FPS。
4.设置游戏画面每秒显示的帧数,默认是60帧。
管理、显示场景。
CCDirector对象一次只能显示一个场景。为了便于管理场景对象,CCDirector对象有3个属性与场景有关(参见CCDirector.h):
1
2 CCScene *m_pRunningScene;
3
4
5 CCScene *m_pNextScene;
6
7
8 CCMutableArray<CCScene*> *m_pobScenesStack;
同时,CCDirector对象管理场景的方法主要有以下几个:
1
2 void runWithScene(CCScene *pScene);
3
4
5 void pushScene(CCScene *pScene);
6
7
9 void popScene(void);
10
11
12 void replaceScene(CCScene *pScene);
13
14
15 void end(void);
16
17
18 void pause(void);
19
20
21 void resume(void);
CCScene(场景类)
场景对象当前比较简单,当前版本的cocos2d-x(1.0.1)基本上没有附加任何特殊功能,基本上可以看作是层(CCLayer)对象的一个容器。有些例子没有使用CCScene来作为场景切换,而是直接使用层的变换,笔者建议大家不要这样。CCScene的作用十分重要,如果要为场景切换增加动画效果,CCScene是必不可少的。
CCLayer(层类)
CCLayer的主要功能在于:
1)接收触摸(touch)操作输入。
2)接收动力感知(Accelerometer)输入。
除此之外,CCLayer对象本身没有提供更多的功能。关于CCLayer对象与TouchDispatcher的相互作用关系,我们放在以后的章节重点介绍。
cocos2d-x为了方便大家使用,直接提供了以下4个层:
CCLayerColor(颜色层)
这是一个透明的,可以按照RGB设置填充颜色的层。可以通过setContentSize设置层的大小,改变颜色块的尺寸。图中红色部分就是一个CCLayerColor的实例。
层也支持动作,可以变色、淡入淡出和混合。
CCLayerGradient(渐变层)
CCLayerGradient是CCLayerColor的子类,他可以在背景上绘制渐变色。
CCMenu(菜单层)
这是一个以CCMenu对象为集合类,CCMenuItem类实例组成各式各样按钮的菜单管理选择画面层。(注意:该层中的实例必须是CCMenuItem类或其子类的实例)
CCMenu类提供的方法主要是用来按照横向、竖向或者多行列排序展示CCMenuItem类实例的。
为了实现不同的按钮效果,系统提供了多种类型的CCMenuItem,但每个按钮都有三中基本状态:正常、选中、禁用。
下面,我们逐一介绍一下CCMenuItem类系:
CCMenuItem
CCMenuItem是基础类,不要直接使用该类。作为所有菜单项的父类,CCMenuItem主要完成以下两个任务:
1.设置按钮的状态。
2.负责处理回调函数(当按钮被单击后,需要调用的函数叫做回调函数)。具体说就是内置一个NSInvocation *invocation来统一实现回调函数的激活。(这段以后深入研究下再修正吧,在cocos2d-x里肯定不是)
CCMenuItemLabel
CCMenuItemLabel能将任何支持CCLabelProtocol协议的CCNode转变成一个菜单项,并增加选中时的放大效果。
CCMenuItemAtlasFont/CCMenuItemFont
CCMenuItemAtlasFont和CCMenuItemFont都继承自CCMenuItemLabel,他们能根据你提供的字符串生成标签,并以此创建菜单项。他们的区别是CCMenuItemAtlasFont使用图片集,CCMenuItemFont使用预设字体。
CCMenuItemSprite
CCMenuItemSprite内置3个支持CCRGBAProtocol协议的CCNode对象,表示正常、选中、禁用三个状态的图像。
CCMenuItemImage
CCMenuItemImage从CCMenuItemSprite派生而来,你只需提供图片的名字,创建CCSprite对象的过程由框架自动完成。
CCMenuItemToggle
内部有一个CCMenuItem数组,负责展示不同的状态,进而达到状态切换的效果。
CCLayerMultiplex(复合层)
这是可以包含多个层的复合层,将来再专门介绍。
CCSprite(精灵类)
精灵是游戏中的主要静态、动态目标(敌方怪物、我方操作对象)。具体讲就是一个独立的图像块,通常情况下他是运动的(Action):位移、旋转、缩放、运动——连续渐变图像形成的运动效果。我们可以直接通过设定精灵的属性让他运动,也可以通过动作(Action)来达到同样的目的。
在cocos2d-x中精灵由CCSprite类实现。精灵允许包含子对象,当父对象变化的时候,子对象会跟着变化。
由于游戏中95%以上的内容都是精灵类实现模拟的,因此如何提高精灵类的执行效率就是一个十分关键的问题。
1)缓存图像内容,减少相同内容文件的读取次数。
通过CCTextureCache类,cocos2d-x库按照文件名为主键索引全部运行时读取的图片文件。当文件名一样时,直接返回内存中的图片而不再读取文件。
所有与图片文件有关的实现在底层统一调用CCTextureCache类的单例对象,保证最少的系统IO操作,提高程序运行效率。
2)批量提交绘画,减少OpenGL函数的调用次数。
通过CCSpriteBatchNode类,cocos2d-x库将所有CCSpriteBatchNode类对象所属的子CCSprite对象一次提交给OpenGL输出。
还有一个叫CCSpriteFrameCache的类,用于管理动画效果的全部帧图像。该类的实现也调用了CCTextureCache类对象。
小结
至此,读者对于cocos2d-x的几个关键概念以及对应的实现类有了整体的把握。CCScene、CCLayer、CCSprite类都是从CCNode类派生的,从类对象角度上来说他们是一样的,可以互相从属。从游戏设计的角度,他们完成的功能则互不相同,各有重点:
使用CCScene是为了:
1)作为某个场景的总体容器对象,将所有的内容对象(菜单、状态、游戏角色、NPC)包含在内,层叠关系通过CCNode的addChild的zOrder决定。
2)实现场景切换的特殊效果。因为所有的场景切换特效都是从CCScene的子类CCTransitionScene派生的。
使用CCLayer是为了处理输入问题:
1)触摸事件处理
2)动力感知处理
使用CCSprite是为了显示各式各样的精灵,展示游戏内容。