花了3天时间,把这个游戏简单地实现了一遍,以加深对cocos2d-x 3.0的进一步了解。在编码过程中也遇到不少问题,不过感谢两位大神的博客专栏,一个为http://blog.csdn.net/column/details/jackyairplane.html,另一个为
http://www.zaojiahua.com/category/cocos2d-xdemo/,在完成代码的过程中遇到的一些小问题都可以在上面的博客中找到了答案。
下面我来总结一下我的开发历程,加深体会的同时也可以记录一下这3天的工作,接下来,我将按照自己的开发思路来和大家分享一下我的飞机大战如何制作。
一、总体框架的设计
首先我们来认识一下这个游戏的组成
1、游戏的主要元素: (a)添加背景 (b)添加飞机 (c)添加子弹 (d)添加敌机 (e)添加ufo
2、游戏的主要成分: (a)碰撞检测 (b)分数统计 (c)播放声效 (d)暂停恢复 (e)数据存储
3、游戏设计的总体思路
分三个场景,第一个为游戏进入场景,第二个为游戏的主逻辑场景,第三个为游戏结束场景,他们的关系如下:
3.1 游戏加载场景要做的就是预加载资源,资源包括声音,图片,动画等,还可以做一些初始化的处理,譬如数据存储等。
3.2 游戏结束场景要做的就是统计当前分数,显示最高记录,并给出菜单进行下一步的操作。
3.3 游戏主场景是游戏的核心,包含了游戏的基本逻辑,下面说一下这个游戏我的思路是怎么样的:
3.3.1、Weapon层来管理飞机和子弹,因为子弹发射需要获取飞机的位置,飞机切换子弹也需要进行一些子弹类中的函数,所以我决定通过一个中介层Weapon来处理他们的交互。
3.3.2、EnemyLayer层管理敌机,由于敌机的类型有3种,我在EnemyLayer里分别用三个vector来管理不同类型的敌机。
3.3.3、UfoLayer层管理ufo,并在该层控制bomb标志的加减。
3.3.4、触摸的话我放在Weapon层里面处理,因为我觉得触摸主要是控制飞机的位置,没必要另外建一个触摸层,这样子还要考虑两个层之间的通讯。
3.3.5、碰撞检测我放在GameScene中处理,这个也算是一个联合国的方式了,把所有的元素管理层都添加在这个Scene中,处理各种交互。不过这样做貌似责任过重了,不过也方便了处理各个层之间的交互,有好有坏吧,看个人怎么权衡。
上面所说的可以简单用下图来表示:
说了那么多,接下来我们一个个来说明。
二、游戏的开始,资源的添加
资源非常宝贵,没有资源游戏写得再怎么牛B也显示不出来啊。资源的获取可以到上述第一个推荐的博客中找,另外还可以直接去下载一个飞机大战的apk,解压里面的资源。
资源的处理cocos2d-x为我们给出了几个缓存类,这里使用了SpriteFrameCache和AnimationCache。在游戏预加载场景中我用一个preloadResource的函数来统一处理资源的预加载,节选如下:
//帧缓存初始化
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("game.plist");
//缓存飞机动画
Animation* heroAnimation = Animation::create();
heroAnimation->setDelayPerUnit(0.2f);
heroAnimation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("hero1.png"));
heroAnimation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("hero2.png"));
AnimationCache::getInstance()->addAnimation(heroAnimation,"heroAnimation");
其中game.plist文件是用TexturePacker来处理的,这个软件相当方便,推荐使用。把图片打包资源存到帧缓存后我们就可以先缓存游戏设计到的动画,这里给出了缓存飞机动画的代码。把资源都处理好了,接下来我们要做的就是调用,而且调用非常便捷。
三、先来个会滚动的背景
其实在这个飞机游戏中,飞机并不是真的在飞行,而是背景在飞行。实现起来也很简单,就是两张图片在改变Y轴方向上的坐标,然后设定边界,但坐标位置到一定值的时候就返回重新开始。先创建两个背景精灵并添加图片:
//添加飞机精灵
auto winSize = Director::getInstance()->getWinSize();
_hero = Sprite::createWithSpriteFrameName("hero1.png");
_hero->setPosition(Point(winSize.width*0.5, _hero->getContentSize().height));
this->addChild(_hero,6);
//创建初始化闪烁动画
auto blink = Blink::create(1.0f, 3);
//创建飞机飞行动画
auto planeAnimate = Animate::create(AnimationCache::getInstance()->getAnimation("heroAnimation"));
_hero->runAction(blink);
_hero->runAction(RepeatForever::create(planeAnimate));
注意上面那一句注释,如果没有设置的话背景会出现一条难看的白边。然后设置坐标变换即可。
void BackgroundLayer::backgroundMove(float dt)
{
background1->setPositionY(background1->getPositionY() - 2.0f);
background2->setPositionY(background1->getPositionY() + background1->getContentSize().height - 2.0f);
if(0 == background2->getPositionY())
{
background1->setPositionY(0);
}
}
四、添加飞机,这个可是主角
在实现飞机的时候,我们到底是使它继承Node还是Sprite好呢。继承Node的话会比较灵活,里面还可以添加其他元素,可以再添加一个Sprite来初始话飞机,而继承Sprite的话貌似就直接一点,但是限制也是有的,就譬如继承之后不能使用很多Sprite中的函数,需要重写初始化图片之类的函数。由于这个主角灵活性比较强,这里我还是选择继承Node吧。
加载飞机动画,这个创建一个精灵来执行播放就OK了。