Cocos2d-x高级开发教程阅读笔记

来源:互联网 发布:oracle数据库迁移 编辑:程序博客网 时间:2024/06/05 05:32

纹理贴图

我们可以认为纹理就是一张图片,这张图片被精灵显示出来。更深层地讲,纹理

是 3D 游戏中绘制到物体表面上的图案。虽然 Cocos2d-x 是平面游戏引擎,但它仍然使用了 3D 绘图库 OpenGL。这样一来,

我们既可以利用图形加速器提高绘图效率,也可以在游戏中加入 3D 变换特效,实现更绚丽的效果。为了在 3D 环境中绘制

平面图形,Cocos2d-x 只需在 3D 空间中垂直于视线的平面上绘制矩形,在矩形的表面使用纹理贴图即可。

通过纹理来显示图片的一部分

CCSprite* smallFish =CCSprite::create("fishes.png", CCRectMake(0, 0, 100, 100));

值得注意的是,纹理的坐标系中原点(0,0),位于左上角,原点向右是 x 轴的正方向,原点向下是 y 轴的正方向

使用纹理来创建精灵

使用 CCTexture2D 纹理创建精灵的相关方法如下:

  static CCSprite* create(CCTexture2D*pTexture);

  static CCSprite* create(CCTexture2D *pTexture, const CCRect& rect);

使用 CCSpriteFrame 精灵框帧创建精灵

使用 CCSpriteFrame 精灵框帧创建精灵的相关方法如下:

  static CCSprite* create(CCSpriteFrame*pSpriteFrame);

  bool initWithSpriteFrame(CCSpriteFrame*pSpriteFrame);

CCSpriteBatchNode* BatchNode:获取或设置精灵所属的批节点

 

Cocos2d- -x 坐标系统

1.绘图坐标系

2.纹理坐标系

在节点不被显示的时候,也不会被调用绘图方法(visit与 draw)

 

float SkewX 与 float SkewY:获取或设置斜切角度

 

void* UserData:获取或设置与节点相关的额外信息。UserData 为 void*类型,我们可以利用这个属性来保存任何数据

 

场景进入

Cocos2d-x 内置了 3 种特殊的 CCLayer

CCLayerColor:一个单纯的实心色块。

CCLayerGradient:一个色块,但可以设置两种颜色的渐变效果。

 

可以调用函数调用色块的大小

void changeWidth(GLfloat w);

voidchangeHeight(GLfloat h);

voidchangeWidthAndHeight(GLfloat w ,GLfloat h);

 

CCMenu:十分常用的游戏菜单。

暂停和恢复游戏可以由 CCDirector 这个游戏大总管来完成

整个游戏暂停CCDirector::sharedDirector()->pause();

记得貌似场景也可以暂停

游戏主循环:

CCDirector 包含一个管理引擎逻辑的方法,它就是CCDirector::mainLoop()方法,这个方法负责调用定时器,绘图,发送全局通知,并处理内存回收池。该方法按帧调用,每帧调用一次,而帧间间隔取决于两个因素,一个是预设的帧率,默认为 60 帧每秒;另一个是每帧的计算量大小。当逻辑处理与绘图计算量过大时,设备无法完成每秒60 次绘制,此时帧率就会降低。

mainLoop()方法会被定时调用,然而在不同的平台下它的调用者不同。通常 CCApplication 类负责处理平台相关的任务,其中就包含了对 mainLoop()的调用。有兴趣的读者可以对比 Android、iOS 与 Windows Phone 三个平台下不同的实现,平台相关的代码位于引擎的"platform"目录。(有空了必须要去了解下)

场景的绘制与 OpenGL 密切相关

动作

CCAction 是动作类的基类,所有的动作都派生自这个类,它创建的一个对象代表了一个动作

瞬时动作

CCFiniteTimeAction* placeAction = CCPlace::create(ccp(100, 100));

接口(即抽象类)

CCFiniteTimeAction 派生出的两个主要类分别是瞬时动作(CCActionInstant)和持续性动作(CCActionInterval)

CCSequence 又称为动作序列,是一种复合动作,它在初始化时,会接受多个动作,当它被执行时,这些动作会按顺序逐个执行,形成有序的一列动作

让动作更平滑流畅  p.79(在游戏设计中挺有用的)

    CCMenu* menu =CCMenu::create(item0, item1, item2, item3, NULL);

 menu->alignItemsVerticallyWithPading(5.0f);

 menu->setPosition(ccp(size.width/2, size.height));

 menu->setTag(menu_pause_tag);

 this->addChild(menu, 5);

 CCMoveTo* move = CCMoveTo::create(0.5f,ccp(size.width/2, size.height/2));

 CCAction* action = CCEaseExponentialOut::create(move);

 menu->runAction(action);

 

void CCActionManager::update(float dt)

 

Action * Node::runAction(Action* action)

{

    CCASSERT( action != nullptr,"Argument must be non-nil");

   _actionManager->addAction(action, this, !_running);

    return action;

}

 

void ActionManager::addAction(Action *action, Node *target, bool paused)

{

    CCASSERT(action != nullptr,"");

    CCASSERT(target != nullptr,"");

 

    tHashElement *element =nullptr;

    // we should convert it toRef*, because we save it as Ref*

    Ref *tmp = target;

    HASH_FIND_PTR(_targets,&tmp, element);

    if (! element)

    {

        element =(tHashElement*)calloc(sizeof(*element), 1);

        element->paused =paused;

        target->retain();

        element->target =target;

        HASH_ADD_PTR(_targets,target, element);

    }

 

     actionAllocWithHashElement(element);

 

     CCASSERT(!ccArrayContainsObject(element->actions, action), "");

    ccArrayAppendObject(element->actions, action);

 

    action->startWithTarget(target);

}

 

 

void Action::startWithTarget(Node *aTarget)

{

    _originalTarget = _target =aTarget;

}

5。动画

在 Cocos2d 中,动画的具体内容是依靠精灵显示出来的。我们知道精灵可以用来显示一张静止的图片,而为了显示动态图片,我们需要不停地切换精灵显示的内容。一旦明白了这个道理,我们可以利用定时器不停地改变精灵的显示内容,把静态的精灵变为动画播放器。事实上,Cocos2d-x 提供的动画就是基于这个原理

动画帧类 CCAnimationFrame 同样包含两个属性,其一是对一个框帧的引用,其二是帧的延时

框帧 (CCSpriteFrame) : 包含纹理与纹理中的一个矩形区域,表示纹理的一部分。一个精灵显示的内容就可以用框帧表示,

同时框帧还是帧动画的基本元素。

动画帧(CCAnimationFrame):由框帧与单位延时组成,可以表示变速动画中的一帧。通常,匀速动画的单位延时为 1。

动画(CCAnimation):由动画帧组成,表示一个动画的内容。

动画动作(CCAnimate):动画的播放器,使用动画对象创建,只能作用于精灵。为了播放一个动画,通常首先创建动画帧

或框帧,然后用它们创建动画,最后利用动画创建动画动作,并指派一个精灵来执行此动作。

框帧—动画帧—动画—动画动作

场景特效(CCTransitionScene):一类特殊的场景,可以把另一个场景包装起来,实现诸如特殊翻页、波纹等华丽的场景

切换特效。

场景特效CCTransitionScene 派生自 CCScene,换句话说,场景特效本身也是一个场景

6。音效引擎

SimpleAudioEngine

7.5加速度计

重力感应

7.6 文字输入

CCTextFieldTTF text = CCTextFieldTTF::textFieldWithPlaceHolder(

 "Input Your Name...", "Arial", 20);

 text->setPosition(ccp(winSize.width / 2, winSize.height / 2 + 40));

 this->addChild(text);

在回调函数里面调用:

 text->attachWithIME();

CCTextFieldTTF 提供了 3 个常用的操作接口

bool attachWithIME(); //激活输入法

bool detachWithIME(); //取消激活输入法

8.1  Cocos2d-x  中的粒子系统

粒子系统(partical system)

Cocos2d-x 为我们提供的粒子系统由 CCParticleSystem 类实现

Plist 文件实质上是一个 XML 文件,我们可以利用任何文本编辑器来创建或修改

内置的几种粒子效果

CCParticleFire    火焰效果

CCParticleSun    太阳效果

CCParticleExplosion 爆炸效果

CCParticleSnow    雪花效果

使用:

CCParticleSystem *particle = CCParticleSnow::node();

particle->setTexture(CCTextureCache::sharedTextureCache()->addImage("snow.png"));

this->addChild(particle);

8.2 粒子效果编辑器(略)

里面有编辑器个每个属性的属性的介绍比较详细。如果项目中有具体使用到的可以去看看

9.1 瓦片地图

TileMap 要求每个瓦片占据地图上一个四边形或六边形的区域。把不同

的瓦片拼接在一起,就可以组成一个完整的地图了。我们需要使用许多较小的纹理来创建瓦片。通常来说,我们会把这些较小的纹理放入一张图片中,就像碎图纹理一样。这样做可以有效地提高绘图性能,同时也为引擎的管理带来了便利。因此,每个小瓦片都应该来自一张大的纹理图片。

9.2.1 Tiled Map Editor

详细使用参照书本一步一步实现,这里掠过了

9.3 导入游戏

CCTMXTiledMap::create("background.tmx")

9.4 实现层次感

10.1   OpenGL(重要)

OpenGL 全称为 Open Graphics Library,是一个开放的、跨平台的高性能图形接口。OpenGLES 则是 OpenGL 在移动设备上的衍生版本,具备与OpenGL 一致的结构,包含了常用的图形功能。Cocos2d-x 就是一个基于 OpenGL 的游戏引擎,因此它的绘图部分完全由 OpenGL 实现OpenGL 作为一个完整的底层图形接口,它的功能几乎涵盖了全部计算机图形学,因此我们无法在短短的几章中详细介绍。

由于 Cocos2d-x 已经封装了大量的绘图细节,这里我们将简单介绍引擎的绘图方式,并从OpenGL 渲染的角度来分析如何使游戏的效率达到最佳。

 

 

10.1.1 L OpenGL  简介(1 )

状态机

OpenGL 是一个基于状态的绘图模型,我们把这种模型称为状态机

游戏通常运行在一台设备上,在设备中 CPU 负责运行游戏的逻辑,并向 GPU(硬件显卡或是软件模拟的显卡)发送绘图指令。在这种架构下,CPU 和 GPU 分别充当客户端与服务器端的角色(正如图 10-1 所描绘的那样)。在实际使用中,OpenGL 的客户端与服务器端是可以分离的,因此可以轻而易举地实现远程绘图。举例说明,如果需要实现一个远程桌面系统,设备 A 是被控制端,设备 B 是控制端,我们需要在设备 B 上呈现设备 A 中的图形,因此设备A 可以通过网络向设备 B 发送绘图指令,而设备 B 负责绘制与渲染图形。在这个例子中,设备 A 就是 OpenGL 客户端,而设备B 是 OpenGL服务器端。

在游戏的例子中,绘图指令及数据由 CPU 发送到 GPU,状态机的优势看似并不是十分明显,而在远程绘图的例子中,绘图指令及数据由设备 A 通过网络发送到设备 B,网络的带宽显然是有限的,因此为了提高效率,我们通常把可以在客户端完成的工作分摊给客户端,只把绘图所必需的数据发送到服务器端即可。事实上,即使是运行在计算机上的游戏,也受益于 OpenGL的架构。在计算机上,CPU 与 GPU 通过总线相连,虽然总线的带宽远高于网络连接,但在许多情况下,带宽明显不能满足高速运算的 CPU 与 GPU 之间传递数据的需要。因此我们也需要尽力避免在客户端与服务器端传递不必要的数据

 

OpenGL 是一个非常接近底层的接口标准,核心部分只包括了约 170 个函数和约 300 个常量,与汇编指令的数量相差无几,这也是我们需要用游戏引擎来减轻开发工作量的原因

OpenGL 是一个三维图形接口,在程序中使用右手三维坐标系。具体地说,在初始化时,屏幕向右的方向为 X 方向,屏幕向上的方向为 Y 方向,由屏幕指向我们的方向为 Z 方向

10.1.1 L OpenGL  简介(2 2 )

为了呈现精灵,引擎会根据精灵的位置创建矩形,在 OpenGL 中设置矩形的顶点以及纹理,把图形绘制并呈现到屏幕上在不对 OpenGL 做任何设置的时候,初始的坐标系称作世界坐标系,我们当然可以在世界坐标系中完成所有绘图。然而如果真的这么做,为了把一个物体绘制到不同的位置,我们就不得不去修改物体的所有顶点坐标

当我们把绘制的图形传递给 OpenGL 后,OpenGL 还要进行许多操作才能完成 3D 空间到屏幕的投影。通常,渲染流水线过程(如图 10-5 所示)有如下几步:显示列表、求值器、顶点装配、像素操作、纹理装配、光栅化和片断操作等。

10.1.2 绘图

随着 OpenGL 的发展,其提供的绘图函数也变得多种多样。对于同一个效果来说,常常有多种不同的实现方法,

draw 大致上可以分为 3 个部分--数据部分、初始化纹理和绘图,它绘制了一个带纹理的矩形

操作:

代码的第一部分是数据部分,在这一部分中我们声明了 3 个静态数组,它们分别是 vertex、coord 和 color,对应了三角形带中共 4 个顶点的顶点坐标、纹理坐标和顶点颜色。每个数组均按照左下、右下、左上、右上的顺序来存储。

vertex:共 4 个顶点,每个顶点包含 x、y 和 z 三个分量,因此顶点坐标数组共有 12 个值。在本例中,矩形位于屏幕左下角,大小为 200×200。

coord:包含 s 和 t(横坐标和纵坐标)两个分量,因此共有 8 个值,每个分量的取值范围是 0 到 1,需要根据纹理的属性确定取值。

color:包含 r、g、b 和 a(红色、绿色、蓝色和不透明度)4 个分量,因此共有 16 个值,每个分量的取值范围是 0~1。把

颜色值设为纯白(1, 1, 1, 1),则会显示纹理原来的颜色。

第二部分是初始化纹理。利用 CCTextureCache 类可以方便地从文件中载入一个纹理, 获取纹理尺寸, 以及获取纹理在 OpenGL的编号。在纹理没有被初始化时,我们首先使用CCTextureCache::addImage 方法载入一个图片,把返回的 CCTexture2D对象保存下来,并使用纹理的属性设置 4 个顶点的纹理坐标。对于单个纹理的图片,只需要按照上面代码中的方法设置纹理坐标即可。关于纹理坐标的细节,我们将在 10.3.2 节中详细介绍。

最后一部分是绘制图片。绘制图片的步骤可以简述为:绑定纹理、设置顶点数组和绘图。绑定纹理是指把一个曾经载入的

纹理当做当前纹理, 从此绘制出来的多边形都使用此纹理。设置顶点数组是指为 OpenGL 指定第一步准备好的顶点坐标数组、纹理坐标数组以及顶点颜色数组。绘图则是最终通知 OpenGL 如何利用刚才提供的信息进行绘图,并实际把图形绘制出来。在这个过程中,我们可以看到最重要的一个函数为 glDrawArrays(GLenum mode, GLint first, GLsizei count),其中 mode指定将要绘制何种图形,first 表示前面数组中起始顶点的下标,count 表示即将绘制的图形顶点数量。

10.1.3 矩阵与变换

OpenGL ES 2.0 已经放弃了固定的渲染流水线,取而代之的是自定义的各种着色器,在这种情况下变换操作通常需要由开发者来维护。所幸引擎也引入了一套第三方库 Kazmath,它使得我们几

乎可以按照原来 OpenGL ES 1.0 所采用的方式进行开发

10.2.1 精灵的绘制

观察 draw 方法的代码可知,它包含 5 部分,其中前 4 个部分较为重要。第 1 部分主要负责设置 OpenGL 状态,如开启贴图等。第 2 部分负责设置颜色混合模式,与贴图渲染的方式有关。第 3、4 部分分别负责绑定纹理与绘图

在进行一次普通精灵的绘制过程中,我们需要绑定一次纹理,设置一次顶点数据,绘制一次三角形带。对 OpenGL 的每一次调用都会花费一定的开销,当我们需要大量绘制精灵的时候,性能就会快速下降,甚至会导致帧率降低。因此,针对不同的情况,可以采取不同的策略来降低 OpenGL 调用次数,从而大幅提高游戏性能。

10.2.2 渲染树的绘制(1 1 )

Cocos2d-x 的调度原理,在游戏的每一帧都会运行一次主循环,并在主循环中实现对渲染树的渲染

先绘制子节点,再绘制自身。整个渲染树就是这样的

10.2.3 坐标变换

Cocos2d-x 使用的一个开源几何计算库 Kazmath,它是 OpenGLES 1.0 变换函数的代替

10.3.1 绘图瓶颈

绘图瓶颈,问题:

纹理过小:OpenGL 在显存中保存的纹理的长宽像素数一定是 2 的幂,对于大小不足的纹理,则在其余部分填充空白,这无疑是对显存极大的浪费;另一方面,同一个纹理可以容纳多个精灵,把内容相近的精灵拼合到一起是一个很好的选择。

纹理切换次数过多:当我们连续使用两个不同的纹理绘图时,GPU 不得不进行一次纹理切换,这是开销很大的操作,然而当我们不断地使用同一个纹理进行绘图时,GPU 工作在同一个状态,额外开销就小了很多,因此,如果我们需要批量绘制一些内容相近的精灵,就可以考虑利用这个特点来减少纹理切换的次数。

纹理过大:显存是有限的,如果在游戏中不加节制地使用很大的纹理,则必然会导致显存紧张,因此要尽可能减少纹理的尺寸以及色深。

显存(显卡内存):显存,也被叫做帧缓存,它的作用是用来存储显卡芯片处理过或者即将提取的渲染数据。如同计算机的内存一样,显存是用来存储要处理的图形信息的部件。

显卡(GPU

FPS(每秒传输帧数(Frames Per Second))

FPS是图像领域中的定义,是指画面每秒传输帧数,通俗来讲就是指动画或视频的画面数。FPS是测量用于保存、显示动态视频的信息数量。每秒钟帧数 愈多,所显示的动作就会愈流畅。通常,要避免动作不流畅的最低是30。某些计算机视频格式,每秒只能提供15帧。

FPS”也可以理解为我们常说的“刷新率(单位为Hz)”,例如我们常在CS游戏里说的“FPS值”。我们在装机选购显卡和显示器的时候,都会注意到“刷新率”。

10.3.2碎图压缩与精灵框帧

10.3.3批量渲染

CCSpriteBatchNode

使用一个张图片多次可以使用这个类

10.3.4色彩深度优化

默认情况下,我们导出的纹理图片是 RGBA8888格式的,它的含义是每个像素的红、蓝、绿、不透明度 4 个值分别占用 8比特(相当于1字节) , 因此一个像素总共需要使用 4 个字节表示。 若降低纹理的品质, 则可以采用 RGBA4444 格式(如图 10-10所示)来保存图片。RGBA4444 图片的每一个像素中每个分量只占用 4 比特,因此一个像素总共占用 2 字节,图片大小将整整减少一半。对于不透明的图片,我们可以选择无 Alpha 通道的颜色格式,例如 RGB565,可以在不增加尺寸的同时提高图像品质。各种图像编辑器通常都可以修改图片的色彩深度,TexturePacker 也提供了这个功能。TexturePacker 提供了抖动的解决方案

10.4小结

在这一章中,我们首先探索了Cocos2d-x的绘图机制,然后根据其机制使用引擎提供的解决方案优化了我们的工程。在这一章中,我们接触到的知识点较多,在此总结如下。

  OpenGL:一个开放式的、跨平台的、高性能的图形 API。

  OpenGL ES:OpenGL 的简化版本,广泛被移动设备采用作为其绘图 API。

 状态机、 流水线: OpenGL 是基于状态机的图形接口,表现为它保存着一系列状态, 用于描述 OpenGL 的行为, 除非进行设置,

否则每个状态都不会被改变。流水线是OpenGL的工作流程,理解流水线有助于理解 OpenGL 的绘图原理。

  OpenGL绘制纹理:基本流程为绑定纹理、输入顶点数据(包括顶点坐标、纹理坐标和顶点颜色)、绘图。

 变换:在 OpenGL 中,我们使用矩阵表示变换,任何点表示为一个四维向量,变换矩阵表示为四阶方阵。对顶点进行的几何

变换对应了矩阵乘法。矩阵运算可以在 GPU中高效完成。在 OpenGL 中,我们维护了一个表示当前坐标系的绘图矩阵,坐标

系变换则通过改变此矩阵来实现。

  Cocos2d-x绘图流程:在游戏主循环中调用场景的 visit 方法,在此之中通过 transform 方法进行坐标系变换,然后递归调

用子节点的 visit方法,完成对整棵渲染树的绘制。

 碎图压缩:通过把小的纹理拼接为一个大纹理,减少纹理空间的浪费。

 批量渲染:把需要批量绘制的纹理通过碎图纹理的方式放入一个大型纹理中,然后利用 CCBatchNode 统一绘制所有精灵,

可以提高绘制效率。

 颜色深度:表达一个颜色所需的比特长度。通常采用的 RGBA8888 格式利用 32 比特来描述一个颜色,而当我们对品质要求

不高时,可以使用 RGBA4444格式,采用 16 比特来描述一个颜色。此时图片的大小会减半,相对地,图片的颜色品质会下

降。

 这里介绍的 OpenGL 以及优化相关的知识还是略为浅显的,在更大型的工程中,为了提高绘制效率,以及降低内存消耗,常常会引入骨骼动画和网格纹理等用到 OpenGL 高级特性的深度优化方案。这里我们不再展开介绍,感兴趣的读者不妨查阅相

关资料。

 着色器:着色器是用于代替渲染流水线的一段程序,可以用来实现对顶点和像素的操作。在这一章中,我们使用着色器实现了水纹效果。

 CCGrid3D:Cocos2d-x提供一套网格变换的功能,通过 CCActionGrid3D 动作类可以实现一些简单画面的 3D 变换,例如水纹

物理引擎

Box2D是与 Cocos2d-x 一起发布的一套开源物理引擎,也是 Cocos2d-x 游戏需要使用物理引擎时的首选。Box2D 是一套基于刚体模拟的物理引擎,它的核心概念为世界、物体、形状、约束和关节,这些概念具体实现为 Box2D 的各个组件

11.3数据交流

截屏功能

CCImage* image =saveScreenToCCImage();

 image->saveToFile("screen.png");

 image->release();

动态的截屏预览,一个动态的屏幕截图预览

CCRenderTexture* render =saveScreenToRenderTexture();

 this->addChild(render);

 render->setScale(0.3);

render->setPosition(ccp(CCDirector::sharedDirector()->getWinSize().width,0));

 render->setAnchorPoint(ccp(1,0));

11.4.1 可编程着色器

在 Cocos2d-x 中,最大的变革就是引入了 OpenGL ES 2.0 作为底层绘图

12项目中没有具体用过,所以暂时先不看了

13 CCUserDefault

  CCUserDefault是Cocos2d-x引擎提供的持久化方案,其作用是存储所有游戏通用的用户配置信息, 例如音乐和音效配置等。

为了方便起见,有时我们也可以用 CCUserDefault 来存储金币数目这种简单的数据项。

CCUserDefault 可以看做一个永久存储的字典,本质是一个 XML 文件,将每个键及其对应的值以节点的形式存储到外存中。

值只支持 int 和 float 等基本类型。使用接口非常简单,只需要一行代码:

  CCUserDefault::sharedUserDefault()->setIntegerForKey("coin",coin - 1);

13.2 格式化存储

  对于稍微复杂的持久化情景,还是可以借助CCUserDefault 来满足我们的需求的

13.3 本地文件存储

  现在我们已经可以将复杂的数据类型存储到配置文件中了,但把全部数据集中在一个文件中显然不是一个明智的做法。如果将不同类别的数据(例如,NPC 的状态和玩家完成的成就)存储到不同的文件中,既可以提高效率,也方便我们查找。下面我们来看看如何实现它。

  不同平台间的文件系统不尽相同,为了简化操作、方便开发,Cocos2d-x引擎为我们提供了 CCFileUtil 类,用于实现获取路径和读取内容等功能

13.4 L XML 与 与  JSON

XML 和 JSON 都是当下流行的数据存储格式,它们的共同特点就是数据明文,十分易于阅读。XML 源自于 SGML,是一种标记性数据描述语言,而 JSON 则是一种轻量级数据交换格式,比 XML 更为简洁。鉴于 C++对 XML 的支持更为完善,Cocos2d-x选择了 XML 作为主要的文件存储格式

13.4 L XML 与 与  JSON

我们再次保存 UserRecord 对象,可以成功地将其存入一个指定的 XML 文档中

13.5 加密与解密

细心的读者应该已经注意到了,XML 的一个很严重的问题是明文存储,存储在外部的数据一旦被截获,就将直接暴露在攻击

者面前,小则篡改用户数据,大则泄露用户隐私信息。因此,对存储在文件中的信息加密不可忽视。对字符串的序列化和反序列化,只要将加密和解密分别放在这两个函数的最后,就可以完成对CCUserDefault 和 XML 文档的读、写及加密、解密。

void encode(string &str)

  {

  for(int i = 0; i <str.length(); i++) {

  int ch = str[i];

  ch = 0xff & (((ch & (1<< 7)) >> 7) & (ch << 1));

  str[i] = ch;

  }

  }

  void decode(string &str)

  {

  for(int i = 0; i <str.length(); i++) {

  int ch = str[i];

  ch = 0xff & (((ch &(1)) << 7) & (ch >> 1));

  str[i] = ch;

  }

  }

13.6 SQLite

SQLite 是移动设备上常用的一个嵌入式数据库,具有开源、轻量等特点,其源代码只有两个".c"文件和两个".h"文件,并

且已经包括了充分的注释说明。相比 MySQL 或者 SQL Server 这样的专业级数据库,甚至是比起同样轻量级的 Access, SQLite

的部署都可谓非常简单,只要将这 4 个文件导入工程中即可,这使得编译之后的 SQLite 非常小。

  SQLite 将数据库的数据存储在磁盘的单一文件中,并通过简单的外部接口提供 SQL 支持。

13.7 小结

  在这一章中,我们从一个小小的金币数入手,讨论了数据持久化的话题。我们尽量使用引擎提供的数据存储方法,以最大

可能地适应跨平台需求。这里介绍的存储方法本质上都是基于 XML 的,对于 1 MB 以下的存储规模来说,已经完全足够了,

而更大型的存储场景在目前的移动游戏中并不常见。在必要的时候,也可以使用 SQLite 来为数据持久化提速调优。 下面

总结一下本章的重要知识点。

  CCUserDefault:它是 Cocos2d-x 提供的一个十分便捷的本地存储解决方案。利用 CCUserDefault 类,可以存取简单的键值

数据。

  CCFileUtil:提供了对本地文件存取的基本功能。与 CCUserDefault 相比,CCFileUtil 更为底层,因此也适合用于存取更

加庞大的二进制文件。

  XML:Cocos2d-x 中常见的文件类型,用于存储配置数据或游戏资料。XML 的优势在于描述性极强,因此易于编辑。

  JSON:网络传输中常用的对象描述格式,与 JavaScript 兼容,在广告平台等网络交换数据的情形中十分常见。

  SQLite:轻量级的关系数据库,用于高速且安全地在本地存储数据。 在对性能要求较高时,可以考虑使用 SQLite 存储数据。

14.1 网络传输架构

直接使用 socket 传输。通信的两端都使用一个特定的端口传输数据,传输面向的是字节流或数据包,需要处理的细节比较

多,包括建立与关闭连接、设计与实现网络协议、维护传输通道的稳定性、监控数据传输速率等。因此,很多情况下,我

们需要在 socket 传输之上再根据游戏需求包装一层操作协议,以降低使用复杂度。

  使用 HTTP 传输。直接用数据包的形式将数据提交到服务端的特定 URL 中,服务器同样用数据包的形式返回响应数据,其中

大量的底层细节隐藏在了 HTTP 中。HTTP 作为非对等、主从式的传输,需要建立对应的服务器,幸运的是,Web 大潮催生了

一批稳定、成熟的 HTTP 服务器框架,让我们可以方便地建立一个 HTTP 服务器。

游戏中涉及网络部分的传输一般采用中心服务器的架构,服务器以 HTTP 服务形式建立 API 服务,各移动终端向中心服务器请求所需的 API 获得服务。

14.2 CURL

CURL 是 Cocos2d-x 推荐使用的网络传输库,随引擎代码一起分发了 CURL 的一份 CPP 实现。 它是免费开源的,而且支持 FTP、HTTP、LDAP等多种传输方式,同时横跨了 Windows、UNIX、Linux 平台,可以在各种主流的移动设备上良好工作。它存在两套核心的接口,分别对应两种不同的使用方式

单线程传输的阻塞方式:每次处理一个传输请求,会一直阻塞当前线程直到传输完成。非阻塞方式:允许同时提交一批传输请求,CURL 会开启后台线程处理这些请求,传输结果的返回也是异步的。

CURL 是纯 C 写成的网络库,其 API 全是函数形式,不涉及类

14.3 简单传输

  简单传输就是阻塞的单线程传输,使用方式相对简单,其接口也都是前缀为 curl_easy_的形式,涉及 4 个常用 API,如下

所示:

  CURL *curl_easy_init(void); //初始化一个传输

  CURLcode curl_easy_setopt(CURL*curl, CURLoption option, ...); //设置传输参数

  CURLcode curl_easy_perform(CURL*curl); //执行当前传输

  void curl_easy_cleanup(CURL*curl); //清理

我们用 curl_easy_setopt 设置了 CURL 的几个关键参数:设置发送 POST 请求、设置上传的数据、设置回调

函数,以及设置缓冲对象

14.4 非阻塞传输

CURL 是支持非阻塞传输的,而且还允许并行地进行多个网络请求,其接口主要是以 curl_multi 为前缀的系列的函数

14.5 用户记录

用户记录包括两方面的信息:一方面是用户对游戏的设置,诸如音效开关等;另一方面是用户的游戏进程信息,如用户的等级、金币数和成就等。这两方面信息本来是保存在本地设备的,我们将其上传到网络的服务器端,这带来的好处是用户可以随时在几台不同的设备上共用同一个捕鱼账户,自由地切换捕鱼的环境。

14.6 多人对战与同步问题

14.6.1 时间同步

一个比较好的解决方案是,在游戏一开始的时候,服务器和设备端都向同一个时间服务器获取时间戳,同步双方的时间。这样在双方传输指令或事件时,可以根据标准时间给出时间戳,保证动作按序执行

由一个随机数发生器控制的随机事件,进而由一个随机数种子控制两台设备间的同步。

14.7 校验

通过轻量级的算法,确保其独特性就足够

  在这一章中,我们探讨了网络相关的几个话题,从直接的数据传输到多人游戏中的多种同步问题。可以看到,涉及网络的游戏设计更追求异步、并发的编程思想。下面总结本章的重要知识点。

   socket、 HTTP: socket (套接字) 是操作系统提供的网络基础设施, 用于计算机在网络间建立数据连接; HTTP 是运行于 TCP/IP

应用层的一套文本传输协议,常用于网页传输。在游戏开发中,通常利用 socket 产生长连接来维持游戏的同步,然而现在

也有许多游戏采用 HTTP。

  CURL:CURL 是一套 URL 通信库,提供了 HTTP、FTP等协议的支持,因此可以利用 CURL 方便地建立 HTTP 连接。

  阻塞、非阻塞:网络传输是一个耗时的任务,因此运行时会阻碍其他代码的执行,直接在主线程中执行网络传输任务称作

阻塞式传输,而在新线程中异步地进行网络传输任务则称为非阻塞式传输。

  同步:网络游戏需要保持用户终端与服务器的数据一致,这个过程称为同步。有许多方案可以解决同步问题。

  本章我们选择了一种分布式的游戏逻辑计算框架,配合校验机制,可以有效降低带宽消耗。更深入的话题,如游戏大厅的

设计和服务器端的负载均衡等,已经超出了本书的讨论范围,感兴趣的读者不妨查阅相关资料进一步学习。

15.2 缓存机制:预加载与重复使用

缓存是指可以进行高速数据交换的存储器,它先于内存CPU交换数据。缓存的工作原理是当CPU要读取一个数据时,首先从CPU缓存中查找,找到就立即读取并送给CPU处理;没有找到,就从速率相对较慢的内存中读取并送给CPU处理,同时把这个数据所在的数据块调入缓存中,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存。

15.3.1 CCTextureCache

首先是最底层也最有效的纹理缓存 CCTextureCache,这里缓存的是加载到内存中的纹理资源,也就是图片资源。其原理是

对加入缓存的纹理资源进行一次引用,使其引用计数加一,保持不被清除,而 Cocos2d-x 的渲染机制是可以重复使用同一

份纹理在不同的场合进行绘制,从而达到重复使用,降低内存和 GPU 运算资源的开销的目的

一般情况下,我们应该在切换场景时清理缓存中的无用纹理,因为不同场景间使用的

纹理是不同的

15.3.2 CCSpriteFrameCache

第二个则是精灵框帧缓存。顾名思义, 这里缓存的是精灵框帧 CCSpriteFrame, 它主要服务于多张碎图合并出来的纹理图片。

这种纹理在一张大图中包含了多张小图,直接通过 CCTextureCache 引用会有诸多不便, 因而衍生出来精灵框帧的处理方式,

即把截取好的纹理信息保存在一个精灵框帧内,精灵通过切换不同的框帧来显示出不同的图案

15.3.3 CCAnimationCache

值得注意的是清理的顺序,应该先清理动画缓存,然后清理精灵帧,最后是纹理。按照引用层级由高到低,以保证释放引

用有效。

  void releaseCaches()

  {

 CCAnimationCache::purgeSharedAnimationCache();

 CCSpriteFrameCache::sharedSpriteFrameCache()->removeUnusedSpriteFrames();

 CCTextureCache::sharedTextureCache()->removeUnusedTextures();

  }

 

 

15.4 对象池机制:可回收与重复使用

缓存是一个字典,而对象池则是一个数组。得益于引用计数的内存管理机制,

只需要在数组上做适当封装就可以提供一个对象池的功能了

15.5 对象池实现(1 1 )

Cocos2d-x 高级开发教程

201

  Boost 是一个可移植、免费开源的C++库,提供了大量实用的开发组件,而且由于对跨平台和 C++标准的强调,其实现的功

能几乎不依赖于操作系统和标准库外的其他组件,因此可以在任何支持 C++的平台上运作良好。

 Boost 提供了一个对象池 object_pool,它位于 boost 库的"boost/pool/object/_pool.hpp"中。这是一个泛型的对象池,

能够针对指定类型的对象进行分配。一个对象池的声明和使用规范为如下结构:

15.6 落实到工厂方法

15.8 使用时机

缓存和对象池不仅仅适用于 C++这类偏底层的开发语言,在 C#和JavaScript 等语言中,内存的开销更大,

使用好缓存和对象池能有效减少不必要的系统内存管理,提升游戏执行效率

15.9 小结

  在这一章中,我们探讨了缓存和对象池这两个隐蔽而实用的组件,也在我们的《捕鱼达人》中加入了相应的优化,进一步

降低了内存和计算消耗。在 Cocos2d-x 中,缓存无处不在,然而在实际开发时,我们仍然需要合理地利用缓存与对象池这

两个组件,才能有效地提高游戏的性能。下面总结这一章的知识点。

  Cocos2d-x 内建的缓存:CCTextureCache用于缓存纹理,CCSpriteFrameCache 用于缓存精灵框帧文件,CCAnimationCache

用于缓存帧动画。

  对象池:对于频繁使用的对象,应当尽可能地减少分配内存和初始化的资源开销。因此,建立一个对象的容器,用于取出

或返还对象,这种机制称为对象池。对于大量使用对象的场合,对象池机制可以大大提高运行效率。

16.1 单线程的尴尬

Cocos2d-x 的并行机制。引擎内部实现了一个庞大的主循环,在每帧之间更新各个精灵的状态、执行动作、调用定时函数等,这些操作之间可以保证严格独立,互不干扰。不得不说,这是一个非常巧妙的机制,它用一个线程就实现了并发,尤其是将连续的动作变化切割为离散的状态更新时,利用帧间间隔刷新这些状态即实现了多个动作的模拟

16.2 pthread

pthread 是一套 POSIX 标准线程库,可以运行在各个平台上,包括 Android、iOS 和Windows,也是 Cocos2d-x 官方推荐的多线程库。它使用 C 语言开发,提供非常友好也足够简洁的开发接口。

 

16.3 线程安全

在 Cocos2d-x 中,最大的线程安全隐患是内存管理。引擎明确声明了 retain、release 和autorelease 三个方法都不是线程安全的。

16.4 线程间任务安排

Cocos2d-x 为我们提供了一个异步加载图片的接口,不会阻塞主线程,其内部正是采用了新建线程的办法。

使用并发编程的是网络通信。网络通信可能比文件读写要慢一个数量级。一般的网络通信库都会提供异步传输形式,我们只需要注意选择就好

16.6 小结

  透过多线程的并发编程,我们可以更充分地利用底层的计算资源,游戏的流畅度得到进一步的提升。从这一章的介绍可以

看到,对于常见的场景,引擎都为我们提供非常友好的辅助,帮助我们更容易实现诸如消息驱动等并发编程模型。下面总

结这一章的知识点。

  pthread:一套 POSIX 标准下的线程库,可以在各种系统下实现线程操作。正是由于可移植性极高,Cocos2d-x 提供了对

pthread 库的支持,因此我们不必添加额外的引用就可以直接使用它了。

  互斥锁:线程间常常出现不同步的问题,例如两个线程同时执行变量递增的操作,结果很可能是变量只递增一次。为了解

决这个问题,我们引入了互斥锁。在 ptherad 中,pthread_mutex_t 表示一个互斥锁。互斥锁是解决线程间同步问题的有力

工具,当一个线程进入互斥锁时,其他线程将无法请求互斥锁,直到互斥锁被释放,这就保护了关键代码不会被并行地执

行。

  异步加载图片:Cocos2d-x 提供了 CCTextureCache::addImageAsync(const char *path, CCObject *target,SEL_CallFuncO

selector)方法来实现图片的异步加载。本质上,它也是利用多线程来实现这个操作的,然而若我们只希望实现图片的异步

加载,就可以直接利用这个功能,没有必要自己完成整个线程操作了。

 

Cocos2d- -HTML5

游戏逻辑采用 JavaScript 实现。 JavaScript 是一种动态、 弱类型、基于原型 (prototype)

的脚本语言,可以直接在各种主流的浏览器上运行

 

之后是一些关于捕鱼达人的开发过程。如果做单机可以去仔细看下很有帮助

 

 

0 0