现在的位置: 首页 > 综合 > 正文

cocos2d-x塔防游戏教程(三)

2013年10月16日 ⁄ 综合 ⁄ 共 5961字 ⁄ 字号 评论关闭

9.添加敌人。打开HelloWorldScene.h文件,添加以下代码:

1
2
3
4
 
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*, _enemies, Enemies);

int wave;
cocos2d::CCLabelBMFont* ui_wave_lbl;

打开HelloWorldScene.cpp文件,在析构函数里,添加如下代码:

1
 
_enemies->release();

添加Enemy类,派生自CCNode类,Enemy.h文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 
#ifndef __ENEMY_H__
#define __ENEMY_H__

#include"cocos2d.h"
#include"HelloWorldScene.h"
#include"Waypoint.h"

class Enemy : public cocos2d::CCNode
{
public:
   Enemy(void);
   ~Enemy(void);

static Enemy* nodeWithTheGame(HelloWorld* game);
bool initWithTheGame(HelloWorld* game);
void doActivate(float dt);
void getRemoved();

void update(float dt);
void draw(void);

   CC_SYNTHESIZE(HelloWorld*, _theGame, TheGame);
   CC_SYNTHESIZE(cocos2d::CCSprite*, _mySprite, MySprite);

private:
   cocos2d::CCPoint myPosition;
int maxHp;
int currentHp;
float walkingSpeed;
   Waypoint *destinationWaypoint;
bool active;
};

#endif// __ENEMY_H__

打开Enemy.cpp文件,代码如下:

using namespace cocos2d;

#define HEALTH_BAR_WIDTH 20
#define HEALTH_BAR_ORIGIN -10

Enemy::Enemy(void)
{
}

Enemy::~Enemy(void)
{
}

Enemy* Enemy::nodeWithTheGame(HelloWorld* game)
{
    Enemy *pRet = new Enemy();
    if (pRet && pRet->initWithTheGame(game))
    {
        return pRet;
    }
    else
    {
        delete pRet;
        pRet = NULL;
        return NULL;
    }
}

bool Enemy::initWithTheGame(HelloWorld* game)
{
    bool bRet = false;
    do
    {
        maxHp = 40;
        currentHp = maxHp;
        active = false;
        walkingSpeed = 0.5;
        
        _theGame = game;
        _mySprite = CCSprite::create("enemy.png");
        this->addChild(_mySprite);
        //获取初始坐标点
        Waypoint *waypoint = (Waypoint*)_theGame->getWaypoints()->objectAtIndex(_theGame->getWaypoints()->count() - 1);
        //获取下一个坐标点,即目标坐标点
        destinationWaypoint = waypoint->getNextWaypoint();
        CCPoint pos = waypoint->getMyPosition();
        myPosition = pos;
        _mySprite->setPosition(pos);
        _theGame->addChild(this);
        
        this->scheduleUpdate();
        
        bRet = true;
    } while (0);
    
    return bRet;
}

void Enemy::doActivate(float dt)
{
    active = true;
}

void Enemy::getRemoved()
{
    //将精灵从父视图中移除  getParent()代表父视图
    this->getParent()->removeChild(this, true);
    //从游戏层中存放敌人的数组中移除
    _theGame->getEnemies()->removeObject(this);
    
    //消灭敌人
    //Notify the game that we killed an enemy so we can check if we can send another wave
    _theGame->enemyGotKilled();
}

void Enemy::update(float dt)
{
    if (!active)
    {
        return;
    }
    //碰撞检测,第一个参数为当前坐标点,第二个参数为碰撞检测的半径 ,第三个为目标坐标点,第四个为半径
    if (_theGame->collisionWithCircle(myPosition, 1, destinationWaypoint->getMyPosition(), 1))
    {
        //如果能够得到下一个目标点
        if (destinationWaypoint->getNextWaypoint())
        {
            //获得新的坐标点
            destinationWaypoint = destinationWaypoint->getNextWaypoint();
        }
        else
        {
            //如果没有下一个坐标点,说明精灵走到终点了,
            //Reached the end of the road. Damage the player
            //主人公收到伤害
            _theGame->getHpDamage();
            //让精灵消失
            this->getRemoved();
        }
    }
    //获得目标点
    CCPoint targetPoint =  destinationWaypoint->getMyPosition();
    //移动速度
    float movementSpeed = walkingSpeed;
    //目标点和当前人物的距离
    CCPoint normalized = ccpNormalize(ccp(targetPoint.x - myPosition.x, targetPoint.y - myPosition.y));
    //精灵转的角度
    _mySprite->setRotation(CC_RADIANS_TO_DEGREES(atan2(normalized.y, - normalized.x)));
    //每帧都刷新精灵的位置
    myPosition = ccp(myPosition.x + normalized.x * movementSpeed, myPosition.y + normalized.y * movementSpeed);
    //设置精灵的位置
    _mySprite->setPosition(myPosition);
}

void Enemy::draw(void)
{
    //绘制红色血条  下面的数组代表4个点分别是左上、右上、右下、左下
    CCPoint healthBarBack[] = {
        ccp(_mySprite->getPosition().x - 10, _mySprite->getPosition().y + 16),
        ccp(_mySprite->getPosition().x + 10, _mySprite->getPosition().y + 16),
        ccp(_mySprite->getPosition().x + 10, _mySprite->getPosition().y + 14),
        ccp(_mySprite->getPosition().x - 10, _mySprite->getPosition().y + 14)
    };
    //ccc4f(255, 0, 0, 255)  代表红色
    ccDrawSolidPoly(healthBarBack, 4, ccc4f(255, 0, 0, 255));
    
    //绘制绿色血条  下面的数组代表4个点分别是左上、右上、右下、左下
    CCPoint healthBar[] = {
        ccp(_mySprite->getPosition().x + HEALTH_BAR_ORIGIN, _mySprite->getPosition().y + 16),
        ccp(_mySprite->getPosition().x + HEALTH_BAR_ORIGIN + (float)(currentHp * HEALTH_BAR_WIDTH) / maxHp, _mySprite->getPosition().y + 16),
        ccp(_mySprite->getPosition().x + HEALTH_BAR_ORIGIN + (float)(currentHp * HEALTH_BAR_WIDTH) / maxHp, _mySprite->getPosition().y + 14),
        ccp(_mySprite->getPosition().x + HEALTH_BAR_ORIGIN
            
            , _mySprite->getPosition().y + 14)
        
    };
    //ccc4f(0, 255, 0, 255)代表绿色
    ccDrawSolidPoly(healthBar, 4, ccc4f(0, 255, 0, 255));
    
    CCNode::draw();
}

首先,通过传递一个HelloWorld对象引用进行初始化。在初始化函数里面,对一些重要的变量进行设置:

  • maxHP: 敌人的生命值。

  • walkingSpeed: 敌人的移动速度。

  • mySprite: 存储敌人的可视化表现。

  • destinationWaypoint: 存储下一个路点的引用。

update方法每帧都会被调用,它首先通过collisionWithCircle方法检查是否到达了目的路点。如果到达了,则前进到下一个路点,直到敌人到达终点,玩家也就受到伤害。接着,它根据敌人的行走速度,沿着一条直线移动精灵到达下一个路点。它通过以下算法
①计算出从当前位置到目标位置的向量,然后将其长度设置为1(向量标准化)
②将移动速度乘以标准化向量,得到移动的距离,将它与当前坐标进行相加,得到新的坐标位置。
最后,draw方法在精灵上面简单的实现了一条血量条。它首先绘制一个红色背景,然后根据敌人的当前生命值用绿色进行覆盖血量条。
10.显示敌人。打开HelloWorldScene.cpp文件,添加头文件声明:

1
 
#include"Enemy.h"

添加如下方法:

bool HelloWorld::loadWave()
{
    //从Plist文件中获取数组
    CCArray *waveData = CCArray::createWithContentsOfFile("Waves.plist");
    if (wave >= waveData->count())
    {
        return false;
    }
    //获取第几波的数组
    CCArray *currentWaveData = (CCArray*)waveData->objectAtIndex(wave);
    CCObject *pObject = NULL;
    CCARRAY_FOREACH(currentWaveData, pObject)
    {
        //
        CCDictionary* enemyData = (CCDictionary*)pObject;
        Enemy *enemy = Enemy::nodeWithTheGame(this);
        _enemies->addObject(enemy);
        //过多长时间执行怪物开始移动的方法,spawnTime是plist文件中事先写好的。
        enemy->schedule(schedule_selector(Enemy::doActivate), ((CCString*)enemyData->objectForKey("spawnTime"))->floatValue());
    }
    
    wave++;
    ui_wave_lbl->setString(CCString::createWithFormat("WAVE: %d", wave)->getCString());
    return true;
}

void HelloWorld::enemyGotKilled()
{
    //If there are no more enemies.
    if (_enemies->count() <= 0)
    {
        if (!this->loadWave())
        {
            CCLog("You win!");
            //从新加载场景
            CCDirector::sharedDirector()->replaceScene(CCTransitionSplitCols::create(1, HelloWorld::scene()));
        }
    }   
}

void HelloWorld::getHpDamage()
{
    
}

init函数里面,添加如下代码:

1
2
3
4
5
6
7
8
9
 

//初始从第0泼开始

        wave = 0;

        //设置标签

ui_wave_lbl = CCLabelBMFont::create(CCString::createWithFormat("WAVE: %d", wave)->getCString(),"font_red_14.fnt");
this->addChild(ui_wave_lbl, 10);
ui_wave_lbl->setPosition(ccp(400, wins.height - 12));
ui_wave_lbl->setAnchorPoint(ccp(00.5));

_enemies = CCArray::create();
_enemies->retain();
this->loadWave();

现在对上面的代码进行一些解释。最重要的部分是loadWave方法,它从Waves.plist文件读取数据。查看这个文件,可以看到它包含了3个数组,每个数组代表着一波敌人。第一个数组包含6个字典,每个字典定义了一个敌人。在本篇文章中,这个字典仅存储敌人的出现时间,但是也可用于定义敌人类型或者其他特殊属性,以区分不同的敌人。loadWave方法检查下一波应出现的敌人,根据波信息创建相应的敌人,并安排它们在规定的时间出现在屏幕上。enemyGotKilled方法检查当前屏幕上的敌人数量,如果已经没有敌人的话,那么就让下一波敌人出现。之后,还使用这个方法来判断玩家是否赢得了游戏。编译运行,敌人正向玩家基地前进,如下图所示:

代码下载:http://vdisk.weibo.com/s/BDn59yfnBVxkb
 

【上篇】
【下篇】

抱歉!评论已关闭.