cocos2dx - 制作纵版射击游戏:喵星战争 (四)

注:本教程主要来自《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 };
GameHeroBullet.h

在这个类定义里,主角子弹只有一个公共变量,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;
        }
    }
}

可以看到,这和主场景释放主角子弹函数相似,除了循环判定是依据敌人子弹列的容量来判断的。

编译并运行游戏,我们就可以看到敌人狗博士在向主角发出正在旋转的子弹了

  

好了,子弹部分也完成了,接下来我们就要实现打飞机游戏的一个很主要的功能 - 攻击判定,请继续关注。

posted @ 2013-11-04 22:03  fuutou  阅读(592)  评论(0编辑  收藏  举报