注:本教程主要来自《Cocos2D-x权威指南》满硕泉著 机械工业出版社,如需要更详细的内容,请支持并购买正版实体书籍
2013/11/05 更新
在上一个教程里,我们完成了敌人狗博士的基本定义与实现,这样我们就有了主角和敌人了,接下来我们一起看看在打飞机游戏里面,主角和敌人的子弹是如何实现的。
主角小猫子弹的定义以及实现
首先创建一个类文件GameHeroBullet.h/cpp, 在GameHeroBullet.h里,初始化定义小猫的子弹类
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;
}
}
}
可以看到,这和主场景释放主角子弹函数相似,除了循环判定是依据敌人子弹列的容量来判断的。
编译并运行游戏,我们就可以看到敌人狗博士在向主角发出正在旋转的子弹了
好了,子弹部分也完成了,接下来我们就要实现打飞机游戏的一个很主要的功能 - 攻击判定,请继续关注。