一、引擎框架
Cocos2D-X引擎的设计思路是将游戏的各个部分抽象成几个概念,包括导演、场景、布景层和人物精灵,它们之 间的关系如下图所示。
几乎任何一款游戏中都会有这些概念,而游戏的复杂程度也就决定这些部分之间的关系的复杂程度。具体说明如 下: <1> 导演(CCDirector):顾名思义,导演类是游戏中的组织者和领导者,是整个游戏的负责人、总指挥。导演类可 以制定游戏的运行规则,从而让游戏内的场景、布景类和精灵类有序地进行。 <2> 场景(CCScene):场景就是一个关卡,或者是一个游戏界面。这样的一个一个场景确定了整个的游戏。 <3> 布景层(CCLayer):一个场景可以由多个布景层构成,布景层就是关卡里的背景,关卡不同也就是场景需要的 布景层不同。有时候,为了游戏的不同模块的管理更加方便,会把一个场景分为多个布景层,如UI布景层、游戏布景 层;有些游戏需要更细致的细分,可以分为游戏对象布景层和游戏地图布景层。 <4> 人物精灵(CCSprite):人物精灵可以分为玩家控制的主角类、敌人类;更复杂的游戏可以分为NPC类、机关类 等。它们是构成游戏的关键要素。 二、节点类(CCNode) 节点类是Cocos2D-X中的主要类,继承自CCObject,继承关系如下图所示。
任何需要画在屏幕上的对象都是节点类。最常用的节点类包括场景类(CCScene)、布景层类(CCLayer)、 人物精灵类(CCSprite)、菜单类(CCMenu)。 <1> 每个节点都可以包含有子节点,这点会在后面给出示例。 <2> 节点含有周期性回调的方法(Schedule、Unschedule等)。关于周期性回调方法,后面会详细讲解,如果现在 不了解,可以跳过这段内容。 <3> 可以含有动作(CCAction)。 CCNode可以为它自己和它的子节点添加额外的功能。无论是CCNode运行的动作(CCAction),还是设置的旋 转角度和位置等属性,父节点的设置都可以传递到子节点上,这点在一些游戏的开发中可以使我们的管理更轻松。比 如某些纵版射击游戏,玩家控制的主角飞机需要携带一些子机,子机的移动位置要随着主机一起移动,我们就可以把 子机设置为主机的子节点,这样,在设置位置的时候,只需要设置主机的位置就可以了,大大减少了程序员需要处理 由于CCNode类不自带贴图,其实在屏幕上看不到任何节点类的效果,所以一般使用CCNode类的场合有两个: 第一个情况就是需要一个父节点来管理一批子节点,这时候可以设置一个“无形”的子节点来管理子节点;另一种情况 就是有时需要自己定义一个在屏幕上显示的对象,这时候让新定义的这个类继承自CCNode。一个类继承自CCNode 类,说明它有如下特点: <2> 在时间线上控制回调。 <3> 重写渲染的绘制方法。 CCNode类不含有贴图,它可以进行位置的平移、大小的伸缩变化、旋转变化。在网络特效(会在后面学习到) 使用的时候,网络特效可以获得屏幕中绘制的内容,并且对获得的屏幕内容进行渲染。这点在游戏中需要一些全屏特 效的时候可以使用。 1、CCNode类的成员数据
CCNode 类的主要公共成员数据,如下图所示。
CCNode 类的主要保护成员数据(子类可见,Protected关键字)
2、CCNode类的函数 CCNode类的主要函数如图所示。
以上就是CCNode类的部分函数,其中不包括我们后面会学习到的调度相关的函数。 在2.0版本之前,CCNode类的构造函数是CCNode::node();在2.0版本以后可以使用create函数进行创建。 注意 :非C++程序员可能会在API文档中遇到陌生的virtual修饰符。其修饰符的结果是使此函数为虚函数。虚函数 必须是基类的非静态成员函数,其访问权限可以是protected或者是public,实现多态性,通过指向派生类的基类指 针,访问派生类中同名覆盖成员函数。 在定义了虚函数后,可以在基类的派生类中对虚函数重新定义。在派生类中重新定义的函数应与虚函数具有相同 的形参个数和形参类型,以实现统一的接口,不同定义过程。如果在派生类中没有对虚函数重新定义,则它继承其基 类的虚函数。简而言之,虚函数允许在程序运行过程中根据指针的类型动态地选择它调用指针类型的函数。 3、坐标系简介
通过上面的学习,相信大家对CCNode类的属性和方法有所了解,但是可能有些名词会令你困惑,如OpenGL坐标 系、世界坐标系、节点相对坐标系、仿射变换等。那么下面我们就来解决这些问题。
① OpenGL坐标系
Cocos2D-X以OpenGL和OpenGL ES为基础,所以自然支持OpenGL坐标系。该坐标系原点在屏幕左下角,X轴向 右,y轴向上。 屏幕坐标系使用的是不同的坐标系统,原点在屏幕左上角,x轴向右,y轴向下。 iOS的屏幕触摸时间CCTouch传 入的位置信息使用的是该坐标系。因此在Cocos2D-X中对触摸事件做出响应前,需要首先把触摸点转化到OpenGL坐 标系。这一点我们会在后面的触屏信息中详细介绍,可以使用CCDirector的convertToGL方法来完成这一转化。
② 世界坐标系
世界坐标系也叫作绝对坐标系,是游戏开发中建立的概念,因此“世界”即是游戏世界。它建立了描述其他坐标系所 需要的参考标准。我们能够用世界坐标系来描述其他坐标系的位置。它是Cocos2D-X中一个比较大的概念。 Cocos2D-X中的元素是有父子关系的层级结构。通过CCNode设置位置使用的是相对其父节点的本地坐标系,而非 世界坐标系。最后在绘制屏幕的时候,Cocos2D-X会把这些元素的本地节点坐标映射成世界坐标系坐标。世界坐标系 和OpenGL坐标系方向一致,原点在屏幕左下角,x轴向右,y轴向上。 下面让我们暂时从纷乱的坐标系中抽出来,看一个重要概念:锚点。 ③ 锚点
锚点指定了贴图上和所在节点原点(也就是设置位置的点)重合的点的位置,因此只有在CCNode类节点使用贴 图的情况下,锚点才有意义。 锚点的默认值是(0.5,0.5),表示的并不是一个像素点,而是一个乘数因子。(0.5,0.5)表示锚点位于贴图长 度乘0.5和宽度乘0.5的地方,即贴图的中心。 改变锚点的值虽然可能看起来节点的图像位置发生了变化,但其实并不会改变节点的位置,其实变化的只是贴图 相对于你设置的位置的相对位置,相当于你在移动节点里面的贴图,而非节点本身。如果把锚点设置成(0,0),贴 图的左下角就会和节点的位置重合,这可能使得元素定位更为方便,但会影响到元素的缩放和旋转等一系列变换。因 此并没有一种锚点设置是放之四海而皆准的,要根据你这个对象的使用情况来定义。在Cocos2D-X中锚点为默认值 (0.5,0.5),这样的锚点设置要把一个节点放置到贴图的中央。 ④ 节点坐标系 节点坐标系是和特定节点相关联的坐标系。每个节点都有独立的坐标系。当节点移动或改变方向时,和该节点关 联的坐标系(它的子节点)将随之移动或改变方向。这一切都是相对的,相对于基准的,只有在节点坐标系中才有意 义。 CCNode类的设置位置使用的就是父节点的节点坐标系。它和OpenGL坐标系的方向也是一致的,x轴向右,y轴向 上,原点在父节点的左下角。如果父节点是场景树中的顶层节点,那么它使用的节点坐标系就和世界坐标系重合了。 CCNode类对象中有两个方便的函数可以做坐标转换: <1> convertToWorldSpace:把基于当前节点的本地坐标系下的坐标转换到世界坐标系中。 <2> convertToNodeSpace:把世界坐标转换到当前节点的本地坐标系中。
这两种转换都是不考虑锚点的,都以当前节点父类的左下角的坐标为准。另外,CCNode还提供了 convertToWorldSpaceAR和convertToNodeSpaceAR。这两个方法完成同样的功能,但是它们的基准坐标是基于坐标 锚点的。几乎所有的游戏引擎都会使用类似的本地坐标系而非世界坐标系来指定元素的位置。 这样做的好处就是,当计算物体运动的时候,使用同一本地坐标系的元素可以作为一个子系统独立计算,最后再 加上坐标系的运动即可,这是物理研究中常用的思路。例如,一个在行驶的车厢内上下跳动的人,只需要在每帧绘制 的时候计算他在车厢坐标系中的位置,然后加上车的位置就可以计算出人在世界坐标系中的位置。如果使用单一的世 界坐标系,人的运动轨迹就变复杂了,就涉及中学所学到的运动轨迹的合成与分解。 ⑤ 仿射变换 最后介绍仿射变换。游戏大量使用的旋转、缩放、平移等都是仿射变化。所谓的仿射变化是指在线性变换的基础 上加上平移。平移不是线性平移。 二维计算机图形学中的仿射变换通常是通过和3 x 3齐次矩阵相乘来实现的。Cocos2D-X的CCAffineTransform结构体的定义如代码清单所示。
其中定义了a、b、c、d、tx、ty,它们在矩阵中的位置如下图所示。
三、实例(通过节点控制屏幕中的全体渲染对象)
学到这个地方,我们已经可以利用目前所学的知识做出一些有趣的东西了。之前已经说过,CCNode类没有贴图, 也就是说在屏幕上单独建立一个节点是没有任何效果的,但是可以通过这个“无形”的节点来控制屏幕上的节点。那 么,让我们现在就开始吧!
1、加入节点
新建一个项目(新手新建项目可参考文章《解决Cocos2D-X新建项目运行报错的问题》),并在 HelloWorldScene.cpp文件中的init函数中做如下所示的修改。 先详细分析一下上面的这段代码:首先通过create函数创建一个节点anode,把anode作为子节点使用addChild函数加入HelloWorld场景类对象中;然后把本来作为子节点加入HelloWorld场景类对象中的对象,使用addChild函数作为子节点加入anode中。这些对象包括菜单类对象、标签类对象、人物精灵类对象。这些对象的类都是CCNode类的子类。如果目前你对这些对象的使用不了解的话,请跳过这些对象的使用,我们会在后面详细的学习它们的使用。 运行项目,发现和修改前的项目并无区别,如下图所示。 2、改变位置 在HelloWorldScene.cpp文件中的init方法中加入如下代码。 可以看到,整体都跟着节点移动了。 3、设置缩放 下面修改这段代码。 看一下缩放的效果。 4、整体旋转 最后再来试验整体旋转,因为默认锚点在左下角,因此需要首先移动一下整体的位置,否则整体一转,屏幕中将只显示黑屏。注意,角度设置为角度制。 运行效果图如下图所示。 这一期我们学习到这里差不多就结束了,下一期我们会学习控制游戏显示的导演类CCDirector的使用。 MyCCNode.zip |