cocos2dx - 制作纵版射击游戏:喵星战争 (四)
注:本教程主要来自《Cocos2D-x权威指南》满硕泉著 机械工业出版社,如需要更详细的内容,请支持并购买正版实体书籍
2013/11/05 更新
在上一个教程里,我们完成了敌人狗博士的基本定义与实现,这样我们就有了主角和敌人了,接下来我们一起看看在打飞机游戏里面,主角和敌人的子弹是如何实现的。
主角小猫子弹的定义以及实现
首先创建一个类文件GameHeroBullet.h/cpp, 在GameHeroBullet.h里,初始化定义小猫的子弹类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include "cocos2d.h" 2 using namespace cocos2d; 3 4 class GameHeroBullet : public CCNode { 5 public: 6 bool isVisable; 7 8 // memory control 9 GameHeroBullet(void); 10 virtual ~GameHeroBullet(void); 11 12 // redefine onEnter and onExit method 13 virtual void onEnter(); 14 virtual void onExit(); 15 16 void setIsVisable(); 17 void setIsNotVisable(); 18 bool getIsVisable(); 19 };
在这个类定义里,主角子弹只有一个公共变量,isVisable用来判断主角的子弹是否可见,是为了实现子弹击中的效果,除了四个基本构成函数之外,还有三个公有函数,分别为void setIsVisable(), void setIsNotVisable(), bool getIsVisable(), 具体的作用在实现这些函数的时候会作详细解释,接下来在GameHeroBullet.cpp中对小猫子弹进行初步实现-完成四个基本函数的重写。
// GameHeroBullet.cpp #include "GameHeroBullet.h" GameHeroBullet::GameHeroBullet() { isVisable = false; } GameHeroBullet::~GameHeroBullet() {} void GameHeroBullet::onEnter() { CCNode::onEnter(); // init bullet this -> setContentSize(CCSizeMake(21, 52)); CCSprite * bullet = CCSprite::create("YuGuZD.png"); addChild(bullet); } void GameHeroBullet::onExit() { CCNode::onExit(); }
在构造对象的时候,将isVisable初始化为false,onEnter()里简单地初始化了主角子弹的大小(宽21,高52),图像(YuGuZD.png),然后我们再简单实现以下刚才添加的三个函数
// void setIsVisable()
void GameHeroBullet::setIsVisable() { // set bullet visiable and start to move this -> setVisible(true); isVisable = true; this -> runAction(CCSequence::create(CCMoveTo::create((-this -> getPosition().y + 550)/150, ccp(this -> getPosition().x, 550)), CCCallFuncN::create(this, callfuncN_selector(GameHeroBullet::setIsNotVisable)), NULL)); }
setIsVisable()用来使子弹出现并作出相应的向前移动动作,首先是向前移动,到达屏幕一段后再执行setIsNotVisable()使其消失不见。
// void setIsNotVisable() void GameHeroBullet::setIsNotVisable() { this -> setVisible(false); isVisable = false; this -> stopAllActions(); }
setIsNotVisable()用于隐藏到达屏幕一端的子弹以及停止其一切动作 - stopAllActions()
// bool getIsVisable() bool GameHeroBullet::getIsVisable() { return isVisable; }
getIsVisable()用来返回子弹的可见判断值isVisable。
这样,主角子弹的基本定义以及实现就基本完成了,接着我们看看怎么将其加入到我们的游戏里去。
首先在GameObjHero.h中加入GameHeroBullet.h头文件声明,再添加一个让主角释放子弹的函数void releasebullet()
#include "GameHeroBullet.h" class GameObjHero: public CCNode, public CCTargetedTouchDelegate { public: …… // hero's attack method void releasebullet(); };
然后在GameHeroBullet.cpp中添加releasebullet()的实现以及使用
void GameObjHero::onEnter() { …… schedule(schedule_selector(GameObjHero::releasebullet), 1.0f); } void GameObjHero::releasebullet() { // release bullet to attack if (isControl) { CCPoint pos = this -> getPosition(); GameMain * p = (GameMain *) this -> getParent(); p -> releaseheroBullet(pos.x, pos.y + 30); } }
到这里或者程序会出现一些报错,但是没有问题,因为我们还要在游戏的主场景运行添加子弹运行部分
首先在主场景类GameScene.h中加入一个私有变量CCArray * bullets用来装载子弹,加入一个释放子弹的公共函数void releaseheroBullet(int x, int y)用来根据主角的位置调用主角的子弹释放函数
#include "GameHeroBullet.h"
class GameMain : public CCLayer { private: ...... CCArray * bullets; public: ...... // bullet method void releaseheroBullet(int x, int y); }
在GameScene.cpp的bool init()中加入子弹初始化的部分
// init hero bullet bullets = new CCArray(); bullets -> initWithCapacity(5); for (int i = 0; i < bullets -> capacity(); i ++) { GameHeroBullet * herobullet = new GameHeroBullet(); herobullet -> setIsNotVisable(); herobullet -> setScale(0.5); bullets -> addObject(herobullet); addChild(herobullet, 3); } bullets -> retain();
从这个初始化部分我们可以看到,主角的子弹是由一个具有5个变量的CCArray组成,最初生成的时候必须设为不可见,并且根据图像的大小做了一个50%缩小的处理。
主场景释放主角子弹的函数void relaseheroBullet(int x, int y)的实现
void GameMain::releaseheroBullet(int x, int y) { // walk throught bullets array, release bullet not visable for (int i = 0; i < 5; i++) { if (!((GameHeroBullet *) bullets -> objectAtIndex(i)) -> getIsVisable()) { // set position and isVisable ((GameHeroBullet *)bullets -> objectAtIndex(i)) -> setPosition(ccp(x, y)); ((GameHeroBullet *)bullets -> objectAtIndex(i)) -> setIsVisable(); break; } } }
这个函数就是遍历初始化生成的子弹,如果这颗子弹还是不可见的,则根据主角的位置(用int x, int y 在主角类实现中返回)调用子弹的可见函数setIsVisable(),运行子弹动画。
最后在GameObjHero.cpp中加入GameScene.h的头文件声明,使得调用主场景释放主角子弹函数的void releasebullet()函数可以正常编译,编译并运行游戏,控制主角小猫,我们就可以看到一颗颗骨头子弹从小猫处发射出去了。
完成了主角小猫的子弹,接下来我们看看敌人的子弹又是如何实现的。
敌人狗博士子弹的定义与实现
首先添加敌人子弹的类文件GameEnemyBullet.h/cpp, 在头文件GameEnemyBullet.h中如下定义敌人子弹类
#include "cocos2d.h" using namespace cocos2d; class GameEnemyBullet : public CCNode { public: bool isVisable; // memory control GameEnemyBullet(void); virtual ~GameEnemyBullet(void); // redefine onEnter and onExit method virtual void onEnter(); virtual void onExit(); void setIsVisable(); void setIsNotVisable(); bool getIsVisable(); };
可以看出来,敌人子弹的定义除了两个基本函数名称不一样之外,其他都基本相同,接下来我们看看如何初始化敌人子弹,在GameEnemyBullet.cpp里
#include "GameEnemyBullet.h" GameEnemyBullet::GameEnemyBullet(void) { isVisable = false; } GameEnemyBullet::~GameEnemyBullet(void) {} void GameEnemyBullet::onEnter() { CCNode::onEnter(); // init bullet this -> setContentSize(CCSizeMake(21, 52)); CCSprite * bullet = CCSprite::create("DrDogZD.png"); bullet -> runAction(CCRepeatForever::create(CCRotateBy::create(1, 360))); addChild(bullet); } void GameEnemyBullet::onExit() { CCNode::onExit(); }
和主角子弹的实现相同,在构造函数GameEnemyBullet(void)中初始化可视判断量为false,和主角子弹不一样的是,它多了一个永久旋转的动作CCRotateBy::create(1, 360),接下来是敌人子弹的三个动作函数实现
void GameEnemyBullet::setIsVisable() { // set bullet visiable and start to move this -> setVisible(true); isVisable = true; this -> runAction(CCSequence::create(CCMoveTo::create((this -> getPosition().y + 50)/150, ccp(this -> getPosition().x, -550)), CCCallFuncN::create(this, callfuncN_selector(GameEnemyBullet::setIsNotVisable)), NULL)); } void GameEnemyBullet::setIsNotVisable() { this -> setVisible(false); isVisable = false; this -> stopAllActions(); } bool GameEnemyBullet::getIsVisable() { return isVisable; }
基本思路和主角子弹是一样的,唯一不一样的就是主角子弹是向上移动而敌人子弹是向下移动的,这里需要注意一下。
敌人子弹类的定义与实现完成后,接下来轮到投入使用了,先在敌人类的头文件GameObjEnemy.h中加入子弹发射函数releasebullet()
class GameObjEnemy : public CCNode { public: ...... // enemy's attack method void releasebullet(); ...... };
在实现文件中加入发射函数的实现
#include "GameScene.h" void GameObjEnemy::moveStart() { ...... schedule(schedule_selector(GameObjEnemy::releasebullet), 1.2f); } void GameObjEnemy::releasebullet() { CCSize size = CCDirector::sharedDirector() -> getWinSize(); // release bullet to attack CCPoint pos = this -> getPosition(); if (pos.y > 20 && pos.y < size.height && isLife) { GameMain *p = (GameMain *) this -> getParent(); p -> releaseenemyBullet(pos.x, pos.y - 30); } }
和主角子弹类不一样的是,释放子弹的函数调用被放在了敌人的开始移动函数moveStart()中,这里记得加入“GameScene.h”的头文件声明,虽然会有些报错,但是在完成主场景实现部分之后就能解决了。
在主场景GameScene.h头文件中加入敌人子弹头文件声明,公共变量的敌人子弹列CCArray * enemybullets, 主场景敌人子弹释放函数void releaseenemyBullet(int x, int y)
#include "GameEnemyBullet.h" class GameMain : public CCLayer { private: ...... CCArray * enemybullets; ...... public: ...... // bullet method ...... void releaseenemyBullet(int x, int y); };
在主场景的实现文件GameScene.cpp中,先在bool init()函数中加入敌人子弹初始化部分
// init enemy bullets enemybullets = new CCArray(); enemybullets -> initWithCapacity(10); for (int i = 0; i < enemybullets -> capacity(); i++) { GameEnemyBullet * ebullet = new GameEnemyBullet(); ebullet -> setIsNotVisable(); ebullet -> setScale(0.5); enemybullets -> addObject(ebullet); addChild(ebullet, 3); } enemybullets -> retain();
在实现主场景释放敌人子弹函数releaseenemyBullet(int x, int y)
void GameMain::releaseenemyBullet(int x, int y) { for (int i = 0; i < enemybullets -> capacity(); i ++) { if (!((GameEnemyBullet *) enemybullets -> objectAtIndex(i)) -> getIsVisable()) { // set position and isVisable ((GameEnemyBullet *)enemybullets -> objectAtIndex(i)) -> setPosition(ccp(x, y)); ((GameEnemyBullet *)enemybullets -> objectAtIndex(i)) -> setIsVisable(); break; } } }
可以看到,这和主场景释放主角子弹函数相似,除了循环判定是依据敌人子弹列的容量来判断的。
编译并运行游戏,我们就可以看到敌人狗博士在向主角发出正在旋转的子弹了
好了,子弹部分也完成了,接下来我们就要实现打飞机游戏的一个很主要的功能 - 攻击判定,请继续关注。