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