按:Cocos2d-x高级开发教程——制作自己的《捕鱼达人》、Cocos2d-x游戏开发技术精解这两本书,都是基于2.0.4版本讲解且于2013年6月份出版的。
Cocos2d-x游戏开发技术精解
刘剑卓 著
2013年6月第1版
chap2 Cocos2d-x引擎的开发环境
2.1跨平台的开发
2.2建立开发环境
2.2.1 PC开发环境
本书将会选择使用稳定的2.0.4引擎版本。
在本书的讲解中,将会以Windows下的Visual Studio 2010为主要的开发环境。
注意:读者最好不要选用VS 2008版本进行开发,因为引擎开发者已经明确未来不再支持此版本的开发环境。
2.2.1 PC开发环境
在引擎包中,提供了三个版本的项目工程。它们分别针对VS2008、VS2010以及VS2012。
注意:VS2010只支持Win7以后的操作系统。【XP也支持】
【
F:\hxhwin7\Cocos2d-xVC2010\cocos2d-2.0-x-2.0.3
用VC2012编译PC版通过
“常规|包含目录”去掉
D:\SixDivisions\Cocos2d-x\2.0-x-2.03_branch_231_NoScript\extensions\GUI\CCControlExtension
D:\cocos2d-2.1rc0-x-2.1.2\extensions
D:\cocos2d-2.1rc0-x-2.1.2\extensions\GUI
“常规|库目录”去掉
$(MSBuildProgramFiles32)\Microsoft SDKs\Windows\v7.1A\lib
2.1.5版本LAYER_CREATE_FUNC改为CREATE_FUNC
2.0.3引擎包的Bug:添加CCEditBox.h和.cpp到libExtensions工程,作如下修改并重新编译
#ifndef WIN32
CCEditBoxImpl* m_pEditBoxImpl;
#endif
“链接器|高级|导入库”
$(OutDir)$(TargetName).lib改为$(TargetDir)$(TargetName).lib
ADT+Builder/VC2012+Cocos2d-x 2.0.3可以正常编译捕鱼原型的Android版和PC版。
静态库libExtensions.lib
CCControl.win32工程|预处理定义:WIN32;_DEBUG;_WINDOWS;
工作目录
$(ProjectDir)..\Resources或$(SolutionDir)samples\CCControl\Resources
Android下路径文件名大小写敏感
】
2.2.2 Android开发环境
2.2.3 iOS开发环境
【
20150614问题:在Classes目录下新建gui目录、HelloWorldScene1.h、HelloWorldScene1.cpp
Xcode7重新编译,会出现如下链接失败的错误提示:
Undefined symbols for architecture armv7:
"HelloWorld1::scene()", referenced from:
AppDelegate::applicationDidFinishLaunching() in AppDelegate.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
解决办法:
选择TARGETS|Build Phases|Compile Sources(230 items)
点击+|Add Other...
选择HelloWorldScene1.cpp
在弹出的Choose options for adding these files:界面中选择Create groups,然后点击【Finish】
可以发现变为Compile Sources(231 items)
重新编译,就可以正常运行了。
在Xcode界面中,左侧为项目路径。
在iOS平台,系统库是以framework的形式引入的。开发者可以点击页面下的“+”按钮进行添加。
而由引擎编译而来的程序库,则是以“.a”为文件后缀的,比如所有游戏项目都需要引入libcocos2dx.a库作为引擎。
除此之外读者还要设置项目依赖关系。
2.3.2 Objective-C与C++的混合编译
为了介绍方便,以后的内容就简写为OC。
编译器允许用户在同一个源文件里自由地混合使用C++和Objective-C,混编后的语言叫Objective-C++。
说明:尽量避免在一个文件中写入两种编程语言的类声明,这很容易造成混乱,不宜阅读。
在项目路径proj.ios下,存放可以混合编写的代码文件。这些文件的后缀为“.mm”。Xcode编译器允许开发者在同一个源文件里自由地混合使用C++和OC,混合后的语言叫Objective-C++,简称为OC++。编译器就是通过文件后缀名来标识OC++文件的。在OC++文件中,可以用C++代码调用方法,也可以从Objective-C调用方法。在两种语言里对象都是指针,可以在任何地方使用。
AppController.mm文件是引擎中应用开启的类。它负责管理游戏应用运行周期。在这里作为例子,主要是为读者介绍OC++的编程方式。
注意:OC++没有为OC类增加C++的功能,也没有为C++类增加OC的功能。例如,你不能用OC语法调用C++对象,也不能为OC对象增加构造函数和析构函数,也不能将this和self互相替换使用。类的体系结构是独立的。C++类不能继承OC类,OC类也不能继承C++类。另外,多语言异常处理是不支持的。也就是说,一个OC抛出的异常不能被C++代码捕获,反过来C++代码抛出的异常也不能被OC代码捕获。
再来看看iOS平台入口文件的内容。
main
//初始化自动释放池
//启动应用程序
代码来自项目路径proj.ios\main.m
这就是iOS应用程序的入口,同样是由引擎为开发者准备妥当了。这段代码也是只有在iOS项目中才能发挥作用。
2.4.2引擎应用入口
在项目路径Classes中,存在一个AppDelegate类文件。此文件的命名是OC编程语言的风格。在此代码文件中,就是与引擎应用生命周期有关的函数。所谓的生命周期,就是游戏应用开始与结束的过程。在此过程中,引擎应用会存在许多的状态。
引擎应用的生命周期
//应用开启的入口
applicationDidFinishLaunching
//当应用进入待机状态时调用的函数
applicationDidEnterBackground
//当应用从待机恢复
applicationWillEnterForeground
引擎中没有提供多款应用同时运行的情况。当引擎启动之后,就会执行唯一的游戏应用。引擎将会调用applicationDidFinishLaunching函数作为游戏应用开启的入口。在引擎应用运行的状态中,还存在一个待机状态。这就是当手机来电话或者退入后台时,游戏进入了暂停状态,同时还要暂停音乐的播放。
】
2.3引擎中的混合编译
2.3.1 Java与C++的混合编译
2.3.2 Objective-C与C++的混合编译
2.4引擎的起点
2.4.1应用程序入口
2.4.2引擎应用入口
2.5丰富的示例程序
2.5.1 TestCpp示例项目
2.5.2脚本示例项目
2.5.3 MoonWarriors示例项目
2.6本章小结
chap3 引擎的核心——渲染框架
3.1基本框架
3.1.1引擎的位置
3.1.2根源种子
3.1.3子类结构
3.2渲染框架
3.2.1框架结构
导演、场景、层次和精灵。它们对应了引擎当中的四个类:CCDirector、CCScene、CCLayer和CCSprite。
3.2.2摄像机类CCCamera
在每一个CCNode对象中,都存在一个摄像机对象CCCamera。
注意:摄像机控制与物体属性控制,读者只能二选其一。如果同时使用,将会导致引擎的坐标系出现错误。
开发者在使用摄像机时还有一些注意事项。
(1)一些特殊的CCNode对象,比如CCParallaxNode、CCParticle依据的是世界坐标系,它们将不会受到摄像机影响。
(2)当精灵对象CCSprite是来自精灵集合CCSpriteBatchNode时,也将不受摄像机的影响。
(3)如果开发者只需要二维的画面效果,则无需控制摄像机。摄像机只是为了体现三维效果而准备的。
3.2.3导演类CCDirector
说明:单例模式也存在一个缺点,就是随处可见的调用关系,很容易导致代码结构的混乱。
说明:在不同的平台,因为显示设备不同,显示窗口(EAGLView)初始化的方式略有不同。
在类CCDirector的代码中,为了便于管理场景对象,采用了队列[栈:先进后出]方式来管理场景对象。
/* The running scene */
CCScene *m_pRunningScene;
/* will be the next 'runningScene' in the next frame
nextScene is a weak reference. */
CCScene *m_pNextScene;
/* scheduled scenes */
CCArray* m_pobScenesStack;
在类CCDirector的源代码中,runningScene_表示当前正在显示的场景,nextScene表示下一个将要显示的场景。而用于储存场景队列的对象则是一个动态可变数组sceneStack_。
3.2.4场景类CCScene
场景通常不包含游戏逻辑,仅仅是做为一个容器,将不同的层组合到一起,最终呈现给玩家一个完整的画面。它代表了游戏运行中的一个状态,其包含的图层是更小一级的容器。图层中包含了游戏逻辑、用户响应以及精灵对象。
所有与显示有关的类几乎都是继承自CCNode。
说明:类CCNode中提供了更改父节点的函数setParent()。
3.2.5图层类CCLayer
图层也是渲染框架中很重要的内容。场景类用来划分游戏状态。图层就用来划分游戏画面。通常图层的尺寸会与屏幕尺寸一致。
图层之间可以叠加,也可以彼此包含。
为了让不同的层能够叠加来产生整体的画面效果,图层中包含了一些透明的区域。不仅如此,图层叠加还可以进行颜色混合。引擎当中就通过类CCLayer提供了上述图层的功能。引擎中图层对象包含了三个功能。
(1)接受用户操作,比如触屏、重力加速度计的信息。
(2)作为游戏内容元素的容器,用于显示游戏画面、承载精灵类、字体文本等对象。
(3)填充背景游戏背景颜色。
在CCLayer头文件的源代码中,还存在一个函数node。它的作用与函数create一样,只不过是旧版本留下来的程序接口。用不了多久,它就会被弃用。
类CCLayer中也没有实际的绘制内容。它的主要作用在于建立用户交互的功能。
注意:默认情况下,图层是不接受用户操作的。开发者需要调用函数来开启图层将要接受的用户操作,比如setTouchEnabled、setAccelerometerEnabled以及isKeypadEnabled。
引擎按照游戏内容提供了一些特殊的图层类。
1、CCLayerColor颜色层
2、CCMenu菜单图层
3、CCMultiplexLayer复合层
3.2.6精灵类CCSprite
精灵类CCSprite继承自CCNode,这是为了满足渲染框架的结构。
精灵类还继承了两个协议类,它们分别为CCRGBAProtocol和CCTextureProtocol。这两个协议类是用于处理精灵类中的纹理图片,前者是负责颜色的管理,后者是用于纹理图片的管理。
3.2.7精灵集合类CCSpriteBatchNode
精灵集合类CCSpriteBatchNode,它的对象常常包含了许多的精灵对象。这些精灵对象具有一个共同的特点,那就是使用同一张纹理图片。虽然是同一张纹理图片,但每个精灵所用的矩形区域不一样。
3.2.8精灵帧缓冲CCSpriteFrameCache
精灵帧缓冲CCSpriteFrameCache就是一个存放精灵帧CCSpriteFrame对象的缓冲池。
它甚至都不是CCNode子类。
3.2.9 Zwoptex纹理编辑器
3.3文字与字体
3.3.1 TTF类型标签(CCLabelTTF)
3.3.2 BMFont标签类(CCLabelBMFont)
3.3.3 Atlas标签类(CCLabelAtlas)
3.4菜单按钮
3.5几何绘制DrawPrimitives
3.6 CocosBuilder编辑器
3.6.1 CocosBuilder使用指南
3.6.2引擎中的应用
3.7本章小结
chap4 动作功能
本章是Cocos2d-x引擎中最有魅力的部分。
4.1概述
4.2动作基类
4.2.1动作类的继承关系
4.2.2动作基类CCAction的成员函数
4.2.3 类CCNode中与动作有关的函数
4.3时间动作
动作基类CCAction首要的继承子类,其实就是CCFiniteTimeAction类。
从字面的意思来理解是与时间相关的动作类,因此在类CCFiniteTimeAction的属性中就加入了一个与时间有关的成员变量,同时提供了如下两个操作函数。
getDuration
setDuration
与时间相关的动作被划分为两类:即时动作和延时动作。
4.3.1即时动作这些继承自类CCActionInstant的子类都是即时动作。这意味着当此类动作被作用在精灵或者其他的CCNode对象时,将会立即被执行,发挥其动作的作用。
现在先来看看每一个即时动作所发挥的作用吧!
6、函数调用动作(CCCallFunc、CCCallFuncND、CCCallFuncN以及CCCallFuncO)
与前几个类的作用一样,函数调用的动作也经常用在动作序列当中。
此系列包含了四个动作类。类CCCallFunc是另外三个的父类。它们只是因为传递的参数不同,而有所区分。
CCCallFunc类的调用函数没有任何参数。
CCCallFuncND类的调用函数有两个参数,分别为CCNode对象和Data数据指针。
虽然有四个函数调用的动作类,但是它们的区别也仅仅是在参数上。其他部分的作用则是一样的。
【
void CBulletManage::Shoot(int iPlayerID, CCPoint ptTouch, CCPoint ptShip, float fRotation, bool bReBound)
{
if (NULL == s_AllBullet)
CCAssert(0, "请先调用 CBulletManage::Init(...) 函数!");
// 子弹间隔延迟控制
DWORD dwNow = ::timeGetTime();
if (dwNow < m_LastShootTime+d_Max_Shoot_Delay)
return;
m_LastShootTime = dwNow;
CCPoint ptMuzzle = CountTwoPointOneCoordinate(ptTouch, ptShip, 150); // 计算枪口位置
CBullet * pBullet = CBullet::create( CCString::createWithFormat("bullet_01_00000.png")->getCString() );
pBullet->autorelease();
pBullet->setRotation( fRotation );
pBullet->setIsCatching( false );
pBullet->setIsReBound( bReBound );
pBullet->setAnchorPoint(CCPoint(0.5, 1.0));
pBullet->setPosition( ptMuzzle );
pBullet->setPlayerID( iPlayerID );
float fDistance = sqrtf( pow(ptTouch.x - ptMuzzle.x, 2) + pow(ptTouch.y - ptMuzzle.y, 2) ) ; // 两点间距离
float fTime = fDistance / d_Bullet_Speed; // 子弹需要时间
CCActionInterval *pMove = CCMoveTo::create( fTime, ptTouch );
CCActionInstant * pCallBack = CCCallFuncND::create(pBullet, callfuncND_selector( CBullet::FinishAnimation ), (void *)&ptTouch);
CCActionInterval * pBulletRun = CCSequence::create(pMove, pCallBack, NULL);
pBullet->runAction( pBulletRun );
CCArray* pArray = CCArray::create();
for (int i=0; i<10; i++)
{
CCString * pStrBuffer = CCString::createWithFormat( "bullet_01_%05d.png", i);
CCSpriteFrame* pSpriteFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName( pStrBuffer->getCString() );
pArray->addObject( pSpriteFrame );
}
pBullet->runAction( CCRepeatForever::create( CCAnimate::create( CCAnimation::createWithSpriteFrames( pArray, 0.1f ) ) ) );
s_AllBullet->addChild( pBullet );
}
void CBullet::FinishAnimation(CCNode* pSender, void* data)
{
stopAllActions();
setVisible( false );
CCPoint * pTouch = (CCPoint *)data;
// 鱼网
CCArray *pArray = CCArray::create();
for (int i=0; i<10; i++)
{
CCString *pStrBuffer = CCString::createWithFormat("FishNet_01_%05d.png", i);
CCSpriteFrame * pFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName( pStrBuffer->getCString() );
pArray->addObject( pFrame );
}
CCActionInterval * pAnimate = CCAnimate::create( CCAnimation::createWithSpriteFrames( pArray, 0.05f ) );
setAnchorPoint(CCPoint(0.5,0.5));
// 结束自己
CCActionInstant *pCallBack = CCCallFunc::create(this, callfunc_selector( CBullet::Release ));
runAction( CCSequence::create(CCShow::create(), pAnimate, pCallBack, NULL) );
}
void CBullet::Release()
{
removeAllChildren();
removeFromParent();
}
】
4.3.2持续动作
按照CCNode类的对象属性来划分,我们可以将上述动作分为以下几个类型。
1、与位置有关的持续动作
CCMoveBy、CCMoveTo、CCJumpBy、CCJumpTo、CCBezierBy、CCBezierTo,上述三对动作类在执行时都会通过修改类CCNode对象中的位置属性来发挥执行效果。
To代表了具体移动的结果。
有By的动作,表示动作执行的程度或者速率。
之后,还会接触许多成对出现、采用类似命名规则的动作类。
先从CCMoveTo和CCMoveBy开始。
在创建此动作类的对象时,第一个参数就是时间,第二个参数就是目标点或者移动距离。
【
/// 划船动画
void OnRowingAnimation(float fTime);
void OnRowingCompleteAnimation();
void Player::OnRowingAnimation(float fTime)
{
unschedule( schedule_selector(Player::OnRowingAnimation) );
float fOffset = 110.0f;
if (PLAYER_COUNT/2 <= m_PlayerPT)
fOffset = -110.0f;
CCSprite * pShip = (CCSprite *)getChildByTag( t_Ship );
pShip->setVisible( true );
pShip->setPositionY( pShip->getPositionY()+fOffset );
CCActionInterval * pMove = CCMoveBy::create(1.5f, ccp(0, -fOffset));
CCActionInstant * pComplete = CCCallFunc::create(this, callfunc_selector(Player::OnRowingCompleteAnimation));
CCActionInterval * pShipAnimation = CCSequence::create(pMove, pComplete, NULL);
pShip->runAction( pShipAnimation );
}
void Player::OnRowingCompleteAnimation()
{
// 玩家划船进入,未到达捕鱼现场,除了炮与船其他信息暂隐藏。
CCSprite *pChild = NULL;
CCObject* pObj = NULL;
CCARRAY_FOREACH(getChildren(), pObj)
{
pChild = (CCSprite*)pObj;
pChild->setVisible( true );
}
}
】
在Cocos2d-x引擎中存在一个专门用来表示[三阶]贝塞尔曲线的配置类ccBezierConfig。此类的对象包含了三个CCPoint对象。它们分别用来表示贝塞尔曲线的三个属性点。
通过四个点P0、P1、P2、P3,就能表示出一条[三阶]贝塞尔曲线。在ccBezierConfig对象中保存了P1、P2、P3这三个点属性。而P0则是CCNode对象的初始位置属性。
3、旋转动作类
CCRotateBy和CCRotateTo,这对动作类是通过修改CCNode对象的角度属性来达到旋转的效果。
4.4组合动作类
在游戏当中,一个角色通常并不是只有一个动作在执行中的。
Cocos2d-x引擎提供了一些将其他动作进行组合的动作类。这些类就好比是一个动作对象的容器。
4.4.1序列动作类CCSequence
序列动作类CCSequence是从CCIntervalAction类派生而来的,因此它也是一个与时间有关的类。
每一个组合动作类都有其特定的执行规则。序列动作类就是将放置在自身序列当中的若干个动作类对象,按照前后的线性顺序逐个执行。
以action开头的的是旧版本中延续下来的函数,在不久的将来就会消失。而create函数则是2.0版本中简化后的创建函数。
如果需要使用序列动作,那么至少要准备两个动作对象。
创建函数的参数恰好就是动作类的对象。
4.4.2同步动作类CCSpawn
组合动作只是一群特殊的持续动作类。
类CCSpawn同样是来自CCIntervalAction类的派生,它的创建函数也与序列动作类CCSequence非常类似。
该类与序列动作类CCSequence的区别就是,它使得CCNode对象可以同时执行若干个动作。
【
void CFish::addPath()
{
switch(rand() % 7)
{
case 0:
this->moveWithParabola(this, ccp(1200, 200), ccp(-500, 800), 0.0f, 20.0f, rand()%10+15);
break;
case 1:
this->moveWithParabola(this, ccp(-200, 300), ccp(1300, 400), 180.0, 170.0, rand()%10+18);
break;
case 2:
this->moveWithParabola(this, ccp(-200, 300), ccp(1000, -200), 190.0, 200.0, rand()%10+18);
break;
case 3:
this->moveWithParabola(this, ccp(1300, 400), ccp(-200, 300), 10.0, 5.0, rand()%10+18);
break;
case 4:
this->moveWithParabola(this, ccp(400, -1200), ccp(600, 1000), 90.0, 93.0, rand()%10+18);
break;
case 5:
this->moveWithParabola(this, ccp(600, 1000), ccp(400, -200), -70.0, -80.0, rand()%10+18);
break;
case 6:
this->moveWithParabola(this, ccp(1200, 2100), ccp(-200, 300), 30.0, -30.0, rand()%10+18);
break;
}
}
void CFish::moveWithParabola(cocos2d::CCSprite* mSprite, cocos2d::CCPoint startP, cocos2d::CCPoint endP, float startAngle, float endAngle, float time)
{
float sx = startP.x;
float sy = startP.y;
float ex =endP.x+rand()%50;
float ey =endP.y+rand()%150;
float h = mSprite->getContentSize().height * 0.5f;
CCPoint pos = CCPointMake(sx - 200 + rand()%400, sy -200 + rand() %400);
mSprite->setPosition(pos);
mSprite->setRotation(startAngle);
// 贝塞尔曲线
ccBezierConfig bezier;
// 控制点1(起点)
bezier.controlPoint_1 = ccp(sx, sy);
// 控制点2
bezier.controlPoint_2 = ccp(sx+(ex-sx)*0.5, sy+(ey-sy)*0.5+rand()%300);
// 终点
bezier.endPosition = ccp(endP.x-30, endP.y+h);
CCBezierTo* actionMove = CCBezierTo::create(time, bezier);
CCRotateTo* actionRotate = CCRotateTo::create(time, endAngle);
CCFiniteTimeAction* action = CCSpawn::create(actionMove, actionRotate, 0);
CCFiniteTimeAction* sq = CCSequence::create(action,CCCallFunc::create(this, callfunc_selector(CFish::removeSelf)), 0);
mSprite->runAction(sq);
}
】
4.4.3重复动作类CCRepeat & CCRepeatForever
类CCRepeat和类CCRepeatForever也是开发者经常会使用的组合动作类。它是用来重复某一个动作对象的。此处的动作对象,可以是单一的基本动作对象,也可以是组合动作对象。
此动作类的执行效果就是能够将一个动作对象重复许多次或者永无止境地循环执行。
它可以使用任何动作对象作为参数,其中包含了单个的基本动作对象,也包含了前面介绍过的序列动作以及同步动作。
对于类CCRepeatForever而言,其创建函数没有第二个参数,因为它是永不停止的执行动作对象。
类CCRepeatForever并不是类CCRepeat的子类,虽然它们有着类似的功能,但它是由类CCActionInterval直接派生的。
【
void CFashManage::addOneFish(CFish::Fish_Type eType/*=Fish_1*/)
{
if (NULL == s_AllFish)
CCAssert(0, "请先调用 CFashManage::Init(...) 函数初始化");
char charBuffer[256] = {0};
CCArray* fish01 = CCArray::create();
// 鱼帧
for(int i = 1; i < d_Fish_Frame_Count; i++)
{
memset(charBuffer, 0, sizeof(charBuffer));
_snprintf(charBuffer, sizeof(charBuffer), "fish0%d_0%d.png", eType, i);
CCSpriteFrame* spriteFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(charBuffer);
fish01->addObject(spriteFrame);
}
// 由帧缓存生成action,帧延迟0.1f
CCActionInterval * pFishAction = CCRepeatForever::create(CCAnimate::create(CCAnimation::createWithSpriteFrames(fish01, 0.1f)));
// 通过起始帧生成鱼实体
CFish* fish = CFish::createWithSpriteFrameName(CCString::createWithFormat("fish0%d_0%d.png", eType, 1)->getCString());
fish->setIsCatching( false );
fish->setTag( eType );
fish->runAction( pFishAction );
fish->addPath();
s_AllFish->addChild( fish );
}
】
4.5可变速度类CCEaseAction
4.5.1CCEaseIn、CCEaseOut、CCEaseInOut
4.5.2EaseSineIn、EaseSineOut、EaseSineInOut
4.5.3CCEaseBackIn、CCEaseBackOut、CCEaseBackInOut
4.5.4EaseExponentialIn、EaseExponentialOut、EaseExponentialInOut
4.5.5CCEaseBounceIn、CCEaseBounceOut、CCEaseBounceInOut
4.5.6CCEaseElasticIn、CCEaseElasticOut、CCEaseElasticInOut
4.6速度类CCSpeed
4.7延迟动作类CCDelay
4.8跟随动作类CCFollow
4.9扩展动作类
4.9.1概述
4.9.2翻页动作类CCPageTurn3D
4.9.3波纹动作CCWaves3D
4.9.4格子动作类CCGridAction
4.10动画动作类
在最后的内容中,留给大家一个最重要的动作类,这个动作类就是动画动作。它同样是来继承自持续动作类CCActionInterval。
没有哪个游戏是没有动画的。
下面的内容,也将不仅仅局限于动画动作,而是将动画动作的实现机制以及引擎当中对其优化的技术都展现出来。
4.10.1精灵帧
最新版的Cocos2d-x引擎专门设计了类CCAnimationFrame为动画帧对象。在旧版本的引擎当中,开发者更习惯直接使用类CCSpriteFrame(精灵帧)。
【
CFish* CFish::createWithSpriteFrameName(const char *pszSpriteFrameName)
{
CCSpriteFrame *pFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(pszSpriteFrameName);
char msg[256] = {0};
sprintf(msg, "Invalid spriteFrameName: %s", pszSpriteFrameName);
CCAssert(pFrame != NULL, msg);
return createWithSpriteFrame(pFrame);
}
】
4.10.2精灵帧缓冲
所谓的精灵帧缓冲类CCSpriteFrameCache,其实就是一个存放精灵帧对象的缓冲池。在引擎运行时,它将会作为一个单例对象。
它的目的在于提升动画的性能。
【
void CFashManage::Init(CCNode * pNode)
{
if (NULL == s_AllFish)
{
for (int i=0; i<d_Fish_Plist_File_Count; i++)
{
CCString *pListName = CCString::createWithFormat("./play/fish%02d%s",i+1,".plist");
CCString *pBatchName = CCString::createWithFormat("./play/fish%02d%s",i+1,".png");
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(pListName->getCString());
s_AllFish = CCSpriteBatchNode::create(pBatchName->getCString());
pNode->addChild( s_AllFish );
}
}
while(NULL != s_AllFish && sharedFish()->getChildren()->count() < d_Fish_Max_Count)
{
// 鱼种
CFish::Fish_Type fishTytpe = (CFish::Fish_Type)(rand() % (CFish::Fish_Type_Count-1) + 1);
addOneFish( fishTytpe );
}
}
】
4.10.3动画类
动画类CCAnimation就是用在精灵之上的动画对象。它包含了一系列的动画帧以及动画帧之间的播放间隔。所以说动画类的对象是将动画帧元素组织起来的、准备播放的集合对象。它决定了动画帧的播放顺序以及时间间隔。
4.10.4动画动作
动画动作虽然也是持续动作类CCActionInterval的一个子类,但是其实现机制却是最为复杂的。
CCAnimation并不是能够执行的动作。它只是作为一个精灵帧的有序集合。同时,它也存有一些与动画相关的播放属性。
Cocos2d-x引擎也提供了一个专门用来执行动画的持续动作类,那就是类CCAnimate。
动画动作也是存在反序动作的。它的执行效果看上去就像是录像的倒带播放。
【
/// 设置炮类型
void SetGunType(bool bAdd);
/// 发炮动作
void EmitGun(CCPoint pt);
/// 旋转炮
/// @return 返回旋转的角度值
float whirlGun(CCPoint &pt);
CCAnimation * m_pGunAnimation;
void Player::SetGunType(bool bAdd)
{
if ( bAdd )
{
m_iGunType = m_iGunType % d_Gun_Count+1;
}
else
{
m_iGunType = (m_iGunType-1) % d_Gun_Count;
m_iGunType = (0 >= m_iGunType) ? d_Gun_Count : m_iGunType;
}
char cBuffer[64]="";
_snprintf(cBuffer, sizeof(cBuffer), "gun_%d_00000.png", m_iGunType);
CCSprite * pShip = (CCSprite *)getChildByTag( t_Ship );
CCSprite * pGun = (CCSprite *)pShip->getChildByTag( t_Gun );
pGun->setDisplayFrame( CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName( cBuffer ) );
// 创建发炮动画
CCArray *pGunArray = CCArray::create();
for (int i=0; i<d_Gun_Frame_Count; i++)
{
char cBuffer[64]="";
_snprintf(cBuffer, sizeof(cBuffer), "gun_%d_0000%d.png", m_iGunType, i);
CCSpriteFrame* pGunFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName( cBuffer );
pGunArray->addObject( pGunFrame );
}
if (NULL != m_pGunAnimation)
{
m_pGunAnimation->release();
m_pGunAnimation = NULL;
}
m_pGunAnimation = CCAnimation::createWithSpriteFrames( pGunArray, 0.05f );
m_pGunAnimation->retain();
}
void Player::EmitGun(CCPoint pt)
{
// 设置炮旋转
CCSprite * pShip = (CCSprite *)getChildByTag( t_Ship );
CCSprite * pGun = (CCSprite *)pShip->getChildByTag( t_Gun );
float angle = whirlGun( pt );
CCLog("xy:(%f,%f),(%f,%f), angle:%f\r", pt.x, pt.y, pGun->getPosition().x, pGun->getPosition().y, angle);
pGun->runAction( CCAnimate::create(m_pGunAnimation) );
CCPoint ptBullet[PLAYER_COUNT] = {ccp(0, 0), ccp(0, 0), ccp(0, 0), ccp(1, 17)};
ptBullet[m_PlayerPT].x += pShip->getPositionX();
ptBullet[m_PlayerPT].y += pShip->getPositionY();
m_BulletManage->Shoot(getPlayerPT(), pt, ptBullet[m_PlayerPT], pGun->getRotation(), false);
CCLog("ZOrder: Bullet=%d, pShip=%d, pGun=%d", m_BulletManage->sharedBullet()->getZOrder(), pShip->getZOrder(), pGun->getZOrder());
}
float Player::whirlGun(CCPoint & pt)
{
// 设置炮旋转
CCSprite * pShip = (CCSprite *)getChildByTag( t_Ship );
CCSprite * pGun = (CCSprite *)pShip->getChildByTag( t_Gun );
float angle = (pt.y - pShip->getPosition().y)/(pt.x - pShip->getPosition().x);
angle = atanf(angle)/M_PI*180;
angle = (0 > angle) ? -(90 + angle) : 90 - angle;
// 单击炮水平线以下,旋转90,-90
if (pt.y < pShip->getPositionY())
{
angle = (pt.x >= pShip->getPositionX()) ? 90 : -90;
pt.y = pShip->getPositionY()+15;
}
pGun->setRotation( angle );
return angle;
}
】
4.11动画编辑器
4.11.1概述
4.11.2CocosBuildr编辑器中的精灵动画
4.11.3SpriteX草莓编辑器
4.11.4MotionWelder动画编辑器
4.12样例程序
4.13本章小结
chap5 用户交互
5.1概述
5.2玩家交互信息
5.3触摸操作的处理机制
以iOS为例,引擎使用了EAGLView来处理画面显示与用户交互。而根据iOS SDK的设计,它为开发者提供了四个用于响应用户操作的函数。
上面代码,来自于引擎目录当中的\cocos2dx\platform\ios\EAGLView.mm。
它的功能是将iOS平台中的用户操作信息传递到了引擎当中。然后,让引擎的用户操作机制来处理。
进入到引擎源代码中的platform\目录,就会看到针对不同平台而编写的CCEGLView文件。
在引擎当中,用户操作将会经过接收、分发、处理三个步骤。
5.4接收操作
引擎开发者设计了一个专门存储用户操作的类CCTouch。
每个类CCTouch的对象都包含了一个用户操作信息。其属性当中存储了id标识、x坐标、y坐标。这就是类CCTouch的作用。它能够将采集自不同平台的触摸信息,保存为统一的格式。
5.5分发机制
代码是来自于CCEGLViewProtoco.cpp文件的代码片段。函数handleTouchesBegin就是将各个平台的用户操作数据转化为引擎所用的用户操作数据。
在代码中,读者可以看到新创建的CCTouch对象。它们作为用户操作信息,被传递了一个m_pDelegate对象。这是一个用户操作的分发对象。
引擎专门提供了一个负责分发用户操作信息的类。类CCTouchDispatcher的作用就是响应对象分发用户操作信息,即类CCTouch对象。
所谓分发,就是类CCTouchDispatcher不仅仅只发送一次。它会向所有需要响应的对象发送用户操作信息。
类CCTouchDispatcher被一个单例对象持有成为了一个成员变量。
类CCTouchDelegate就是分发器发送用户操作信息的目标对象。我们姑且将此类型的对象称为用户操作委托对象吧!
类CCTouchDelegate具有三个子类:CCLayer、CCStandardTouchDelegate、CCTargetTouchDelegate。
5.6处理响应
类CCLayer是一个标准的委托对象。
作为目标委托对象,其响应函数所传递的参数都为单一的用户操作信息。而标准委托对象,其响应函数所传递的参数则是一个用户信息的集合。说的明白一点,目标委托对象是用来响应单点触摸的,而标准委托对象是用来响应多点触摸的。
说明:多点触碰是可以包含单点触碰的,因此标准委托对象可以替代目标委托对象的功能。
单点触摸却无法实现如捏合、分离的用户操作。
函数ccTouchBegan是唯一一个具有返回值的。如果返回值为真值,则分发器将会停止此次事件的分发。换句话说,在分发器中后续的委托对象将不能再接收到此次用户操作数据。
5.7多点触碰
5.8加速计的响应函数
在Cocos2d-x引擎当中,使用了类CCAccelerometer来存储加速度计的信息。类CCAccelerometer的作用与用户操作的分发器类似。区别在于用户操作的分发器可以拥有很多的委托对象。而加速度计只存在一个委托对象。
类CCAccelerometerDelegate就是加速度计的委托对象。开发者同样需要采用注册的方式来使用它。
5.9本章小结
chap9 文件操作模块
9.1概述
9.2引擎文件操作模块
9.3读取文件
9.4写入文件
9.5游戏中用户数据
9.5.1游戏中的用户数据
9.5.2用户数据的基本类型
9.5.3读取与写入操作
9.6示例程序
9.7本章小结
chap10 内存管理机制
10.1内存管理概述
10.2引用计数
10.3自动释放池
10.3.1使用方法
10.3.2实现原理
10.4管理模式
10.4.1引擎当中的应用
10.4.2缓冲区
10.5日志调试方式
10.6本章小结
chap14
引擎之外的附加功能
14.1概述
14.2网络通信支持
14.2.1HTTP介绍
14.2.2 curl库(libcurl)
14.2.3 HTTP在引擎中的运用
14.2.4 HTTP示例项目
14.2.5 Socket的介绍
14.2.6 BSD Socket在引擎中的应用
14.3收费模式
14.3.1下载计费
14.3.2内置计费
14.3.3广告版本
14.4社交网络游戏在游戏中的应用
14.4.1 Game Center
14.4.2 OpenFeint
14.5数据分析
14.5.1 Flurry介绍
14.5.2友盟