存档

2012年10月 的存档

知易游戏开发教程cocos2d-x移植版007

2012年10月31日 没有评论

原文:http://www.cnblogs.com/cocos2d-x/archive/2012/06/10/2544260.html

在新的一章中,知易带来了粒子系统的相关知识。如果你不清楚粒子系统到底是怎么一回事,那就赶快来看一下吧。

为什么需要粒子系统

在示例6中,我们通过一组图片实现了炮弹的爆炸效果,这是游戏开发中常见的表现手法之一,它的理论基础就是动画。但是如果我们希望模拟更加真实的效果,比如烟雾、闪电、雨雪、火焰等,我们就需要使用另外一个工具——粒子系统。

粒子系统是计算机图形学中模拟模糊现象的技术之一。一般来说,为了表现某一现象,会有大量的微小粒子被创建出来。从宏观上讲,这些粒子是一个整体,它们是确定的可控的;从微观上讲,这些粒子各自为战,每一个个体都保持着自身的随机性。

粒子系统能让你的玩家眼前一亮,整个游戏都灵动起来。

科普一下

1983年先驱William T. Reeves在《Computer Graphics》上发表了《Particle
Systems: A Technique for Modeling a Class of Fuzzy Objects
》一文。他向人们介绍了在《Star Trek II: The Wrath of Khan》中使用的粒子系统,被认为是最早掌握此系统的人之一。

维基百科上写的是William "Bill" Reeves,论文上署名的是William T. Reeves,我想应该是同一个人吧,卢卡斯影业的大牛,皮克斯的创始员工之一。

有兴趣的朋友可以去拜读一下大师的文章,十多页,我水平有限,看得很慢,只看了个开头。

使用粒子系统

额,这方面的知识还真是繁杂啊,为了避免一知半解误导大家,所以关于粒子系统的详细介绍请参看知易原教程第7章

http://blog.sina.com.cn/s/blog_537cabb80100i0qu.html

或者老G的学习笔记14

http://4137613.blog.51cto.com/4127613/767801

让我们跳过枯燥的原理和参数介绍,关注最基本的使用。

1)基本用法

像其他类一样,cocos2d-x的粒子效果类也提供了静态函数以方便我们创建其实例。

知易游戏开发教程cocos2d-x移植版007
 1     emitter2 = CCParticleSun::node();
 2     emitter2->retain();
 3     emitter2->setTexture(CCTextureCache::sharedTextureCache()->addImage(s_fire));
 4     emitter2->setPosVar(ccp(3, 3));
 5     emitter2->setStartSize(20.0f);
 6     emitter2->setStartSizeVar(2.0f);
 7     emitter2->setEndSize(0.0f);
 8     emitter2->setEndSizeVar(0.0f);
 9     ccColor4F endColor2 = { 0.0f, 0.0f, 0.0f, 0.1f };
10     emitter2->setEndColor(endColor2);
11     emitter2->stopSystem();
12     addChild(emitter2);
知易游戏开发教程cocos2d-x移植版007

使用这种方法的缺点是我们不能指定粒子的数量,因为它在init内固定了这个值。

知易游戏开发教程cocos2d-x移植版007
 1 class CC_DLL CCParticleSun : public ARCH_OPTIMAL_PARTICLE_SYSTEM
 2 {
 3 public:
 4     CCParticleSun(){}
 5     virtual ~CCParticleSun(){}
 6     // 使用node方法创建的对象实例默认350个粒子
 7     bool init(){ return initWithTotalParticles(350); }
 8     virtual bool initWithTotalParticles(unsigned int numberOfParticles);
 9     static CCParticleSun * node()
10     {
11         CCParticleSun *pRet = new CCParticleSun();
12         if (pRet->init())
13         {
14             pRet->autorelease();
15             return pRet;
16         }
17         CC_SAFE_DELETE(pRet)
18         return NULL;
19     }
20 };
知易游戏开发教程cocos2d-x移植版007

2)更灵活的应用

为了定制粒子的数量,我们会使用另外一种方法:

知易游戏开发教程cocos2d-x移植版007
 1     emitter2 = new CCParticleSun();
 2     emitter2->initWithTotalParticles(250);
 3     emitter2->setTexture(CCTextureCache::sharedTextureCache()->addImage(s_fire));
 4     emitter2->setPosVar(ccp(3, 3));
 5     emitter2->setStartSize(20.0f);
 6     emitter2->setStartSizeVar(2.0f);
 7     emitter2->setEndSize(0.0f);
 8     emitter2->setEndSizeVar(0.0f);
 9     ccColor4F endColor2 = { 0.0f, 0.0f, 0.0f, 0.1f };
10     emitter2->setEndColor(endColor2);
11     emitter2->stopSystem();
知易游戏开发教程cocos2d-x移植版007

3)第三种方式

还有一种方式,那就是使用粒子编辑器来创建粒子效果文件。我还没有尝试过这种方法,只能以后再补充啦。

注意事项

需要注意的是,无论使用哪种方式来创建粒子效果对象,请确保设置了纹理,否则渲染时会崩溃。

此外,这里还涉及到cocos2d-x中对象生命周期的管理。

如果你使用方法一,并将创建的对象保存在成员变量中,那么需要调用retain来避免其自动释放掉,并记得在不用的时候调用release来释放。

如果你使用的是方法二,你要时刻记住“谁申请谁就负责释放”的原则。不过对于这些CCObject对象,请使用release来代替直接delete掉,因为它可能不止一份引用。

小结

虽然繁杂的参数稍稍变动可能就有一个新的效果,但是不要有任何心里负担,你要坚定信心,常用的就那么几个,用的多了自然就会得心应手。粒子系统中不同效果的使用方法是相通的,即便是一个没用过的新效果,只要稍微学习一下,也可以立马上手的。

示例7

在上一个示例的基础上,我们只做两处更改,给敌方坦克的炮弹增加烟雾效果,给我方坦克的炮弹增加火焰效果。

都是非常简单的调用,如果遇到问题,可以参考我上传的代码。

http://files.cnblogs.com/cocos2d-x/ZYG007.rar

分类: cocos2d, cocos2d-x 标签:

知易游戏开发教程cocos2d-x移植版006

2012年10月31日 没有评论

原文:http://www.cnblogs.com/cocos2d-x/archive/2012/06/03/2532932.html

在上一节中,我们使用经典FC游戏《坦克大战》的元素设计了一张地图,来演示Tiled Map Editor工具的基本用法,并在cocos2d-x程序中完成了tmx地图加载、查看以及动态修改地图元素的功能。

这一节,我们将对示例5进一步扩充、完善,使其成为能“玩”一下的游戏。

为了这个目标,我们需要做以下调整:

1)增加双方坦克,我方一辆,敌方八辆。

2)坦克在地图上行走时,需要完成基本的碰撞检测,不可以穿墙而过。

3)坦克可以开炮,炮弹可以摧毁砖墙和敌对方坦克。

4)视角锁定在我方坦克上,显示区域跟随我方坦克的移动而改变。

5)敌方(电脑)坦克拥有基本的AI来增加点儿乐趣。

增加坦克

我们的目标只是一个简单的示例游戏,所以我准备用同一个类来表示双方坦克,通过一个布尔型变量来区分敌我。

知易游戏开发教程cocos2d-x移植版006
1 class TankSprite : public CCSprite
2 {
3 public:
4     TankSprite(void);
5     virtual ~TankSprite(void);
6 
7     bool bIsEnemy;
8 }
知易游戏开发教程cocos2d-x移植版006

在地图中漫游

作为一个“完整”的游戏,我们需要实现在游戏地图中漫游的功能。这包含两方面的内容,下面我们来分别描述一下。

1)坦克精灵在地图上的移动

游戏讲究一个代入感。是谁在玩游戏?不是上帝,我只是一辆小小的坦克。当操作指令下达时,应该是一辆坦克去完成它,而不是通过上帝的眼睛来观察。所以,现在需要完成的功能就是让坦克在tmx地图上移动。

要实现这样的功能并不困难,因为CCSprite精灵类为我们准备好的大部分内容。我们要做的只是为坦克增加当前的状态、移动速度以及四个方向的移动函数。

知易游戏开发教程cocos2d-x移植版006
1     // TankAction 是一个枚举类型,用来表示坦克当期的状态
2     TankAction kAct;
3     float moveStep;
4     void moveLeft(GameLayer *layer);
5     void moveRight(GameLayer *layer);
6     void moveUp(GameLayer *layer);
7     void moveDown(GameLayer *layer);
知易游戏开发教程cocos2d-x移植版006

在实现四方向移动函数时,因为坦克是tmx地图的子节点,所以坐标不需要太多计算。

知易游戏开发教程cocos2d-x移植版006
 1 void TankSprite::moveLeft(GameLayer *layer)
 2 {
 3     kAct = kLeft;
 4     setRotation(-90.0f);
 5     CCPoint tankPos = getPosition();
 6     tankPos.x -= moveStep;
 7     CCSize tankSize = getTextureRect().size;
 8 
 9     // 越界检测
10     if (tankPos.x - layer->mapX < tankSize.width / 2)
11         setPosition(ccp(tankSize.width / 2, tankPos.y));
12     else
13         setPosition(tankPos);
14 }
知易游戏开发教程cocos2d-x移植版006

2)视口跟随

完成上面的功能,坦克就可以在地图上行走了。但是我们只能看见屏幕大小的地图,坦克很容易就走到屏幕之外去了。我们不愿意做一只“井底之蛙”,眼睛要跟上坦克。

我们前面介绍过视口这个概念,它就是整个游戏世界向我们打开的一扇窗子。而且,在示例5中我们也尝试了移动视口来观察整个游戏地图。我们现在要做的就是加一个视口跟随的功能,让视口跟随主角(我方)坦克车移动,将合适的地图区域展示给我们。

纵观大多数人的设计,对于视口跟随,一个普遍的做法是这样的。

大部分时间都将主角精灵固定在视口中心,主角的移动是通过反方向移动背景地图模拟的。只有当视口已经到达地图边缘,再没有更多地图供我们移动时,才移动主角本身。

句子有点儿拗口,没理解的朋友请看下面的演示。

将上面的过程写成代码:

知易游戏开发教程cocos2d-x移植版006
 1 void GameLayer::setWorldPosition(void)
 2 {
 3     CCSize tankSize = tank->getTextureRect().size;
 4     CCPoint tankPos = tank->getPosition();
 5 
 6     if (tankPos.x < (screenWidth - tankSize.width) / 2)
 7         mapX = 0;
 8     else if (tankPos.x > gameWorldWidth() - screenWidth / 2)
 9         mapX = -gameWorldWidth();
10     else
11         mapX = -(tankPos.x - screenWidth / 2 + tankSize.width / 2);
12 
13     if (tankPos.y < (screenHeight - tankSize.height) / 2)
14         mapY = 0;
15     else if (tankPos.y > gameWorldHeight() - screenHeight / 2)
16         mapY = -gameWorldHeight();
17     else
18         mapY = -(tankPos.y - screenHeight / 2 + tankSize.height / 2);
19 
20     // 越界复位
21     if (mapX > 0)
22         mapX = 0;
23     if (mapY > 0)
24         mapY = 0;
25     if (mapX < -(gameWorldWidth() - screenWidth))
26         mapX = -(gameWorldWidth() - screenWidth);
27     if (mapY < -(gameWorldHeight() - screenHeight))
28         mapY = -(gameWorldHeight() - screenHeight);
29     gameWorld->setPosition(mapX, mapY);
30 }
知易游戏开发教程cocos2d-x移植版006

碰撞检测

坦克也不是说是无坚不摧的,所以遇到河啊什么的,还是从桥上走方便一些。所以我们就得判断是不是没路了,是不是撞墙了,于是“碰撞检测”的概念就引入进来了。

试想,有8辆敌方坦克正在地图上游荡,他们撞墙要检测,互相之间也要检测,他们偶尔还会发射炮弹,每个炮弹的飞行和爆炸也都需要检测。而且这些都是并发进行的。哇!多么混乱的一个场面啊!

呵呵,其实“碰撞检测”并没有大家想的那么复杂,不是说要做碰撞检测就要弄个物理引擎进来,只要不是在实现逼真的物理效果,我们完全可以用自己的方法来检测碰撞。

大家知道,什么同时啊,并行啊,这些统统都是理论上的,或者说是在一定精度范围上的东西。即便是多核CPU,在共享同一资源时,它们也要分优先级的。所以,在小游戏这类简单的应用上,我们完全可以认为,一次碰撞发生时,世界是静止的。

这样一来,我们只需要按照一定的优先级,顺次为每一个需要检测碰撞的对象进行检测即可。

对于坦克的行走来说,我们需要做的检测只有地形一个因素,又因为是在同一坐标系下,事情灰常简单。

考虑到坦克是有体积的,这里取3个采样点检测碰撞,以避免其边缘进入墙里。

知易游戏开发教程cocos2d-x移植版006
 1     // 这是坦克左移时的碰撞检测代码
 2     CCPoint nextPos = ccp(tankPos.x - tankSize.width / 2, tankPos.y);
 3     unsigned int tid = layer->tileIDFromPosition(nextPos);
 4     if (tid != 4)
 5         return;
 6     nextPos = ccp(tankPos.x - tankSize.width / 2, tankPos.y + tankSize.height / 4);
 7     tid = layer->tileIDFromPosition(nextPos);
 8     if (tid != 4)
 9         return;
10     nextPos = ccp(tankPos.x - tankSize.width / 2, tankPos.y - tankSize.height / 4);
11     tid = layer->tileIDFromPosition(nextPos);
12     if (tid != 4)
13         return;
知易游戏开发教程cocos2d-x移植版006

开火射击

瞧,那辆破坦克摇头摆尾地开过来了,让我们干掉他。Fire! BOOM…

好吧,既然你想开火,那就给每辆坦克都配上炮弹吧。

知易游戏开发教程cocos2d-x移植版006
 1 BulletSprite* BulletSprite::bulletWithLayer(GameLayer *layer)
 2 {
 3     BulletSprite *sprite = new BulletSprite();
 4     if (sprite && sprite->initWithFile("bullet.png", CCRectMake(0, 0, 16, 16)))
 5     {
 6         sprite->autorelease();
 7         layer->gameWorld->addChild(sprite, 100);
 8         sprite->setIsVisible(false);
 9         sprite->setGameLayer(layer);
10         return sprite;
11     }
12     CC_SAFE_DELETE(sprite);
13     return NULL;
14 }
知易游戏开发教程cocos2d-x移植版006

虽然炮弹是坦克的配备,但是为了方便坐标计算,我们将炮弹归为地图的子节点。换个思路想,炮弹发射后就跟坦克分离了,所以我们这么设计也是可以接受的。

理所当然的,我们还需要为每一辆坦克增加一个开火按钮。

知易游戏开发教程cocos2d-x移植版006
 1 void TankSprite::onFire(GameLayer *layer)
 2 {
 3     CCPoint tankPos = getPosition();
 4     CCSize tankSize = getTextureRect().size;
 5     CCPoint bulletPos, bulletTarget;
 6     switch (kAct)
 7     {
 8     case kUp:
 9         bulletPos = ccp(tankPos.x, tankPos.y + tankSize.height / 2);
10         bulletTarget = ccp(bulletPos.x, bulletPos.y + 1024);
11         break;
12     case kDown:
13         bulletPos = ccp(tankPos.x, tankPos.y - tankSize.height / 2);
14         bulletTarget = ccp(bulletPos.x, bulletPos.y - 1024);
15         break;
16     case kLeft:
17         bulletPos = ccp(tankPos.x - tankSize.width / 2, tankPos.y);
18         bulletTarget = ccp(bulletPos.x - 1024, bulletPos.y);
19         break;
20     case kRight:
21         bulletPos = ccp(tankPos.x + tankSize.width / 2, tankPos.y);
22         bulletTarget = ccp(bulletPos.x + 1024, bulletPos.y);
23         break;
24     default:
25         break;
26     }
27     bullet->setPosition(bulletPos);
28     bullet->setIsVisible(true);
29     CCShow *ac1 = CCShow::action();
30     CCMoveTo *ac2 = CCMoveTo::actionWithDuration(5.0f, bulletTarget);
31     bullet->runAction(CCSequence::actions(ac1, ac2, NULL));
32     // 启动炮弹的碰撞检测
33     this->schedule(schedule_selector(TankSprite::checkExplosion), 1.0f / 30.0f);
34 }
知易游戏开发教程cocos2d-x移植版006

大家可以看到,炮弹发射是用系统内置的MoveTo动作模拟的,最后一行开启每秒30次的炮弹碰撞检测。检测的方法与行走时的检测类似,这里不再重复。

小结

至此,一个简单的坦克大战演示就初步完成了。为了给大家一个更形象的认识,上一张层结构示意图。

知易游戏开发教程cocos2d-x移植版006

本章示例代码基于cocos2d-1.0.1-x-0.13.0-beta编写。虽然cocos2d-2.0-rc0a-x-2.0已经发布了,但是变动比较大,主要是得换模板向导,我比较懒,所以本系列结束之前,我先不换版本了。

下载地址

http://files.cnblogs.com/cocos2d-x/ZYG006.rar

分类: 未分类 标签:

知易游戏开发教程cocos2d-x移植版005(下)

2012年10月31日 没有评论

原文:http://www.cnblogs.com/cocos2d-x/archive/2012/05/18/2508457.html

这一节,我们将使用cocos2d-x开发一个有关瓦片地图的示例。

在这个示例当中,我们需要完成以下功能:

1)TMX地图的加载与显示。

2)在地图中游走。

3)搞点儿小破坏,动态修改地图。

内部规则

在开始之前,我们还需要了解一些TMX地图的内部规则。

1)坐标

在TMX地图中,坐标是从零开始计算的,左上角那一块的坐标为(0,0),右下角那一块的坐标为(宽度-1,高度-1)。

知易游戏开发教程cocos2d-x移植版005(下)

看到上面这张截图时请不要惊慌,Qt版的确不能在每个格子上显示坐标,这图是我拼接出来的。

2)“瓦片”值

如果要操作地图上的元素,我们还需要知道每个瓦片的内容,即瓦片值。

用文本编辑工具打开之前绘制的坦克大战的地图,其实就是一个xml文件。

知易游戏开发教程cocos2d-x移植版005(下)

图中用红线标记的这个属性“firstgid”的值为"1",也就是说瓦片集中的瓦片是从一开始编号的。

知易游戏开发教程cocos2d-x移植版005(下)

如果地图中某一块的GID等于零,那就说明这个地方没有使用瓦片填充,即透明的。


下面我们学习如何在cocos2d-x中使用TMX地图,将涉及到CCTMXLayer和CCTMXTiledMap这两个类。

显示地图

要在cocos2d-x中使用TMX地图,你只需要调用tiledMapWithTMXFile来创建一个CCTMXTiledMap对象,并把它添加到场景中。

1 // Load level map
2 gameWorld = CCTMXTiledMap::tiledMapWithTMXFile("Level1.tmx");
3 this->addChild(gameWorld, 0, 9);

是的,就是这么简单。编译执行,然后地图就出现在你面前了。

知易游戏开发教程cocos2d-x移植版005(下)

四处游走

大家都知道,一般来说地图的尺寸要比游戏窗口(画面)大一些,就好像是透过窗子在观察游戏里的世界。这个窗子就是我们常说的视口。

在3D游戏中,我们只需要将角色放置在正确的位置上,然后移动摄像机改变视口就可以了。

在2D游戏中,我们通常的做法是,当显示区域不在地图边缘时,把角色放在画面的中心,反向移动地图,达到角色移动的效果。当显示区域到达地图边缘时,因为画面不能超出地图范围,所以这个时候我们就要真的移动角色了。

你还可以绘制一个稍微大一圈的地图,并设置厚厚的阻挡,这样角色就永远不会走到地图边缘了。这算是个取巧的方法。

但无论采用上面哪一种方法,我们都需要一个移动地图的方法。

通过查看CCTMXTiledMap类的声明,我们知道CCTMXTiledMap其实就是一个CCNode,它也拥有CCNode的setPosition成员方法。调用setPosition就可以达到我们移动地图的目的。

虚拟按键

为了方便控制,我们最好再设计一组虚拟按键来获取玩家的输入操作。当然,你在这里添加一个CCMenu也能实现类似的效果,但多少会有些限制,不如自己的虚拟按键方便。

在第4章中我们介绍过触摸事件的目标代理,添加虚拟按键使用的就是这个方法。

我们新建一个控制层,这个层的主要作用就是处理触摸事件。为了玩家能直观的看见虚拟按键,这个层还负责虚拟按键的显示,以及切换它们的显示状态。毕竟不是系统提供的菜单项,这些都是需要自己处理的。

虽然有点儿繁琐,但都是以前的知识点,所以就不贴代码了,如有需要请参考附件

动态修改地图

对TMX地图进行动态修改的操作是针对层的。修改的过程就是先要找到对应的层,然后对指定坐标上的GID进行修改。

知易游戏开发教程cocos2d-x移植版005(下)
 1 void GameLayer::ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent)
 2 {
 3     CCTouch *pTouch = reinterpret_cast<CCTouch *>(pTouches->anyObject());
 4     // 注意,升级到cocos2d-1.0.1-x-0.13.0-beta这个版本后,下面这个locationInView()是没有参数的。
 5     CCPoint touchPoint = CCDirector::sharedDirector()->convertToGL(pTouch->locationInView());
 6     // pt是触摸点转换到地图上的坐标
 7     CCPoint pt = this->tileCoordinateFromPos(ccp(touchPoint.x - mapX, touchPoint.y - mapY));
 8     if (pt.x != -1)
 9     {
10         // "tile"是我们要操作的层的名字
11         CCTMXLayer *ly = gameWorld->layerNamed("tile");
12         unsigned int gid = ly->tileGIDAt(pt);
13         if (gid != 1)
14             this->ShowExplodeAt(touchPoint);
15         if (gid == 2)
16             ly->setTileGID(5, pt);
17         if (gid == 4)
18             ly->setTileGID(6, pt);
19         if (gid == 5)
20             ly->setTileGID(4, pt);
21         if (gid == 6)
22             ly->setTileGID(1, pt);
23     }
24 }
知易游戏开发教程cocos2d-x移植版005(下)

上面这段代码实现了对触摸点显示爆炸动画,并根据地形不同修改爆炸后的地面显示的功能。

知易游戏开发教程cocos2d-x移植版005(下)

小结

在这一章中,我们学会了如何加载并显示一张TMX地图,如果获取某一坐标的地形信息以及如何动态修改这一属性。虽然这些只是十分基础的地图操作,但我们已经向着开发更复杂更有意思的游戏又迈进了一步。

示例代码下载:http://dl.dbank.com/c094e7ash7

代码更新

我以前未能理解到LAYER_NODE_FUNC和SCENE_NODE_FUNC这两个宏定义的精妙之处,所以按照知易的源代码那样为MainScene类加了一个ShowScene静态成员函数,导致游戏中节点的关系变成了:CCScene根场景下多了一个用层实现的MainScene,然后才是GameLayer和ControlLayer。太罗嗦了,所以这里更新一下。

新的下载地址:http://files.cnblogs.com/cocos2d-x/ZYG005.rar

题外话

大家是不是觉得CCLayer和CCScene里的"bool init(void);"应该改成虚函数"virtual
bool
 init(void);"会更好一些?

分类: 未分类 标签:

知易游戏开发教程cocos2d-x移植版005(上)

2012年10月31日 没有评论

原文:http://www.cnblogs.com/cocos2d-x/archive/2012/05/06/2485947.html

游戏中的地图是整个游戏的虚拟场景。通常情况下,手机上的游戏地图按照画面滚动方向可以分为以下三类:

1)纵向滚动

常见的是打飞机类游戏,画面滚动主要是从上至下的模拟正在按照常规速度飞行的飞机。

知易游戏开发教程cocos2d-x移植版005(上)

2)横向滚动

ARPG类游戏,主角通过画面从左至右的滚动来探索未知的世界、消灭敌人、成功过关。例如魂斗罗、超级玛丽等等。

知易游戏开发教程cocos2d-x移植版005(上)

3)纵、横向滚动

RPG游戏中这种情况最为常见,主角的一个关键任务就是探索一片较大的未知领域:发现敌人、发现特殊位置地点、建筑物等。

知易游戏开发教程cocos2d-x移植版005(上)

无论哪种场景,都会涉及到以下问题:

1)移动位置控制:主角在地图中移动时必须考虑到地图中物体的阻碍,不能越过墙体,必须通过木桥过河。更加真实的模拟会要求主角在不同的地面上有不同的移动速度:土地上速度一般,冰面上会打滑,停止效果滞后等。

2)地图的动态变化:主角在游戏过程中的行为导致地图变化。这里所说的变化是永久的,比如摧毁敌方建筑,建立我方建筑,修建道路等。而像敌人尸体的腐烂消逝这种临时变化不在此列。

由此可见,游戏中地图编程最主要的功能就是:

1)如何展示一个较大的地图场景。

2)如何实现地图对游戏主角和敌人移动的限制:阻止移动、修改运动效果。

3)如何动态的改变局部地图的显示内容。

从本章开始,我们讲解cocos2d-x引擎提供的地图处理机制:CCTMXTiledMap类。

TMX是当前主要支持的地图格式,你可以使用Tiled Map Editor来生成这种格式的文件。

Tiled Map Editor分Qt版和Java版两个版本,本文使用Qt版进行讲解,知易原文使用Java版。至于更换版本的原因,参见我的前一篇文章《地图编辑器的选择1》。

基础知识

考虑到内存容量、运行效率以及图像显示限制等诸多因素,手机上的游戏多半采用一种叫做瓦片地图的机制。制作这种地图时,首先对要绘制的地图进行分析,将使用到的物件制作成尺寸一致的小图片,即“瓦片”,然后使用这些瓦片拼接出完整的地图。

例如下面这幅地图:

知易游戏开发教程cocos2d-x移植版005(上)

绘制这幅地图只使用了以下3个瓦片:

知易游戏开发教程cocos2d-x移植版005(上)

这样一来,使用很少的图像元素就可以表现一副大尺寸的地图,大大地减少了内存使用量,提高了游戏运行效率。

下面就以这幅地图为目标,逐步讲解Tiled Map Editor的基本使用方法。

实战演练

1)创建地图

选择菜单文件->新文件。

知易游戏开发教程cocos2d-x移植版005(上)

在弹出的新地图对话框中,选择地图方向为正常,地图大小为38 x 30,块大小为20 x 20,确定。

知易游戏开发教程cocos2d-x移植版005(上)

2)创建“瓦片”集

选择菜单地图->新图块。

知易游戏开发教程cocos2d-x移植版005(上)

在弹出的新图块对话框中,输入图块的名称“codes”(不要问我为什么起这么个名字,我也不知道,也许知易是随便起的吧),并选择“tileSet.PNG”作为图像的来源,确定。

知易游戏开发教程cocos2d-x移植版005(上)

这时,你就可以从窗口右下角的图块面板中看到它了。

知易游戏开发教程cocos2d-x移植版005(上)

3)创建图层

你可以通过图层面板的“添加图层”按钮来创建新图层。

知易游戏开发教程cocos2d-x移植版005(上)

本例中,我们直接使用系统创建的默认图层。双击图层列表里的“块层 1”,将其重命名为“tile”,回车。

4)绘制地图

在开始之前,检查一下显示网格的功能是否开启,它可以帮助我们精准地绘制地图。

此功能如未开启,你可以选择菜单视图->显示网格来开启它。

知易游戏开发教程cocos2d-x移植版005(上)

首先,选择填充工具,将整张地图填充为土地。

知易游戏开发教程cocos2d-x移植版005(上)

然后,选择图章刷工具,在地图上绘制砖墙。

知易游戏开发教程cocos2d-x移植版005(上)

Tiled Map Editor编辑器支持地图的复制、粘贴操作。所以可以对图案相同的部分复制、粘贴来加速地图的制作。

知易游戏开发教程cocos2d-x移植版005(上)

知易游戏开发教程cocos2d-x移植版005(上)

接着,点缀上几块钢板完成整张地图的绘制。

5)保存地图

忙活这么半天了,最后千万别忘了保存文件。选择菜单文件->保存,将TMX地图和用到的图片资源保存在同一个文件夹下

ps.如果遇到生成的地图文件无法在cocos2d-x中使用的情况,请检查编辑器参数设置中的“另存为数据层”一项是否为Base64 (不压缩)、Base64 (gzip压缩)、Base64 (zlib压缩)之一。

知易游戏开发教程cocos2d-x移植版005(上)

 

绘制优质地图的几点建议

程序员们通常中规中矩,作品就显得死板而不生动。

知易游戏开发教程cocos2d-x移植版005(上)

本段翻译自下面的链接:

http://wiki.themanaworld.org/index.php/Mapping_Tutorial

  • 在开始绘制之前你应当对整体布局有大致的规划,先从纸上画个草图,这将对你有所帮助。
  • 避免将桥、山道、走廊之类的阻挡设置为1个瓦片的宽度,否则很容易卡在这些地方。
  • 不要让太多相似的物件同屏出现。非常显眼的瓦片应当有节制地使用。不要让大片相同的瓦片反复出现,你可以通过点缀一些不一样的瓦片来断开它们。
  • 避免将自然环境画得太规则。通常,树不会成排成列地生长。河流、山脊以及洞穴里的走廊从来都不是笔直的。
  • 地图不能画的太功能化。给玩家们一些东西看,即便这对玩游戏没什么帮助。
  • 但是,不要忘了可玩性。

稍作调整,感觉就好多了。

知易游戏开发教程cocos2d-x移植版005(上)

当然,这就要求我们在绘制地图时要多少有些“艺术细胞”。如果感觉自己没有这个水平,那就找帮手吧。你身边肯定会有的,比如那个看见你一回家就趴在电脑前面而很恼火的人。

分类: 未分类 标签:

知易游戏开发教程cocos2d-x移植版004

2012年10月31日 没有评论

原文:http://www.cnblogs.com/cocos2d-x/archive/2012/03/25/2416890.html

我们知道cocos2d-x是cocos2d-iphone项目的C++移植版本,它拥有跨平台的特性。同时cocos2d-x与cocos2d-iphone保持着高度地同步,这也就从根本上限制住它是一个为手机、平板等设备量身定做的游戏引擎。而对Win32等平台的支持,仅仅是为了方便开发与调试。

如果你正准备开发PC版的游戏,使用那种专为PC设计的引擎才是你正确的选择。虽然在一篇介绍cocos2d-x的文章里出现这样的句子有些扎眼,但我想有些事情还是讲明白的好,任何人的时间都不应该被浪费。

如果你也像我一样是从PC端开发加入到cocos2d-x中的,那么有很多概念是需要注意的。

比如,在PC上我们经常使用的鼠标在手机和平板上变成了手指头。这当然不单单是介质的转变,发生变化的还包括使用习惯。

消失的鼠标经过状态

大家都知道,在PC上,一个按钮通常包含4种状态——普通、鼠标经过、鼠标按下、禁用,而在手机和平板上,一个按钮通常只有3种状态——普通、按下、禁用。

这是很容易理解的。

第一,不管鼠标放在什么地方,它终究是脱离不了显示器屏幕的,而让一个人时刻把手指按在触摸屏上,要困难得多。

第二,鼠标是有按键的,而人手没有按键,想实现移动而脱离点击是有难度的。

所以在手机和平板上,传统意义的鼠标经过状态几乎不存在。

神奇的多点触摸

对于手机和平板用户来说,多点触屏稀松平常。虽然PC也有支持多点输入的外设,但支持这一特性的软件却不常见。对于用惯了PC的人来说,还是鼠标的单点输入更容易接受。

所谓习惯嘛,就是习以为常,时间久了,也就是那么回事儿了。今天我们就来认识认识这个Touch(触摸)。

cocos2d-x的触摸事件处理

在cocos2d-x中,触摸是一种重要的交互手段。虽然我们之前没有明确指出这个触摸事件,但是我们一刻也没有离开它。每一个Menu的触发,其实就是一次触摸。

CCStandardTouchDelegate

准确来说,我们与Touch有过一面之缘。在第2篇中,我们实现了一个点击屏幕后,飞船移动过去的功能。

知易游戏开发教程cocos2d-x移植版004
1 void GameLayer::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)
2 {
3 CCTouch *touch = (CCTouch *)pTouches->anyObject();
4 // 获得触摸点坐标
5 CCPoint location = touch->locationInView(touch->view());
6 CCPoint convertedLocation = CCDirector::sharedDirector()->convertToGL(location);
7 // 让飞船在1秒钟内移动过去
8 flight->runAction(CCMoveTo::actionWithDuration(1.0f, ccp(convertedLocation.x, convertedLocation.y)));
9 }
知易游戏开发教程cocos2d-x移植版004

但是,想要上面的代码生效,还必须在初始化中开启触摸事件。

知易游戏开发教程cocos2d-x移植版004
1 bool GameLayer::init(void)
2 {
3 ...
4 // accept touch now!
5 setIsTouchEnabled(true);
6 ...
7 }
知易游戏开发教程cocos2d-x移植版004

跟进去看看开启触摸事件的实现。

知易游戏开发教程cocos2d-x移植版004
 1 void CCLayer::setIsTouchEnabled(bool enabled)
2 {
3 if (m_bIsTouchEnabled != enabled)
4 {
5 m_bIsTouchEnabled = enabled;
6 if (m_bIsRunning)
7 {
8 if (enabled)
9 {
10 this->registerWithTouchDispatcher();
11 }
12 else
13 {
14 // have problems?
15 CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);
16 }
17 }
18 }
19 }
20
21 void CCLayer::registerWithTouchDispatcher()
22 {
23 CCTouchDispatcher::sharedDispatcher()->addStandardDelegate(this,0);
24 }
知易游戏开发教程cocos2d-x移植版004

上面的代码通过CCTouchDispatcher添加标准代理实现开启触摸的功能。

所谓标准代理就是指你可以接收到所有的消息——开始(Began)、移动(Moved)、结束(Ended)、取消(Cancelled)。触摸点的信息是由CCSet表示一个集合。

与高权限相随的是高灵活性,而要获得高灵活性的代价就是你必须自己来编写额外的代码。

灵活性太高,反而不知从何开始,干脆就不讲了,以后有实际需要的时候再说。不过有一点必须提一下,标准代理类型与苹果的CocoaTouch框架基本一致,所以CocoaTouch的大部分方案在cocos2d-x中也是可行的。

CCTargetedTouchDelegate

相较标准代理来说,目标代理的功能自然是弱一些,但使用起来要简便得多。

使用目标代理有两个显著的好处:

1.你不用跟CCSet打交道,调度程序会替你做好拆分,每次使用时你会得到单一的Touch事件。

2.你可以通过让ccTouchBegan返回true来“认领”一个触摸事件。被“认领”的触摸事件只会被分发给“认领”它的代理对象。这样你就可以从多点触摸的检测苦海中解脱出来。

我们来实战演练一下。假设我们接到一个任务,要求在屏幕上显示一个精灵,这个精灵是一个循环播放的序列帧动画。当我们按在精灵上的时候可以拖动它,如果点在精灵外面,那就不能拖动。

首先分析下我们接到的任务。显示一个循环播放的序列帧动画精灵,这个没有难点,我们之前已经做过多次了。按在精灵上的时候可以拖动它,按在外面就不能拖动,这一条如果使用标准代理对象实现,必然要添加大量的判断和状态,显然目标代理是我们应该优先考虑的。

因此,我们需要的是一个符合目标代理的精灵,所以这是一个多重继承的类,它有2个父类——CCSprite和CCTargetedTouchDelegate。

知易游戏开发教程cocos2d-x移植版004
1 class KillingLight : public CCSprite, public CCTargetedTouchDelegate
2 {
3 public:
4 KillingLight(void);
5 virtual ~KillingLight(void);
6 }
知易游戏开发教程cocos2d-x移植版004

要接收触摸事件必须先进行注册,当不再需要的时候要进行反注册。我们重写CCNode的onEnter和onExit函数来完成触摸事件的注册与反注册。

知易游戏开发教程cocos2d-x移植版004
 1 void KillingLight::onEnter(void)
2 {
3 CCSprite::onEnter();
4 // 我们希望独占被“认领”的触摸事件,所以第3个参数传入true
5 CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true);
6 }
7
8 void KillingLight::onExit(void)
9 {
10 CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);
11 CCSprite::onExit();
12 }
知易游戏开发教程cocos2d-x移植版004

在目标代理对象中,只有先“认领”触摸事件,才能使用。所以我们要重写CCTargetedTouchDelegate的ccTouchBegan函数。

因为任务要求只有点在精灵上面时才能拖动,所以我们需要增加判断触摸点是否在精灵上的判断。

知易游戏开发教程cocos2d-x移植版004
 1 CCRect KillingLight::rect(void)
2 {
3 // 后面的比较是以锚点为标准的
4 const CCSize& s = getTextureRect().size;
5 const CCPoint& p = this->getAnchorPointInPixels();
6 return CCRectMake(-p.x, -p.y, s.width, s.height);
7 }
8
9 bool KillingLight::containsTouchLocation(CCTouch *touch)
10 {
11 return CCRect::CCRectContainsPoint(rect(), convertTouchToNodeSpaceAR(touch));
12 }
13
14 bool KillingLight::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
15 {
16 if (! containsTouchLocation(pTouch))
17 return false;
18
19 return true;
20 }
知易游戏开发教程cocos2d-x移植版004

接着,我们重写CCTargetedTouchDelegate的ccTouchMoved函数,来实现拖动功能。

知易游戏开发教程cocos2d-x移植版004
1 void KillingLight::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
2 {
3 CCPoint touchPoint = pTouch->locationInView(pTouch->view());
4 touchPoint = CCDirector::sharedDirector()->convertToGL(touchPoint);
5 setPosition(touchPoint);
6 }
知易游戏开发教程cocos2d-x移植版004

为了便于使用,我们再添加一个静态成员函数KillingLightWithBatchNode来创建KillingLight对象。

知易游戏开发教程cocos2d-x移植版004
 1 KillingLight* KillingLight::KillingLightWithBatchNode(cocos2d::CCSpriteBatchNode *batchNode, const cocos2d::CCRect& rect)
2 {
3 KillingLight *pobSprite = new KillingLight();
4 if (pobSprite && pobSprite->initWithBatchNode(batchNode, rect))
5 {
6 pobSprite->autorelease();
7 return pobSprite;
8 }
9 CC_SAFE_DELETE(pobSprite);
10 return NULL;
11 }
知易游戏开发教程cocos2d-x移植版004

这样,一个支持点击拖动的精灵类就完成了,你可以像使用CCSprite那样使用它。

知易游戏开发教程cocos2d-x移植版004

如果你希望让这个类再完美一些,比如只响应第一个按上的触摸事件以免多点抢一个精灵等等,你可以参考TouchesTest中的Paddle类。

代码下载:

http://files.cnblogs.com/cocos2d-x/ZYG004.rar

在处理触摸事件的时候,最需要注意的是坐标转换。这包括很多层面,是屏幕坐标系还是GL坐标系,是世界坐标还是局部坐标,还有坐标是相对哪个点计算的等等。

分类: 未分类 标签:

cocos2d-x动作系统浅析

2012年10月31日 没有评论
尊重作者劳动,转载时请标明文章出处。

作者:Bugs Bunny

地址:http://www.cnblogs.com/cocos2d-x/archive/2012/03/13/2393898.html

在上一篇博文中我们对cocos2d-x的动作使用有了初步了解。今天,我们将通过阅读部分cocos2d-x源码来了解这个动作系统是如何运作的,以及在使用时还有什么细节需要特别注意。

小弟初学cocos2d-x,如果文章中存在什么错误或者不足,望各位不吝赐教,及时指出。

http://www.cnblogs.com/cocos2d-x/

动作的祖先 – CCAction

CCAction是所有动作类的基类,我们来看一下CCAction的声明:

cocos2d-x动作系统浅析
 1 /** 
2 @brief Base class for CCAction objects.
3 */
4 class CC_DLL CCAction : public CCObject
5 {
6 public:
7 CCAction(void);
8 virtual ~CCAction(void);
9
10 char * description();
11
12 virtual CCObject* copyWithZone(CCZone *pZone);
13
14 //! return true if the action has finished
15 virtual bool isDone(void);
16
17 //! called before the action start. It will also set the target.
18 virtual void startWithTarget(CCNode *pTarget);
19
20 /**
21 called after the action has finished. It will set the 'target' to nil.
22 IMPORTANT: You should never call "[action stop]" manually. Instead, use: "target->stopAction(action);"
23 */
24 virtual void stop(void);
25
26 //! called every frame with it's delta time. DON'T override unless you know what you are doing.
27 virtual void step(ccTime dt);
28
29 /**
30 called once per frame. time a value between 0 and 1
31
32 For example:
33 - 0 means that the action just started
34 - 0.5 means that the action is in the middle
35 - 1 means that the action is over
36 */
37 virtual void update(ccTime time);
38
39 inline CCNode* getTarget(void) { return m_pTarget; }
40 /** The action will modify the target properties. */
41 inline void setTarget(CCNode *pTarget) { m_pTarget = pTarget; }
42
43 inline CCNode* getOriginalTarget(void) { return m_pOriginalTarget; }
44 /** Set the original target, since target can be nil.
45 Is the target that were used to run the action.
46 Unless you are doing something complex, like CCActionManager, you should NOT call this method.
47 The target is 'assigned', it is not 'retained'.
48 @since v0.8.2
49 */
50 inline void setOriginalTarget(CCNode *pOriginalTarget) { m_pOriginalTarget = pOriginalTarget; }
51
52 inline int getTag(void) { return m_nTag; }
53 inline void setTag(int nTag) { m_nTag = nTag; }
54
55 public:
56 /** Allocates and initializes the action */
57 static CCAction* action();
58
59 protected:
60 CCNode *m_pOriginalTarget;
61 /** The "target".
62 The target will be set with the 'startWithTarget' method.
63 When the 'stop' method is called, target will be set to nil.
64 The target is 'assigned', it is not 'retained'.
65 */
66 CCNode *m_pTarget;
67 /** The action tag. An identifier of the action */
68 int m_nTag;
69 };
cocos2d-x动作系统浅析

1)description

跳过构造和析构函数,第一个映入眼帘的是description成员函数,没有注释。从字面上来看,这个函数应该是返回类的描述,那么它是如何定义的呢?

cocos2d-x动作系统浅析
1 char * CCAction::description()
2 {
3 char *ret = new char[100] ;
4 sprintf(ret,"<CCAction | Tag = %d>", m_nTag);
5 return ret;
6 }
cocos2d-x动作系统浅析

可以看到description像strdup之类的函数那样返回了在堆上分配的内存。所以,如果你调用了description,那么你就要时刻注意,不要忘记将其释放。

2)isDone

再往下看可以看到copyWithZone成员函数,这是一个继承自父类的函数,跟动作的关系不大。接下来的函数叫做isDone,通过它可以查询动作是否执行完毕。大体上来说,这个函数是通过对比流逝时间与目标时间来得出结果。但不同子类可能有着不同的实现,例如:瞬时动作总是返回true,而CCRepeat使用执行次数来取代流逝时间等等。

3)startWithTarget/stop/step

在cocos2d-x中,一个动作从startWithTarget开始,到stop结束。在动作的执行过程中,每帧调用一次step函数。这些构成了一个动作的完整流程。

4)update

此函数接受一个百分比参数,它表示动作的完成进度。update根据这个百分比将目标对象(可能是一个CCSprite对象,也可能是别的什么)做出相应的调整。

笔者经过统计发现,只有2种函数调用过update,一个是step,另一个就是update本身。在第一种情况中,step通过update来更新动作的表现,在第二种情况中,这多半是一个包含了其它动作的复杂动作(比如CCActionEase类系)。

内务府总管 – CCActionManager

前面我们提到startWithTarget、stop、step构成了一个动作的完整流程,那么这一整套流程是由谁来驱动的呢?下面我们就着重分析这个问题。

1)startWithTarget

我们知道,在runAction一个动作之前,必须先创建这个动作。

以CCMoveTo为例,我们是调用它的actionWithDuration成员函数来创建动作的,那么是不是它调用了startWithTarget开始这个动作呢?

稍微思考一下,不难发现,绝对不是actionWithDuration调用了startWithTarget。因为startWithTarget需要一个Target,也就是动作的执行者,而这里仅仅是创建动作,这个执行者的参数无从获取。

按照这个思路,我们想到了runAction函数。此时动作对象已创建,执行者也是明确的。

cocos2d-x动作系统浅析
 1 CCAction * CCNode::runAction(CCAction* action)
2 {
3 CCAssert( action != NULL, "Argument must be non-nil");
4 CCActionManager::sharedManager()->addAction(action, this, !m_bIsRunning);
5 return action;
6 }
7
8 void CCActionManager::addAction(CCAction *pAction, CCNode *pTarget, bool paused)
9 {
10 CCAssert(pAction != NULL, "");
11 CCAssert(pTarget != NULL, "");
12
13 tHashElement *pElement = NULL;
14 // we should convert it to CCObject*, because we save it as CCObject*
15 CCObject *tmp = pTarget;
16 HASH_FIND_INT(m_pTargets, &tmp, pElement);
17 if (! pElement)
18 {
19 pElement = (tHashElement*)calloc(sizeof(*pElement), 1);
20 pElement->paused = paused;
21 pTarget->retain();
22 pElement->target = pTarget;
23 HASH_ADD_INT(m_pTargets, target, pElement);
24 }
25
26 actionAllocWithHashElement(pElement);
27
28 CCAssert(! ccArrayContainsObject(pElement->actions, pAction), "");
29 ccArrayAppendObject(pElement->actions, pAction);
30
31 pAction->startWithTarget(pTarget);
32 }
cocos2d-x动作系统浅析

可以看到,在CCActionManager的addAction里,startWithTarget被调用了,这个动作开始了执行。

2)step

还记得我们在介绍CCAction时说过,step函数会按照每帧一次的速度被调用,因此必然存在一套驱动机制。通过对CCMoveTo的update下断点,可以得到这样的函数调用关系图。

cocos2d-x动作系统浅析

可以看出CCActionManager的update调用了动作的step函数(第23行),驱动着动作的执行。

cocos2d-x动作系统浅析
 1 // main loop
2 void CCActionManager::update(ccTime dt)
3 {
4 for (tHashElement *elt = m_pTargets; elt != NULL; )
5 {
6 m_pCurrentTarget = elt;
7 m_bCurrentTargetSalvaged = false;
8
9 if (! m_pCurrentTarget->paused)
10 {
11 // The 'actions' CCMutableArray may change while inside this loop.
12 for (m_pCurrentTarget->actionIndex = 0;
m_pCurrentTarget->actionIndex < m_pCurrentTarget->actions->num;
13 m_pCurrentTarget->actionIndex++)
14 {
15 m_pCurrentTarget->currentAction =
(CCAction*)m_pCurrentTarget->actions->arr[m_pCurrentTarget->actionIndex];
16 if (m_pCurrentTarget->currentAction == NULL)
17 {
18 continue;
19 }
20
21 m_pCurrentTarget->currentActionSalvaged = false;
22
23 m_pCurrentTarget->currentAction->step(dt);
24
25 if (m_pCurrentTarget->currentActionSalvaged)
26 {
27 // The currentAction told the node to remove it. To prevent the action from
28 // accidentally deallocating itself before finishing its step, we retained
29 // it. Now that step is done, it's safe to release it.
30 m_pCurrentTarget->currentAction->release();
31 } else
32 if (m_pCurrentTarget->currentAction->isDone())
33 {
34 m_pCurrentTarget->currentAction->stop();
35
36 CCAction *pAction = m_pCurrentTarget->currentAction;
37 // Make currentAction nil to prevent removeAction from salvaging it.
38 m_pCurrentTarget->currentAction = NULL;
39 removeAction(pAction);
40 }
41
42 m_pCurrentTarget->currentAction = NULL;
43 }
44 }
45
46 // elt, at this moment, is still valid
47 // so it is safe to ask this here (issue #490)
48 elt = (tHashElement*)(elt->hh.next);
49
50 // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
51 if (m_bCurrentTargetSalvaged && m_pCurrentTarget->actions->num == 0)
52 {
53 deleteHashElement(m_pCurrentTarget);
54 }
55 }
56
57 // issue #635
58 m_pCurrentTarget = NULL;
59 }
cocos2d-x动作系统浅析

而在CCActionManager初始化的时候就已经向任务调度系统提交了注册,保证自己每一帧都得到更新。

cocos2d-x动作系统浅析
1 bool CCActionManager::init(void)
2 {
3 CCScheduler::sharedScheduler()->scheduleUpdateForTarget(this, 0, false);
4 m_pTargets = NULL;
5 return true;
6 }
cocos2d-x动作系统浅析

3)stop

同理,从上面的代码中(第32行~第40行)可以看到,每次执行step后,会判断一下动作是否执行完毕,如果完毕则调用stop善后处理,并移除该动作。

小结

CCAction与CCActionManager相互配合,一个井然有序的世界就这样建立起来了。

需要特别指出的是,大部分时候我们无需直接与CCActionManager打交道,系统会自动为我们打点好一切。

分类: 未分类 标签:

知易游戏开发教程cocos2d-x移植版003

2012年10月31日 没有评论

原文地址: http://www.cnblogs.com/cocos2d-x/archive/2012/03/04/2379562.html

从本章开始,我们开始讲解cocos2d-x库的动作(Action)。游戏的世界是一个动态的世界:无论是主角精灵还是NPC精灵都处于不断的运动当中,甚至是背景中漂流的树叶,随风而动的小草。这些明显的或者不明显的运动构成了我们栩栩如生的游戏世界。

仔细研究游戏中精灵的运动,我们发现:所有这样的运动都可以细分为若干个基本动作和基本动作的组合。通过进一步扩展,我们可以将同一精灵的更多动作和不同精灵之间的不同动作连贯起来,形成关于整个运动世界的连续模拟。

我们给出示例ZYG003,展示cocos2d-x支持的主要动作:

知易游戏开发教程cocos2d-x移植版003

基本动作

从技术上来说,基本动作的本质就是改变某个图形对象的属性:位置、角度、大小等。cocos2d-x提供超过20种基本动作供我们使用。根据改变完成所需的时间,可以分为瞬时动作和延时动作。其中,延时动作的执行速度又可以按照不同的方式来改变(位置、大小、颜色、闪烁……)。

再进一步介绍基本动作之前,我们先来简单明确一下动作是如何与CCNode关联起来的。CCNode有一个成员函数叫runAction,定义为:

知易游戏开发教程cocos2d-x移植版003
1 /** Executes an action, and returns the action that is executed.
2 The node becomes the action's target.
3 @warning Starting from v0.8 actions don't retain their target anymore.
4 @since v0.7.1
5 @return An Action pointer
6 */
7 CCAction* runAction(CCAction* action);
知易游戏开发教程cocos2d-x移植版003

此接口确保所有的精灵都可以执行各种动作。也正是为了服从这个接口的定义,导致后续各种组合动作也都从CCAction派生。

下面的代码是通常调用某个动作的方法:

知易游戏开发教程cocos2d-x移植版003
1 CCSize s = CCDirector::sharedDirector()->getWinSize();
2 // 创建动作
3 CCActionInterval *actionTo = CCMoveTo::actionWithDuration(2.0f, ccp(s.width - 40.0f, s.height - 40.0f));
4 // 使用动作(tamara是一个CCSprite指针)
5 tamara->runAction(actionTo);
知易游戏开发教程cocos2d-x移植版003

瞬时动作

顾名思义,瞬时动作就是不需要时间,马上就完成的动作。瞬时动作的共同基类是CCActionInstant

cocos2d-x提供以下瞬时动作:

知易游戏开发教程cocos2d-x移植版003

瞬时动作大都有与之对应的属性设置方法,之所以作为一个动作来实现,是为了可以与其他动作形成一个连续动作。下面来看一下瞬时动作的使用。

放置 – CCPlace

效果类似于setPosition(ccp(x, y))。示例代码如下:

知易游戏开发教程cocos2d-x移植版003
1 void InstantActionLayer::onPlace(CCObject* pSender)
2 {
3 CCSize s = CCDirector::sharedDirector()->getWinSize();
4 // 理论上使用伪随机数前应该srand一下,但是知易的例子中没有,不知道是不是故意这么设计的。
5 // 如果使用time(NULL)初始化伪随机数种子,要注意time_t有可能是64位的!
6 CCPoint p = ccp(CCRANDOM_0_1() * s.width, CCRANDOM_0_1() * s.height);
7 flight->runAction(CCPlace::actionWithPosition(p));
8 }
知易游戏开发教程cocos2d-x移植版003

隐藏 – CCHide

效果类似于setIsVisible(false)。示例代码如下:

1 void InstantActionLayer::onHide(CCObject* pSender)
2 {
3 flight->runAction(CCHide::action());
4 }

显示 – CCShow

效果类似于setIsVisible(true)。示例代码如下:

1 void InstantActionLayer::onShow(CCObject* pSender)
2 {
3 flight->runAction(CCShow::action());
4 }

可见切换 – CCToggleVisibility

效果类似于setIsVisible(!getIsVisible())。示例代码如下:

1 void InstantActionLayer::onToggle(CCObject* pSender)
2 {
3 flight->runAction(CCToggleVisibility::action());
4 }

水平翻转 – CCFlipX

效果类似于setFlipX(true/false)。示例代码如下:

1 void InstantActionLayer::onFlipX(CCObject* pSender)
2 {
3 flight->runAction(CCFlipX::actionWithFlipX(true));
4 }

垂直翻转 – onFlipY

效果类似于setFlipY(true/false)。示例代码如下:

1 void InstantActionLayer::onFlipY(CCObject* pSender)
2 {
3 flight->runAction(CCFlipY::actionWithFlipY(true));
4 }

还有两个较为特殊的动作(网格重用 – CCReuseGrid|停止网格 – CCStopGrid),我们以后介绍。

延时动作

延时动作就是指动作的完成需要一段时间。因此,几乎所有的延时动作都使用执行时间作为第一个参数,它们有着共同的基类CCActionInterval

cocos2d-x中常用的延时动作:

知易游戏开发教程cocos2d-x移植版003

这里有一个简单的类命名规则:

CCXxxxTo – 绝对动作,执行的结果与当前的状态关系不密切;

CCXxxxBy – 相对动作,在当前的状态上执行某种动作,执行的结果与当前状态是紧密相关的。

移动到 – CCMoveTo

移动 – CCMoveBy

跳跃到 – CCJumpTo

参数为终点位置、跳跃高度和跳跃次数。

跳跃 – CCJumpBy

贝赛尔曲线 – CCBezierBy

支持三次贝赛尔曲线:P0-起点,P1-起点切线方向,P2-终点切线方向,P3-终点。

知易游戏开发教程cocos2d-x移植版003



首先设置贝塞尔参数,然后执行。

放大到 – CCScaleTo

放大 – CCScaleBy

如果参数为小数,那就是缩小了。

旋转到 – CCRotateTo

旋转 – CCRotateBy

闪烁 – CCBlink

色调变化到 – CCTintTo

色调变换 – CCTintBy

变暗到 – CCFadeTo

由无变亮 – CCFadeIn

由亮变无 – CCFadeOut

组合动作

按照一定的次序将上述基本动作组合起来,形成连贯的一套组合动作。组合动作包括以下几类:

知易游戏开发教程cocos2d-x移植版003

序列 – CCSequence

序列的使用非常简单,该类从CCActionInterval派生,本身就可以被CCNode对象执行。该类的作用就是线性排列若干个动作,然后按先后次序逐个执行。

知易游戏开发教程cocos2d-x移植版003
 1 void CompositionActionLayer::onSequence(CCObject* pSender)
2 {
3 CCSize s = CCDirector::sharedDirector()->getWinSize();
4 // 创建5个动作
5 CCFiniteTimeAction *action0 = CCPlace::actionWithPosition(ccp(s.width / 2, 50));
6 CCFiniteTimeAction *action1 = CCMoveTo::actionWithDuration(1.2f, ccp(s.width - 50.0f, s.height - 50.0f));
7 CCFiniteTimeAction *action2 = CCJumpTo::actionWithDuration(1.2f, ccp(150, 50), 30.0f, 5);
8 CCFiniteTimeAction *action3 = CCBlink::actionWithDuration(1.2f, 3);
9 CCFiniteTimeAction *action4 = CCTintBy::actionWithDuration(0.5f, 0, 255, 255);
10 // 将5个动作组合为一个序列,注意不要忘了用NULL结尾。
11 flight->runAction(CCSequence::actions(action0, action1, action2, action3, action4, action0, NULL));
12 }
知易游戏开发教程cocos2d-x移植版003

同步 – CCSpawn

同步的使用非常简单,该类也是从CCActionInterval派生,可以被CCNode对象执行。该类的作用就是同时并列执行若干个动作,但要求动作本身是可以同时执行的,比如:移动式翻转、变色、缩放等。

需要特别注意的是,同步执行最后完成的时间由基本动作中用时最大者决定。

知易游戏开发教程cocos2d-x移植版003
 1 void CompositionActionLayer::onSpawn(CCObject* pSender)
2 {
3 CCSize s = CCDirector::sharedDirector()->getWinSize();
4 flight->setRotation(0.0f);
5 flight->setPosition(ccp(s.width / 2, 50));
6 // 创建4个需要并行的动作,确保动作用时可组合。(action1/action2/sequence的执行时间都是2秒)
7 CCFiniteTimeAction *action1 = CCMoveTo::actionWithDuration(2.0f, ccp(s.width - 50.0f, s.height - 50.0f));
8 CCFiniteTimeAction *action2 = CCRotateTo::actionWithDuration(2.0f, 180.0f);
9 CCFiniteTimeAction *action3 = CCScaleTo::actionWithDuration(1.0f, 4.0f);
10 CCFiniteTimeAction *action4 = CCScaleBy::actionWithDuration(1.0f, 0.5f);
11 CCFiniteTimeAction *sequence = CCSequence::actions(action3, action4, NULL);
12 // 创建并执行同步动作。
13 flight->runAction(CCSpawn::actions(action1, action2, sequence, NULL));
14 }
知易游戏开发教程cocos2d-x移植版003

重复有限次数 – CCRepeat

CCRepeat用来将某一动作重复有限次数,示例代码如下:

知易游戏开发教程cocos2d-x移植版003
 1 void CompositionActionLayer::onRepeat(CCObject* pSender)
2 {
3 CCSize s = CCDirector::sharedDirector()->getWinSize();
4 flight->setRotation(0.0f);
5 flight->setPosition(ccp(s.width / 2, 50));
6 // 创建动作序列
7 CCFiniteTimeAction *action1 = CCMoveTo::actionWithDuration(2.0f, ccp(s.width - 50.0f, s.height - 50.0f));
8 CCFiniteTimeAction *action2 = CCJumpBy::actionWithDuration(2.0f, ccp(-400, -200), 30.0f, 5);
9 CCFiniteTimeAction *action3 = CCJumpBy::actionWithDuration(2.0f, ccp(s.width / 2, 0), 20.0f, 3);
10 CCFiniteTimeAction *sequence = CCSequence::actions(action1, action2, action3, NULL);
11 // 重复运行上述动作序列3次
12 flight->runAction(CCRepeat::actionWithAction(sequence, 3));
13 }
知易游戏开发教程cocos2d-x移植版003

反动作 – Reverse

反动作就是反向(逆向)执行某个动作,支持针对动作序列的反动作序列。反动作不是一个专门的类,而是CCFiniteTimeAction引入的一个接口。不是所有的类都支持反动作,CCXxxxTo类通常不支持反动作,而CCXxxxBy类通常支持,示例如下:

知易游戏开发教程cocos2d-x移植版003
 1 void CompositionActionLayer::onReverse(CCObject* pSender)
2 {
3 CCSize s = CCDirector::sharedDirector()->getWinSize();
4 flight->setRotation(0.0f);
5 flight->setPosition(ccp(s.width / 2, 50));
6
7 CCFiniteTimeAction *action1 = CCMoveBy::actionWithDuration(2.0f, ccp(190, 220));
8 // 创建某个动作的反动作
9 CCFiniteTimeAction *action2 = action1->reverse();
10
11 flight->runAction(CCRepeat::actionWithAction(CCSequence::actions(action1, action2, NULL), 2));
12 }
知易游戏开发教程cocos2d-x移植版003

动画 – CCAnimate

动画就是让精灵自身连续执行一段影像,形成模拟运动的效果:行走时的状态,打斗时的状态等。

知易游戏开发教程cocos2d-x移植版003
 1 void CompositionActionLayer::onAnimation(CCObject* pSender)
2 {
3 CCSpriteBatchNode *mgr = static_cast<CCSpriteBatchNode *>(this->getChildByTag(4));
4
5 CCAnimation *animation = CCAnimation::animation();
6 animation->setName("flight");
7 animation->setDelay(0.2f);
8 for (int i = 0; i < 3; ++i)
9 {
10 // 定义每一帧的内容
11 float x = static_cast<float>(i % 3);
12 animation->addFrameWithTexture(mgr->getTexture(), CCRectMake(x * 32, 0, 31, 30));
13 }
14 // 创建并执行动画效果,而且要重复10次。
15 CCAnimate *action = CCAnimate::actionWithAnimation(animation);
16 flight->runAction(CCRepeat::actionWithAction(action, 10));
17 }
知易游戏开发教程cocos2d-x移植版003

无限重复 – CCRepeatForever

CCRepeatForever Class Reference中,有这样一条警告“Warning:
This action can’t be Sequenceable because it is not an IntervalAction”,而实际上它的确是派生自CCActionInterval,这真有点儿把我也搞懵了。

仅从其本意来说,该类的作用就是无限期执行某个动作或动作序列,直到被停止。因此无法参与序列和同步,自身也无法反向执行(但是你可以将某一动作反向,然后无限重复执行)。

知易游戏开发教程cocos2d-x移植版003
 1 void CompositionActionLayer::onRepeatForever(CCObject* pSender)
2 {
3 CCSpriteBatchNode *mgr = static_cast<CCSpriteBatchNode *>(this->getChildByTag(4));
4 // 飞行喷火模拟动画
5 CCAnimation *animation = CCAnimation::animation();
6 animation->setName("flight");
7 animation->setDelay(0.1f);
8 for (int i = 0; i < 3; ++i)
9 {
10 float x = static_cast<float>(i % 3);
11 animation->addFrameWithTexture(mgr->getTexture(), CCRectMake(x * 32, 0, 31, 30));
12 }
13 CCAnimate *action = CCAnimate::actionWithAnimation(animation);
14 // 将该动画作为精灵的本征动画,一直运行。
15 flight->runAction(CCRepeatForever::actionWithAction(action));
16
17 CCSize s = CCDirector::sharedDirector()->getWinSize();
18 flight->setRotation(0.0f);
19 flight->setPosition(ccp(100, 50));
20
21 // 创建第二个连续无限期动作序列,叠加两者形成完整效果。
22 ccBezierConfig bezier;
23 bezier.controlPoint_1 = ccp(0, s.height / 2);
24 bezier.controlPoint_2 = ccp(300, -s.height / 2);
25 bezier.endPosition = ccp(300, 100);
26 CCFiniteTimeAction *action1 = CCBezierBy::actionWithDuration(3.0f, bezier);
27 CCFiniteTimeAction *action2 = CCTintBy::actionWithDuration(0.5f, 0, 255, 255);
28 CCFiniteTimeAction *action3 = CCSpawn::actions(action1, CCRepeat::actionWithAction(action2, 4), NULL);
29 CCFiniteTimeAction *action4 = CCSpawn::actions(action1->reverse(), CCRepeat::actionWithAction(action2, 4), NULL);
30 // CCSequence的actions成员函数返回的是CCFiniteTimeAction指针类型,
31 // 而CCRepeatForever的actionWithAction接受的是CCActionInterval指针类型,
32 // 所以这里需要强转一下,转成CCSequence指针类型,
33 // 只要保证序列中有2个或2个以上的动作,这么做是绝对没有问题的。
34 flight->runAction(CCRepeatForever::actionWithAction(static_cast<CCSequence *>(CCSequence::actions(action3, action4, NULL))));
35 }
知易游戏开发教程cocos2d-x移植版003

速度变化

基本动作和组合动作实现了针对精灵的各种运动和动画效果,但它们的速度通常是恒定不变的。通过CCActionEase类系和CCSpeed类,我们可以很方便地改变精灵执行动作的速度,是由快至慢还是由慢至快。

知易游戏开发教程cocos2d-x移植版003

CCEaseIn – 由慢至快(速度线性变化)

CCEaseOut – 由快至慢

CCEaseInOut – 由慢至快再由快至慢

CCEaseSineIn – 由慢至快(速度正弦变化)

CCEaseSineOut – 由快至慢

CCEaseSineInOut – 由慢至快再由快至慢

CCEaseExponentialIn – 由慢至极快(速度指数级变化)

CCEaseExponentialOut – 由极快至慢

CCEaseExponentialInOut – 由慢至极快再由极快至慢

CCSpeed – 人工设定速度,还可通过setSpeed不断调整。

扩展动作

我们已经掌握了各种各样的动作,也可以按照不同的速度要求修改动作执行的时间,cocos2d-x还提供了针对现有动作的扩展,以实现各种灵活的效果。

知易游戏开发教程cocos2d-x移植版003

延时 – CCDelayTime

通过CCDelayTime,我们可以在动作序列中增加一个时间间歇。

知易游戏开发教程cocos2d-x移植版003
1 void ExtendActionLayer::onDelay(CCObject* pSender)
2 {
3 CCFiniteTimeAction *action1 = CCMoveBy::actionWithDuration(1.2f, ccp(200, 200));
4 CCFiniteTimeAction *action2 = action1->reverse();
5 // 实现一个等待间歇
6 flight->runAction(CCSequence::actions(action1, CCDelayTime::actionWithDuration(1.0f), action2, NULL));
7 }
知易游戏开发教程cocos2d-x移植版003

函数调用

在动作序列中间或者末尾调用某个函数,执行任何任务:动作、状态修改等。在cocos2d-x中,调用函数的动作一共有4种。

1.CCCallFunc

仅函数调用,无任何参数。

知易游戏开发教程cocos2d-x移植版003
 1 void ExtendActionLayer::onCallBack1()
2 {
3 flight->runAction(CCTintBy::actionWithDuration(0.5f, 255, 0, 255));
4 }
5
6 void ExtendActionLayer::onCallFunc(CCObject* pSender)
7 {
8 CCFiniteTimeAction *action1 = CCMoveBy::actionWithDuration(2.0f, ccp(200, 200));
9 CCFiniteTimeAction *action2 = action1->reverse();
10 CCFiniteTimeAction *actionF = CCCallFunc::actionWithTarget(this, callfunc_selector(ExtendActionLayer::onCallBack1));
11 flight->runAction(CCSequence::actions(action1, actionF, action2, NULL));
12 }
知易游戏开发教程cocos2d-x移植版003

2.CCCallFuncN

调用函数,并将当前对象的指针(CCNode指针)作为第一个参数传递进去。

知易游戏开发教程cocos2d-x移植版003
 1 void ExtendActionLayer::onCallBack2(CCNode* pSender)
2 {
3 // 在这个例子里,pSender就是flight,因为是他执行了那个actionF
4 pSender->runAction(CCTintBy::actionWithDuration(1.0f, 255, 0, 255));
5 }
6
7 void ExtendActionLayer::onCallFuncN(CCObject* pSender)
8 {
9 CCFiniteTimeAction *action1 = CCMoveBy::actionWithDuration(2.0f, ccp(200, 200));
10 CCFiniteTimeAction *action2 = action1->reverse();
11 CCFiniteTimeAction *actionF = CCCallFuncN::actionWithTarget(this, callfuncN_selector(ExtendActionLayer::onCallBack2));
12 flight->runAction(CCSequence::actions(action1, actionF, action2, NULL));
13 }
知易游戏开发教程cocos2d-x移植版003

3.CCCallFuncND

在前一种方式的基础上增加一个数据参数,这是void指针类型。

知易游戏开发教程cocos2d-x移植版003
 1 void ExtendActionLayer::onCallBack3(CCNode* pSender, void* pData)
2 {
3 pSender->runAction(CCTintBy::actionWithDuration(static_cast<float>((int)pData), 255, 0, 255));
4 }
5
6 void ExtendActionLayer::onCallFuncND(CCObject* pSender)
7 {
8 CCFiniteTimeAction *action1 = CCMoveBy::actionWithDuration(2.0f, ccp(200, 200));
9 CCFiniteTimeAction *action2 = action1->reverse();
10 // 这里直接将整数常量强转成(void *)类型似乎有欠妥当,但对这些Action的生命周期不太清楚,稍后深入一下。
11 CCFiniteTimeAction *actionF = CCCallFuncND::actionWithTarget(this, callfuncND_selector(ExtendActionLayer::onCallBack3), (void *)2);
12 flight->runAction(CCSequence::actions(action1, actionF, action2, NULL));
13 }
知易游戏开发教程cocos2d-x移植版003

4.CCCallFuncO

调用函数,并传递一个CCObject指针作为参数。这个似乎不太常用,资料比较少,以后再深入。

小结

至此,我们对cocos2d-x支持的动作有了整体了解。动作是我们的好帮手,它让游戏世界充满生机。在后面的章节中,我们会对部分动作以及动作系统继续深入。

示例代码下载:

http://files.cnblogs.com/cocos2d-x/ZYG003.rar

分类: 未分类 标签:

Cocos2d-x 2.0 TestCpp框架源码分析

2012年10月31日 没有评论

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]

红孩儿Cocos2d-X学习园地QQ群:249941957 加群写:Cocos2d-x

本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!

    Cocos2d-x2.0 TestCpp框架源码分析       

             [本版教程使用的Cocos2d-x版本为cocos2d-2.0-x-2.0.2]


         好的引擎,会提供一系列完整的功能示例,Cocos2d-x之所以能得到很多人的喜爱,其重要的原因是它提供了丰富而易学的示例。在cocos2d-2.0-x-2.0.2中这些示例被放在一个名叫TestCpp的工程中,为了更好的学习Cocos2d-x的功能示例,我们今天来学习一下这个工程的框架结构。


         在VS的解决方案里展开TestCpp工程,其下有43个示例目录,除此之前还有几个文件:

 

AppDelegate.h/cpp : 程序控制类AppDelegate 。

controller.h/cpp:示例场景管理类TestController,用于显示所有示例的菜单。

testBasic.h/cpp:示例场景基类TestScene,用于返回到主界面场景。

testResource.h:文件资源名称字符串定义头文件

tests.h:示例总头文件

main.h/cpp:主函数及头文件

 

          所有的示例都是写在单独的由TestScene派生场景类中,在这些场景中加入一些由CCLayer派生的示例对象来实现相应功能的展示。

 

          与HelloWorld一样,我们需要在main.cpp中创建AppDelegate实例,并设置窗口大小,启动游戏程序。

#include "main.h"
#include "AppDelegate.h"
#include "CCEGLView.h"
USING_NS_CC;
//WIN32程序主函数
int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
	//实例化一个Cocos2d-x程序对象
    AppDelegate app;
	//创建OpenGL视窗并设置窗口大小
    CCEGLView* eglView = CCEGLView::sharedOpenGLView();
    eglView->setFrameSize(480, 320);
	//运行程序对象
    return CCApplication::sharedApplication()->run();
}


 

    在AppDelegate.cpp中的boolAppDelegate::applicationDid。FinishLaunching()函数中创建场景并运行场景

bool AppDelegate::applicationDidFinishLaunching()
{
    // 取得当前显示设备指针并设置其OpenGL视窗
    CCDirector *pDirector = CCDirector::sharedDirector();
    pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
	//取得当前运行硬件平台
    TargetPlatform target = getTargetPlatform();
    //如果是IPAD
    if (target == kTargetIpad)
    {
		//如果能启动高清屏,使用高精度资源,否则使用一般精度资源
        if (pDirector->enableRetinaDisplay(true))
        {
            CCFileUtils::sharedFileUtils()->setResourceDirectory("ipadhd");
        }
        else 
        {
            CCFileUtils::sharedFileUtils()->setResourceDirectory("ipad");
        }
    }
	//如果是Iphone
    else if (target == kTargetIphone)
    {
		//如果能启动高清屏,使用高精度资源
        if (pDirector->enableRetinaDisplay(true))
        {
            CCFileUtils::sharedFileUtils()->setResourceDirectory("hd");
        }
    }

    // 设置FPS信息显示
    pDirector->setDisplayStats(true);

    //设置帧间隔时间
    pDirector->setAnimationInterval(1.0 / 60);
	//在这里创建一个场景
    CCScene * pScene = CCScene::create();
	//实例化一个TestController,它是一个CCLayer。
    CCLayer * pLayer = new TestController();
	//设置由内存管理器来进行自动回收,不必手动DELETE
    pLayer->autorelease();
	//将它放置到场景中
    pScene->addChild(pLayer);
	//运行这个新创建的场景。
    pDirector->runWithScene(pScene);

    return true;
}


 

   
下面我们来看一下testBase.h/cpp,这里面有一个所有示例都要用到的场景基类TestScene。

#ifndef _TEST_BASIC_H_
#define _TEST_BASIC_H_
//Cocos2d头文件
#include "cocos2d.h"
//使用Cocos2d命名空间
USING_NS_CC;
using namespace std;

class TestScene : public CCScene
{
public: 
	//构造
    TestScene(bool bPortrait = false);
	//场景被加载时的回调函数
    virtual void onEnter();
	//自定义的虚函数,用于运行场景。
    virtual void runThisTest() = 0;
    //返回主界面的菜单按钮响应回调函数。
    virtual void MainMenuCallback(CCObject* pSender);
};

#endif

cpp文件:

#include "testBasic.h"
#include "controller.h"
//构造函数
TestScene::TestScene(bool bPortrait)
{
    
    CCScene::init();
}
//场景被加载时的回调函数
void TestScene::onEnter()
{
    CCScene::onEnter();

   	  //#if (CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE)
//    CCLabelBMFont* label = CCLabelBMFont::create("MainMenu",  "fonts/arial16.fnt");
//#else
	//创建一个文字标签,显示“MainMenu”字符串
    CCLabelTTF* label = CCLabelTTF::create("MainMenu", "Arial", 20);
//#endif
	//增加一个菜单标签,设定其被按下时的回调函数,用于返回到主界面
    CCMenuItemLabel* pMenuItem = CCMenuItemLabel::create(label, this, menu_selector(TestScene::MainMenuCallback));
	//将菜单标签加入创建的菜单中,并设置相应位置。
    CCMenu* pMenu =CCMenu::create(pMenuItem, NULL);
    CCSize s = CCDirector::sharedDirector()->getWinSize();
    pMenu->setPosition( CCPointZero );
    pMenuItem->setPosition( CCPointMake( s.width - 50, 25) );
	//将菜单放入场景中。
    addChild(pMenu, 1);
}
//菜单标签被按下时的响应函数
void TestScene::MainMenuCallback(CCObject* pSender)
{
	//新创建一个场景。
    CCScene* pScene = CCScene::create();
	//新创建一个TestController对象
    CCLayer* pLayer = new TestController();
    pLayer->autorelease();
	//将它放入新创建的场景中并运行。
    pScene->addChild(pLayer);
    CCDirector::sharedDirector()->replaceScene(pScene);
}

 

         在TestScene中,提供了一个可以被点击的标签MainMenu,从字面意思就知道点击它可以返回主菜单界面,这个主菜单在哪呢?就是TestController。

#ifndef _CONTROLLER_H_
#define _CONTROLLER_H_

#include "cocos2d.h"

USING_NS_CC;

class TestController : public CCLayer
{
public:
	//构造
    TestController();
	//析构
    ~TestController();
	//菜单项响应回调函数
    void menuCallback(CCObject * pSender);
	//关闭程序响应回调函数
    void closeCallback(CCObject * pSender);
	//按下触点响应函数。
    virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
	//按下状态移动触点响应函数。
    virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent);

private:
	//接下状态移动触点的开始位置
    CCPoint m_tBeginPos;
	//主菜单
    CCMenu* m_pItemMenu;
};

#endif

进入Cpp看一下:

#include "controller.h"
#include "testResource.h"
#include "tests.h"
//菜单项的行间距 ,设40点
#define LINE_SPACE          40
//创建一个当前触点位置变量,初始化为左下角位置。
static CCPoint s_tCurPos = CCPointZero;
//创建指定的示例场景
static TestScene* CreateTestScene(int nIdx)
{
	//先清空一下显示设备使用的数据
    CCDirector::sharedDirector()->purgeCachedData();

	//定义TestScene指针用于接收创建的示例场景
    TestScene* pScene = NULL;
	//跟据参数来创建相应的TestScene派生类对象。
    switch (nIdx)
    {
    case TEST_ACTIONS://动画处理
        pScene = new ActionsTestScene(); break;
    case TEST_TRANSITIONS://场景切换
        pScene = new TransitionsTestScene(); break;
     case TEST_PROGRESS_ACTIONS://进度动画控制
         pScene = new ProgressActionsTestScene(); break;
    case TEST_EFFECTS://特效演示
        pScene = new EffectTestScene(); break;
    case TEST_CLICK_AND_MOVE://拖动演示
        pScene = new ClickAndMoveTestScene(); break;
    case TEST_ROTATE_WORLD://旋转CCLayer
        pScene = new RotateWorldTestScene(); break;
    case TEST_PARTICLE://粒子系统
        pScene = new ParticleTestScene(); break;
    case TEST_EASE_ACTIONS://多样化运动动画
        pScene = new ActionsEaseTestScene(); break;
    case TEST_MOTION_STREAK://拖尾动画效果
        pScene = new MotionStreakTestScene(); break;
    case TEST_DRAW_PRIMITIVES://基本图形绘制
        pScene = new DrawPrimitivesTestScene(); break;
    case TEST_COCOSNODE://结点控制
        pScene = new CocosNodeTestScene(); break;
    case TEST_TOUCHES://触屏处理
        pScene = new PongScene(); break;
    case TEST_MENU://菜单处理
        pScene = new MenuTestScene(); break;
    case TEST_ACTION_MANAGER://动画管理
        pScene = new ActionManagerTestScene(); break;
    case TEST_LAYER://CCLayer功能展示
        pScene = new LayerTestScene(); break;
    case TEST_SCENE://CCScene功能展示
        pScene = new SceneTestScene(); break;
    case TEST_PARALLAX://视角控制
        pScene = new ParallaxTestScene(); break;
    case TEST_TILE_MAP://基于格子的地图
        pScene = new TileMapTestScene(); break;
    case TEST_INTERVAL://时间控制
        pScene = new IntervalTestScene(); break;
    case TEST_CHIPMUNKACCELTOUCH://ChipMunk物理引擎与多点触屏
#if (CC_TARGET_PLATFORM != CC_PLATFORM_MARMALADE)
        pScene = new ChipmunkAccelTouchTestScene(); break;
#else
#ifdef MARMALADEUSECHIPMUNK
#if    (MARMALADEUSECHIPMUNK == 1)
        pScene = new ChipmunkAccelTouchTestScene(); 
#endif
        break;
#endif
#endif
    case TEST_LABEL://文字标签
        pScene = new AtlasTestScene(); break;
    case TEST_TEXT_INPUT://输入处理
        pScene = new TextInputTestScene(); break;
    case TEST_SPRITE://精灵功能展示
        pScene = new SpriteTestScene(); break;
    case TEST_SCHEDULER://动画的时间调度
        pScene = new SchedulerTestScene(); break;
    case TEST_RENDERTEXTURE://渲染到纹理
        pScene = new RenderTextureScene(); break;
    case TEST_TEXTURE2D://纹理功能展示
        pScene = new TextureTestScene(); break;
    case TEST_BOX2D://Box2D物理引擎展示
        pScene = new Box2DTestScene(); break;
    case TEST_BOX2DBED: //Box2D物理引擎展示[物体的固定与连接]
        pScene = new Box2dTestBedScene(); break;
    case TEST_EFFECT_ADVANCE://高级屏幕效果
        pScene = new EffectAdvanceScene(); break;
    case TEST_ACCELEROMRTER://加速度
        pScene = new AccelerometerTestScene(); break;
#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)
    case TEST_KEYPAD://键区处理
        pScene = new KeypadTestScene(); break;
#endif
    case TEST_COCOSDENSHION://声效控制
        pScene = new CocosDenshionTestScene(); break;
    case TEST_PERFORMANCE://性能测试
        pScene = new PerformanceTestScene(); break;
    case TEST_ZWOPTEX://ZWOPTEX动国
        pScene = new ZwoptexTestScene(); break;
#if (CC_TARGET_PLATFORM != CC_PLATFORM_MARMALADE)
// bada don't support libcurl
#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)
    case TEST_CURL://网络通信基本演示
        pScene = new CurlTestScene(); break;
#endif
#endif
    case TEST_USERDEFAULT://用户日志保存示例
        pScene = new UserDefaultTestScene(); break;
    case TEST_BUGS://BUG示例
        pScene = new BugsTestScene(); break;
    case TEST_FONTS://字体演示
        pScene = new FontTestScene(); break;
    case TEST_CURRENT_LANGUAGE://获取当前语言
        pScene = new CurrentLanguageTestScene(); break;
#if (CC_TARGET_PLATFORM != CC_PLATFORM_MARMALADE)
    case TEST_TEXTURECACHE: pScene = new TextureCacheTestScene(); break;
#endif
    case TEST_EXTENSIONS:
        {	//更多扩展实例
            pScene = new ExtensionsTestScene();
        }
        break;
    case TEST_SHADER://使用Shader的渲染演示
        pScene = new ShaderTestScene();
        break;
    case TEST_MUTITOUCH://多点触屏
        pScene = new MutiTouchTestScene();
        break;
    default:
        break;
    }
	//返回创建的场景
    return pScene;
}
//构造函数
TestController::TestController()
: m_tBeginPos(CCPointZero)
{
    // 加入一个关闭菜单按钮
    CCMenuItemImage *pCloseItem = CCMenuItemImage::create(s_pPathClose, s_pPathClose, this, menu_selector(TestController::closeCallback) );
	//创建一个菜单放置关闭按钮
    CCMenu* pMenu =CCMenu::create(pCloseItem, NULL);
    CCSize s = CCDirector::sharedDirector()->getWinSize();
	//设置这个菜单位置
    pMenu->setPosition( CCPointZero );
    pCloseItem->setPosition(CCPointMake( s.width - 30, s.height - 30));

    //新建一个测试项菜单,将所有代表测试场景的菜单标签项加入菜单
    m_pItemMenu = CCMenu::create();
	//
    for (int i = 0; i < TESTS_COUNT; ++i)
    {
// #if (CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE)
//         CCLabelBMFont* label = CCLabelBMFont::create(g_aTestNames[i].c_str(),  "fonts/arial16.fnt");
// #else
        CCLabelTTF* label = CCLabelTTF::create(g_aTestNames[i].c_str(), "Arial", 24);
// #endif        
        CCMenuItemLabel* pMenuItem = CCMenuItemLabel::create(label, this, menu_selector(TestController::menuCallback));
		//将代表测试场景的菜单标签加入菜单中,以i+10000的值做为Z值。
        m_pItemMenu->addChild(pMenuItem, i + 10000);
		//设置代表测试场景的菜单标签在测试项菜单层的位置
        pMenuItem->setPosition( CCPointMake( s.width / 2, (s.height - (i + 1) * LINE_SPACE) ));
    }
	//设置测试项菜单的大小
    m_pItemMenu->setContentSize(CCSizeMake(s.width, (TESTS_COUNT + 1) * (LINE_SPACE)));
	//设置测试项菜单的位置
    m_pItemMenu->setPosition(s_tCurPos);
	//将测试项菜单放入当前CCLayer
    addChild(m_pItemMenu);
	//设置开启触屏事件响应
    setTouchEnabled(true);
	//将关闭按钮项菜单放入当前CCLayer
    addChild(pMenu, 1);

}

TestController::~TestController()
{
}
//点击菜单项的事件响应函数
void TestController::menuCallback(CCObject * pSender)
{
    // 取得菜单项
    CCMenuItem* pMenuItem = (CCMenuItem *)(pSender);
	//取得Z值,减10000算出是第几个菜单项
    int nIdx = pMenuItem->getZOrder() - 10000;

    //创建相应的场景
    TestScene* pScene = CreateTestScene(nIdx);
    if (pScene)
    {
		//运行当前测试场景
        pScene->runThisTest();
		//runThisTest中会对其引用计数加1变为2,这里做一次引用计数减1操作,以保证后面再释放场景时可以正常的释放。
        pScene->release();
    }
}
//点击关闭按钮时的响应处理
void TestController::closeCallback(CCObject * pSender)
{
	//退出程序
    CCDirector::sharedDirector()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
}
//触点按下时的响应处理
void TestController::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
{
	//获取第一个触点位置
    CCSetIterator it = pTouches->begin();
    CCTouch* touch = (CCTouch*)(*it);
	//将位置保存到变量m_tBeginPos中。
    m_tBeginPos = touch->getLocation();    
}
//触点
void TestController::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent)
{
	//获取第一个触点位置
    CCSetIterator it = pTouches->begin();
    CCTouch* touch = (CCTouch*)(*it);
	//取得这个位置与上一帧移动的Y值之差,即在纵方向的偏移。
    CCPoint touchLocation = touch->getLocation();    
    float nMoveY = touchLocation.y - m_tBeginPos.y;
	//计算菜单在纵方向上也移动相应值后的新位置。
    CCPoint curPos  = m_pItemMenu->getPosition();
    CCPoint nextPos = ccp(curPos.x, curPos.y + nMoveY);
	//这里对新位置的有效范围做个限定
    CCSize winSize = CCDirector::sharedDirector()->getWinSize();
    if (nextPos.y < 0.0f)
    {
        m_pItemMenu->setPosition(CCPointZero);
        return;
    }

    if (nextPos.y > ((TESTS_COUNT + 1)* LINE_SPACE - winSize.height))
    {
        m_pItemMenu->setPosition(ccp(0, ((TESTS_COUNT + 1)* LINE_SPACE - winSize.height)));
        return;
    }
	//更新菜单到新位置
    m_pItemMenu->setPosition(nextPos);
	//记录当前位置为旧位置。
    m_tBeginPos = touchLocation;
    s_tCurPos   = nextPos;
}

 

         总结:示例框架展示了一个菜单,每个菜单项代表了一个示例工程,菜单项的Z值是递增排列的。点击菜单项响应TestController::menuCallback函数,此函数通过减去Z值基数可以取得菜单项的索引,并通过CreateTestScene(nIdx);来创建出相应的示例场景。本示例框架还演示了如何在纵方向上拖动一个菜单。

      Cocos2d-x 2.0  TestCpp框架源码分析

分类: 未分类 标签:

知易游戏开发教程cocos2d-x移植版002(下)

2012年10月31日 没有评论

原文:http://www.cnblogs.com/cocos2d-x/archive/2012/03/01/2376143.html

示例说明

上一节我们对cocos2d-x的基本概念有了初步了解,下面我们将通过一个实例对前面的概念进行实战演练。

假设我们要开发一款飞行射击类游戏,其中有3个主要画面。

1)主菜单画面

2)游戏画面

3)设置画面

知易游戏开发教程cocos2d-x移植版002(下) 知易游戏开发教程cocos2d-x移植版002(下)

知易游戏开发教程cocos2d-x移植版002(下)

在游戏场景中,我们使用一副太空的图片作为背景,游戏的主角是一架喷着火焰的飞行器,状态栏用来显示生命条数和游戏时间,当然少不了一个"Main Menu"以便返回主菜单画面。

为了让游戏看起来更加精致,我们在场景切换时添加一些效果。

幻灯片切换效果:

知易游戏开发教程cocos2d-x移植版002(下)

缩放显示切换效果:

知易游戏开发教程cocos2d-x移植版002(下)

预备知识

场景切换

实例中包含3个典型场景,分别由3个不同的层来实现。场景的切换主要使用CCDirector的replaceScene函数实现,具体代码如下:

知易游戏开发教程cocos2d-x移植版002(下)
1 void SysMenu::onNewGame(CCObject* pSender)
2 {
3 CCScene *scene = CCScene::node();
4 scene->addChild(GameLayer::node());
5 CCDirector::sharedDirector()->replaceScene(scene);
6 }
知易游戏开发教程cocos2d-x移植版002(下)

首先创建一个新的CCScene实例,接着创建一个目标层的新实例(我们这里就是GameLayer),并附加给场景实例。然后调用CCDirector的replaceScene函数,用新场景取代当前场景。

需要为画面切换增加效果时,可以使用画面切换效果类,以CCTransitionSlideInR类为例,上面的代码要修改成:

1     CCDirector::sharedDirector()->replaceScene(CCTransitionSlideInR::transitionWithDuration(1.2f, scene));

从本章开始,我们将逐步深入讲解基于cocos2d-x的代码编写。网上很多示例代码的作者没有深入研究cocos2d-x自带的例子和分析该引擎的源代码,因此写出来的代码比较随意,不规范。笔者建议大家仔细分析官方的源代码,并尽量使用标准的编程方法。

坐标系

再进一步细讲每个层的实现之前,先明确一下坐标系的概念。

一般意义上的坐标系为笛卡尔坐标系(应该是初中平面几何开始讲的吧,高中立体几何扩展到三维空间。):

知易游戏开发教程cocos2d-x移植版002(下)

不同的图形库采用不同的坐标系。iPhone平台提供了两种绘图库:Quartz 2D和OpenGL ES。其中Quartz 2D是Core Graphics绘图库的子集,OpenGL ES是跨平台图形库OpenGL的嵌入设备版。这两者的坐标系原点不一样。

Quartz 2D的原点在左上角:

知易游戏开发教程cocos2d-x移植版002(下)

大多数图形窗口应用程序都采用类似的坐标系。这是一种基于虚拟“画布”绘图模型的图形库,绘图指令按次序向“画布”上画下不同的内容,后画的内容会覆盖先画的内容(透明的除外)。这比较容易理解。

OpenGL ES的原点在左下角:

知易游戏开发教程cocos2d-x移植版002(下)

OpenGL ES相对比较复杂,这实际上是一个3D的绘图库,按照“状态机”模型设计的绘图库。他不是简单地让后者叠加在前者上面,而是记录各个绘制内容的三维位置关系,再按照系统设定的投影关系,将绘制的所有内容投影在某个特定的虚拟窗口上。

cocos2d-x是基于OpenGL ES的,所以请大家牢记我们使用笛卡尔坐标系,坐标系的原点在左下角。

cocos2d-x引擎的大多数可显示对象都是从CCNode类派生而来的,理解该类对于使用该图形引擎至关重要。今天我们先来说明与位置相关的属性:

1)anchorPoint(锚点)

为了将一个矩形图像精准地放置在屏幕某一位置上,需要设置该矩形的位置参考点。通常人们习惯于将该参考点设置在矩形的左上角上,而在cocos2d-x中我们称其为锚点,默认位置在矩形的中心。

2)Position(位置)

Position就是CCNode对象实际的OpenGL ES坐标。

下图说明了如何利用这两个属性来设置CCNode对象的显示位置:

知易游戏开发教程cocos2d-x移植版002(下)

图中红色矩形框的Position为(5, 5),anchorPoint为(0.3, 0.5)。若要选择紫色大圆点A为锚点,则设置anchorPoint为(0, 0),如要选择粉红色大圆点B为锚点,则设置anchorPoint为(1, 1)。显然设置为(0.5, 0.5)时,锚点位于矩形对象的几何中心点C,这是CCSprite类对象的默认anchorPoint值。

代码分析

上文说明了几个场景之间的切换。下面,我们重点介绍每个场景内部的实现。

SysMenu

作为游戏的主菜单,SysMenu的实现并不复杂。

1)主菜单的构建

知易游戏开发教程cocos2d-x移植版002(下)
 1 bool SysMenu::init(void)
2 {
3 bool bRet = false;
4 do
5 {
6 CC_BREAK_IF(! CCLayer::init());
7
8 // 用一个图片做画面的背景
9 CCSprite *sp = CCSprite::spriteWithFile("bg.png");
10 CC_BREAK_IF(! sp);
11 sp->setAnchorPoint(CCPointZero);
12 // 既然是背景,zOrder值尽量小。
13 this->addChild(sp, 0, 1);
14
15 // 设置菜单项字体
16 CCMenuItemFont::setFontName("Marker Felt");
17 CCMenuItemFont::setFontSize(25);
18
19 // 逐一创建5个菜单项
20 CCMenuItemFont *newGame = CCMenuItemFont::itemFromString("New Game", this, menu_selector(SysMenu::onNewGame));
21 CC_BREAK_IF(! newGame);
22 CCMenuItemFont *loadGame = CCMenuItemFont::itemFromString("Load");
23 CC_BREAK_IF(! loadGame);
24 CCMenuItemFont *gameSettings = CCMenuItemFont::itemFromString("Option", this, menu_selector(SysMenu::onSettings));
25 CC_BREAK_IF(! gameSettings);
26 CCMenuItemFont *help = CCMenuItemFont::itemFromString("Help");
27 CC_BREAK_IF(! help);
28 CCMenuItemFont *quit = CCMenuItemFont::itemFromString("Quit", this, menu_selector(SysMenu::onQuit));
29 CC_BREAK_IF(! quit);
30
31 // 将这些菜单项都加入到菜单对象中
32 CCMenu *menu = CCMenu::menuWithItems(newGame, loadGame, gameSettings, help, quit, NULL);
33 CC_BREAK_IF(! menu);
34 // 设置菜单以纵向布局显示
35 menu->alignItemsVertically();
36 // 最重要的是不要忘记把菜单加入主层对象,否则不会显示
37 this->addChild(menu, 1, 2);
38
39 bRet = true;
40 } while (0);
41
42 return bRet;
43 }
知易游戏开发教程cocos2d-x移植版002(下)

需要讲解的核心函数是itemFromString,他是CCMenuItemFont的一个静态成员函数,能够帮助我们创建CCMenuItemFont菜单项。

第一个参数比较直接,就是菜单项显示的文字。

第二个参数是响应菜单事件的对象,通常是菜单所属的层对象。

第三个参数是菜单事件对应的回调函数。

2)切换到其他场景

以下两个函数onNewGame和onSettings被作为参数传递给菜单构建函数,他们的作用都是进行场景切换。

知易游戏开发教程cocos2d-x移植版002(下)
 1 void SysMenu::onNewGame(CCObject* pSender)
2 {
3 CCScene *scene = CCScene::node();
4 // 特别说明,我们将游戏层和控制层在此分开,由scene作为他们统一的父节点。
5 scene->addChild(GameLayer::node());
6 scene->addChild(GameControlMenu::node());
7 // 从右至左滚动画面切换到游戏场景。
8 CCDirector::sharedDirector()->replaceScene(CCTransitionSlideInR::transitionWithDuration(1.2f, scene));
9 }
10
11 void SysMenu::onSettings(CCObject* pSender)
12 {
13 CCScene *scene = CCScene::node();
14 scene->addChild(SettingsLayer::node());
15 // 缩放变换切换到游戏设置画面。
16 CCDirector::sharedDirector()->replaceScene(CCTransitionShrinkGrow::transitionWithDuration(1.2f, scene));
17 }
知易游戏开发教程cocos2d-x移植版002(下)

GameLayer

作为游戏的主画面,主要包含以下几个关键要素:

1)状态栏:显示主角的生命数,得分,游戏时间。

2)控制栏:游戏需要的控制菜单,动作按钮等。

3)场景画面:游戏场景、NPC、助教角色动作(是主要角色动作吗?)。

下面我们详细介绍GameLayer的三个重点:

主画面的创建

知易游戏开发教程cocos2d-x移植版002(下)
 1 bool GameLayer::init(void)
2 {
3 bool bRet = false;
4 do
5 {
6 CC_BREAK_IF(! CCLayer::init());
7
8 // 游戏场景背景图
9 CCSprite *bg = CCSprite::spriteWithFile("Space.png");
10 CC_BREAK_IF(! bg);
11 bg->setAnchorPoint(CCPointZero);
12 // 为了突出游戏场景中的精灵,将背景色彩变淡
13 bg->setOpacity(100);
14 this->addChild(bg, 0, 1);
15
16 // 使用位图字体显示游戏时间
17 CCLabelBMFont *lbScore = CCLabelBMFont::labelWithString("Time: 0", "font09.fnt");
18 CC_BREAK_IF(! lbScore);
19 lbScore->setAnchorPoint(ccp(1, 1));
20 lbScore->setScale(0.6f);
21 this->addChild(lbScore, 1, 3);
22 lbScore->setPosition(ccp(310, 450));
23
24 // 载入飞船图像集。整个图像集仅载入一次,可以被使用多次。
25 CCSpriteBatchNode *mgr = CCSpriteBatchNode::batchNodeWithFile("flight.png", 5);
26 CC_BREAK_IF(! mgr);
27 this->addChild(mgr, 0, 4);
28
29 // 在状态栏显示一个飞船的图标
30 CCSprite *sprite = CCSprite::spriteWithTexture(mgr->getTexture(), CCRectMake(0, 0, 31, 30));
31 CC_BREAK_IF(! sprite);
32 mgr->addChild(sprite, 1, 5);
33 sprite->setScale(1.1f);
34 sprite->setAnchorPoint(ccp(0, 1));
35 sprite->setPosition(ccp(10, 460));
36
37 // 显示当前飞船生命条数
38 CCLabelBMFont *lbLife = CCLabelBMFont::labelWithString("3", "font09.fnt");
39 CC_BREAK_IF(! lbLife);
40 lbLife->setAnchorPoint(ccp(0, 1));
41 lbLife->setScale(0.6f);
42 this->addChild(lbLife, 1, 6);
43 lbLife->setPosition(ccp(50, 450));
44
45 // 设定时间回调函数,修改游戏用时显示
46 this->schedule(schedule_selector(GameLayer::step), 1.0f);
47
48 // 显示飞船。为了让飞船有不断闪烁的火焰喷射效果,这是一个简单的重复性动作。
49 // 在后面的章节中我们会专门讲解动作。
50 flight = CCSprite::spriteWithTexture(mgr->getTexture(), CCRectMake(0, 0, 31, 30));
51 CC_BREAK_IF(! flight);
52 flight->setPosition(ccp(160, 30));
53 flight->setScale(1.6f);
54 mgr->addChild(flight, 1, 99);
55
56 // 设定动画每一帧的内容
57 CCAnimation *animation = CCAnimation::animation();
58 CC_BREAK_IF(! animation);
59 animation->setName("flight");
60 animation->setDelay(0.2f);
61 for (int i = 0; i < 3; ++i)
62 {
63 int x = i % 3;
64 animation->addFrameWithTexture(mgr->getTexture(), CCRectMake(x * 32, 0, 31, 30));
65 }
66
67 // 基于动画创建动作
68 CCAnimate *action = CCAnimate::actionWithAnimation(animation);
69 CC_BREAK_IF(! action);
70 // 主角精灵不断重复动作,实现动态飞行效果
71 flight->runAction(CCRepeatForever::actionWithAction(action));
72
73 // accept touch now!
74 this->setIsTouchEnabled(true);
75
76 bRet = true;
77 } while (0);
78
79 return bRet;
80 }
知易游戏开发教程cocos2d-x移植版002(下)

计时器

在游戏设计时,我们需要不断的改变屏幕显示来反映游戏操作的效果,最简单的就是提示用户已经进行的游戏时间。为此,我们需要使用cocos2d-x内置的任务调度机制,即CCNode的schedule成员函数。

1     // 设定时间回调函数,修改游戏用时显示
2 this->schedule(schedule_selector(GameLayer::step), 1.0f);

schedule的作用类似计时器,按照指定的时间间隔不断调用某个指定的回调函数。在我们的演示程序中,这个回调函数是这样的:

知易游戏开发教程cocos2d-x移植版002(下)
1 void GameLayer::step(ccTime dt)
2 {
3 time += dt;
4 char time_str[20];
5 sprintf(time_str, "Time: %d", (int)time);
6 CCLabelBMFont *label1 = (CCLabelBMFont *)this->getChildByTag(3);
7 label1->setString(time_str);
8 }
知易游戏开发教程cocos2d-x移植版002(下)

这段代码的作用十分简单,每隔1秒记录一下新的时间值,并修改游戏用时的画面显示。需要特别说明的是本函数的参数dt,这是系统决定的,是上一次调用到本次调用的时间间隔。因此为了计算游戏的用时,我们只需将它与累积时间相加即可。

按照时间间隔检查内部对象之前的相互作用,及时更新画面。这是游戏编程中经常要用到的逻辑。

对于这样的处理,我们是统一设定一个处理所有检查反馈逻辑的计时器呢,还是使用多个专用的计时器,这两种方法哪一个效率更高?cocos2d-iphone的作者指出:小于20个的专用计时器和一个复杂时间计时器的效率基本相同。

可以通过CCNode的unschedule成员函数来取消计时器。

简单的动作

让游戏中的精灵执行一次或者重复执行某个动画动作是游戏中经常遇到的,因此cocos2d-x引擎支持一个重要的概念Action(动作)。本例中我们只是给出了一个简单的“喷火”动画效果来模拟飞船的飞行。后面我们将有专门的章节来详细说明动作。

为了允许飞船在用户的操作下移动,我们增加了Touch事件的响应:

知易游戏开发教程cocos2d-x移植版002(下)
1 void GameLayer::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)
2 {
3 CCTouch *touch = (CCTouch *)pTouches->anyObject();
4 // 获得触摸点坐标
5 CCPoint location = touch->locationInView(touch->view());
6 CCPoint convertedLocation = CCDirector::sharedDirector()->convertToGL(location);
7 // 让飞船在1秒钟内移动过去
8 flight->runAction(CCMoveTo::actionWithDuration(1.0f, ccp(convertedLocation.x, convertedLocation.y)));
9 }
知易游戏开发教程cocos2d-x移植版002(下)

这里又用到了一个简单的动作:CCMoveTo。他的作用像他的名字一样直接,就是移动到某个点。第一个参数是动作执行的时间,第二参数是目标点的坐标。

GameControlMenu

完全可以将菜单按钮放置在GameLayer层实现,但考虑到将来画面滚动等问题,我们把游戏界面上的控制画面分离出来,由一个独立的层实现。

目前来说,这上面只有唯一的一个菜单项。

知易游戏开发教程cocos2d-x移植版002(下)
 1 bool GameControlMenu::init(void)
2 {
3 bool bRet = false;
4 do
5 {
6 CC_BREAK_IF(! CCLayer::init());
7
8 // 控制栏部分出于简单考虑,只有一个切换到主菜单的菜单项
9 CCMenuItemFont::setFontSize(22);
10 CCMenuItemFont *systemMenu = CCMenuItemFont::itemFromString("Main Menu",
11 this, menu_selector(GameControlMenu::sysMenu));
12 CC_BREAK_IF(! systemMenu);
13 CCMenu *menu = CCMenu::menuWithItems(systemMenu, NULL);
14 CC_BREAK_IF(! menu);
15 menu->setPosition(ccp(0, 0));
16 // 手工设置菜单位置在左下角
17 systemMenu->setAnchorPoint(ccp(0, 0));
18 systemMenu->setPosition(ccp(0, 0));
19 this->addChild(menu, 1, 2);
20
21 bRet = true;
22 } while (0);
23
24 return bRet;
25 }
知易游戏开发教程cocos2d-x移植版002(下)

下面的函数实现切换到主菜单画面的功能:

知易游戏开发教程cocos2d-x移植版002(下)
1 void GameControlMenu::sysMenu(CCObject* pSender)
2 {
3 CCScene *scene = CCScene::node();
4 scene->addChild(SysMenu::node());
5 CCDirector::sharedDirector()->replaceScene(CCTransitionSlideInL::transitionWithDuration(1.2f, scene));
6 }
知易游戏开发教程cocos2d-x移植版002(下)

SettingsLayer

这个画面的实现我们主要参考了cocos2d-x自带的MenuTest例子。

知易游戏开发教程cocos2d-x移植版002(下)
 1 bool SettingsLayer::init(void)
2 {
3 bool bRet = false;
4 do
5 {
6 CC_BREAK_IF(! CCLayer::init());
7
8 // 设置标题字体
9 CCMenuItemFont::setFontName("American Typewriter");
10 CCMenuItemFont::setFontSize(18);
11 // 用一个禁用状态的菜单项作为标题
12 CCMenuItemFont *title1 = CCMenuItemFont::itemFromString("Sound");
13 CC_BREAK_IF(! title1);
14 title1->setIsEnabled(false);
15
16 // 设置选项字体(设置不同的字体以示与标题的区别)
17 CCMenuItemFont::setFontName("Marker Felt");
18 CCMenuItemFont::setFontSize(26);
19 // 设置可切换的菜单项,菜单状态:开、关。
20 CCMenuItemToggle *item1 = CCMenuItemToggle::itemWithTarget(NULL, NULL,
21 CCMenuItemFont::itemFromString("On"), CCMenuItemFont::itemFromString("Off"), NULL);
22 CC_BREAK_IF(! item1);
23
24 // 设置标题字体
25 CCMenuItemFont::setFontName("American Typewriter");
26 CCMenuItemFont::setFontSize(18);
27 CCMenuItemFont *title2 = CCMenuItemFont::itemFromString("Music");
28 CC_BREAK_IF(! title2);
29 title2->setIsEnabled(false);
30
31 // 设置选项字体
32 CCMenuItemFont::setFontName("Marker Felt");
33 CCMenuItemFont::setFontSize(26);
34 CCMenuItemToggle *item2 = CCMenuItemToggle::itemWithTarget(NULL, NULL,
35 CCMenuItemFont::itemFromString("On"), CCMenuItemFont::itemFromString("Off"), NULL);
36 CC_BREAK_IF(! item2);
37
38 CCMenuItemFont::setFontName("American Typewriter");
39 CCMenuItemFont::setFontSize(18);
40 CCMenuItemFont *title3 = CCMenuItemFont::itemFromString("AI");
41 CC_BREAK_IF(! title3);
42 title3->setIsEnabled(false);
43
44 CCMenuItemFont::setFontName("Marker Felt");
45 CCMenuItemFont::setFontSize(26);
46 CCMenuItemToggle *item3 = CCMenuItemToggle::itemWithTarget(NULL, NULL,
47 CCMenuItemFont::itemFromString("Attack"), CCMenuItemFont::itemFromString("Defense"), NULL);
48 CC_BREAK_IF(! item3);
49
50 CCMenuItemFont::setFontName("American Typewriter");
51 CCMenuItemFont::setFontSize(18);
52 CCMenuItemFont *title4 = CCMenuItemFont::itemFromString("Mode");
53 CC_BREAK_IF(! title4);
54 title4->setIsEnabled(false);
55
56 // 设置多选项效果。首先加入一个子选项(subItems),再加入一个包含了多个子菜单的数组。
57 CCMenuItemFont::setFontName("Marker Felt");
58 CCMenuItemFont::setFontSize(26);
59 CCMenuItemToggle *item4 = CCMenuItemToggle::itemWithTarget(NULL, NULL,
60 CCMenuItemFont::itemFromString("Easy"), NULL);
61 CC_BREAK_IF(! item4);
62 CCMutableArray<CCMenuItem *> *more_items =
63 CCMutableArray<CCMenuItem *>::arrayWithObjects(CCMenuItemFont::itemFromString("Normal"),
64 CCMenuItemFont::itemFromString("Hard"), CCMenuItemFont::itemFromString("Nightmare"), NULL);
65 CC_BREAK_IF(! more_items);
66 // TIP: you can manipulate the items like any other CCMutableArray
67 item4->getSubItems()->addObjectsFromArray(more_items);
68 // you can change the one of the items by doing this
69 item4->setSelectedIndex(0);
70
71 CCMenuItemFont::setFontName("Marker Felt");
72 CCMenuItemFont::setFontSize(26);
73 CCLabelBMFont *label = CCLabelBMFont::labelWithString("Go back", "font01.fnt");
74 CC_BREAK_IF(! label);
75 CCMenuItemLabel *back = CCMenuItemLabel::itemWithLabel(label, this, menu_selector(SettingsLayer::backCallback));
76 CC_BREAK_IF(! back);
77 back->setScale(0.8f);
78
79 // 组合创建菜单层
80 CCMenu *menu = CCMenu::menuWithItems(title1, title2, item1, item2,
81 title3, title4, item3, item4, back, NULL);
82 CC_BREAK_IF(! menu);
83 // 设置多列的菜单项布局
84 menu->alignItemsInColumns(2, 2, 2, 2, 1, NULL);
85 this->addChild(menu);
86
87 // 手工微调一下最后一个菜单项的位置
88 cocos2d::CCPoint cp_back = back->getPosition();
89 cp_back.y -= 50.0f;
90 back->setPosition(cp_back);
91
92 bRet = true;
93 } while (0);
94
95 return bRet;
96 }
知易游戏开发教程cocos2d-x移植版002(下)

演示实例源代码可以从下面的链接获取

http://files.cnblogs.com/cocos2d-x/ZYG002.rar

分类: 未分类 标签:

知易游戏开发教程cocos2d-x移植版002(上)

2012年10月30日 没有评论

原文:http://www.cnblogs.com/cocos2d-x/archive/2012/02/29/2373598.html


基本概念

为了全面掌握cocos2d-x的开发,我们首先需要了解该引擎的几个基本概念。实际上,这些基本概念是所有游戏开发所必须的,并非cocos2d-x专有。任何游戏都是通过这些概念所针对的对象组建起来的,游戏的复杂程度决定了这些对象实现的复杂程度。

场景(CCScene)

我们假设一个只有两关的游戏:第一关(2个小鬼,1个小BOSS);第二关(5个小鬼,1个大BOSS)。通常情况下,我们会这样设计整个游戏的流程(workflow):

知易游戏开发教程cocos2d-x移植版002(上)

开场动画,可以有几个目的:

简单介绍一下游戏操作

讲述故事背景

公司或者工作室动画LOGO

进入主菜单后可以引导用户:

开始新游戏

读取进度

设置游戏 声音,文字,游戏内容设置……

高分排名 通常都是列表,按分数排列。

帮助 操作简介

退出

接下来,玩家可以有多种选择,无论开始新游戏还是读取进度,游戏都会进入到预设关卡。游戏过程中,第一关胜利就进入第二关,第二关胜利则进入结尾的胜利画面(播放视频或者在背景图上显示文字),确认后进入排名画面看看本次得了多少分。如果失败了,那就会进入失败结束的画面,确定后跳转到主菜单,重新开始。

可以看出,玩家玩游戏的过程就是在我们预设的画面之间进行跳转,根据玩家操作的结果(选择菜单项、消灭敌人或者被敌人杀死)跳转到不同的画面。

这些构成游戏流程的画面就是我们所说的场景,图中用黑色线框表示的部分。显然,不同的场景提供不同的操作,大致可以分为以下几类场景:

展示类场景:播放视频或简单的在图像上输出文字,来实现游戏的开场介绍、胜利/失败提示、帮助说明等。

选项类场景:主菜单、游戏参数设置等。

游戏场景:这是游戏的主要内容,除了这个场景之外,其他类别的场景基本都是通用框架实现的。

那么不同的场景是如何实现不同功能的呢?每个场景都是通过不同的层的叠加和组合来实现不同的功能的。因此,通常一个场景是由一个或多个层组成的。

层(CCLayer)

层是我们写游戏的重点,我们99%以上的时间是在层上实现我们的游戏内容。如下图所示,一个简单的主菜单画面是由3个层叠加实现的:

知易游戏开发教程cocos2d-x移植版002(上)

细心的读者可能已经注意到,为了让不同的层可以组合产生统一的效果,这些层基本上都是透明或者半透明的。(否则我们就只能看到最上面的一个层了)

层的叠加是有顺序的,如图所示,编号为1的背景层在最下层,2号在中间,3号在最上面。cocos2d-x也是按照这个次序来叠加画面的,处于最上层的不透明的部分会将下面的内容覆盖。

这个次序同样用于编程模型中的事件响应机制。即编号3的层最先接收到系统事件(手指单击屏幕事件),然后是编号2,最后是编号1的层。在事件的传递过程中,如果有一个层处理了该事件,那么排在后面的层将不再接收到该事件。

我们可以简单地把层理解为我们在Windows编程中的窗口(hWnd或者WinForm,还有Delphi中的TForm)。

为了方便大家进行游戏开发,cocos2d-x从技术实现角度提供一些公用层:处理菜单用的菜单层,处理颜色显示的颜色层等。

每一层又可以包含各式各样的内容要素:文本、链接、精灵、地图等等。其中,精灵是游戏的重点。

精灵(CCSprite)

精灵是整个游戏开发处理的主要对象。敌方的飞机、坦克是系统控制的精灵,玩家控制的我方飞机也是精灵,甚至随机飞过的一片云、一只鸟都是精灵。

从技术上讲,精灵就是一个可以不断变化的图片。这些变化包括:位移、旋转、缩放以及图片帧的切换。

所谓游戏,就是玩家操作一个或多个精灵与系统控制的精灵进行互动:近身肉搏、远程射击、对话等等。

导演(CCDirector)

我们已经大概了解了一个游戏的整体架构,不同的场景由不同的层组成,每个层又包含精灵在上面运动。玩家玩游戏的过程就是在操作层上的精灵或者菜单项,从而在不同的场景中切换。

好了,有些OO编程基础的读者已经猜到导演对象的作用了。是的,按照面向对象的设计原则和反向依赖原则:精灵不应该依赖层,层不应该依赖场景,场景不应该依赖整个流程。导演对象就是整个流程的代表,他负责游戏过程中的场景切换。

导演通常只有一个,因此这个对象是单例(singleton)。cocos2d-x框架已经预定义了该实例,无需额外创建,我们直接使用就可以。

导演对象接收层对象/场景对象的请求,按照预先设计好的流程来切换场景。至此,我们可以勾勒出一个游戏的整体框架和cocos2d-x关键对象与之的对应关系:

知易游戏开发教程cocos2d-x移植版002(上)

需要特别说明的是:任何时间,只有一个CCScene对象实例处于激活状态。该对象可以作为当前游戏内容的对象的容器,对于菜单对象来说,通常属于当前场景的主层。以上就是一个游戏的主要架构。

实际上,针对每一个游戏场景而言,不同场景(关卡)、每一个层(静态、动态)、每一个对象(敌人、我方、中立方)其实都很复杂。万里长征,这才第一步呢。

cocos2d-x的实现类

下面,我们首先逐一介绍cocos2d-x对应上述基本概念的对象,以及他们之间的程序关联。

CCDirector(导演类)

CCDirector对象的作用类似于Windows编程中的主窗口对象(不同之处在于该对象并不可见),它负责创建、管理应用程序/游戏的主窗口,在特定的条件下显示某个场景。

针对CCDirector的调用代码(下面这段代码是主程序启动时的标准步骤,在AppDelegate的applicationDidFinishLaunching成员函数内实现):

知易游戏开发教程cocos2d-x移植版002(上)
 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 }
知易游戏开发教程cocos2d-x移植版002(上)

显然,我们看到可以通过CCDirector对象完成以下两大类任务:

设置主程序窗口的显示属性。

依次设定了以下内容:

1.初始化CCDirector对象。

2.设置是否开启Retina显示模式

3.设置是否显示FPS。

4.设置游戏画面每秒显示的帧数,默认是60帧。

管理、显示场景。

CCDirector对象一次只能显示一个场景。为了便于管理场景对象,CCDirector对象有3个属性与场景有关(参见CCDirector.h):

知易游戏开发教程cocos2d-x移植版002(上)
1     /* 当前显示的场景 */
2 CCScene *m_pRunningScene;
3
4 /* 下一个将要显示的场景 */
5 CCScene *m_pNextScene;
6
7 /* 待执行的场景队列 */
8 CCMutableArray<CCScene*> *m_pobScenesStack;
知易游戏开发教程cocos2d-x移植版002(上)

同时,CCDirector对象管理场景的方法主要有以下几个:

知易游戏开发教程cocos2d-x移植版002(上)
 1     /* 用来显示主程序启动后的第一个场景,如果已存在运行的场景,则不可使用 */
2 void runWithScene(CCScene *pScene);
3
4 /* 挂起当前正在运行的场景,压入待执行场景队列,并开始执行新的场景 */
5 void pushScene(CCScene *pScene);
6
7 /* 从待执行场景队列中弹出一个场景,并以此取代当前运行中的场景。
8 如果待执行场景队列已为空,那么程序将终止运行。 */
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);
知易游戏开发教程cocos2d-x移植版002(上)

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的实例。

知易游戏开发教程cocos2d-x移植版002(上)

层也支持动作,可以变色、淡入淡出和混合。

CCLayerGradient(渐变层)

CCLayerGradient是CCLayerColor的子类,他可以在背景上绘制渐变色。

知易游戏开发教程cocos2d-x移植版002(上)

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是为了显示各式各样的精灵,展示游戏内容。

分类: 未分类 标签: