求BK-Demon's Tower towerdefense hd下载

如何制作一个塔防游戏 Cocos2d-x 2.0.4 - 无幻
- 博客频道 - CSDN.NET
28025人阅读
&&&&& 本文实践自 Pablo Ruiz 的文章《》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.0.4进行学习和移植。在这篇文章,将会学习到如何制作一个塔防游戏。在这当中,学习如何在设定的时间内出现一波波的敌人,使这些敌人沿着指定的路点前进,如何在地图上指定的位置创建炮塔,如何使炮塔射击敌人,如何可视化调试路点和炮塔的攻击范围。
步骤如下:
1.新建Cocos2d-win32工程,工程名为&TowerDefense&,去除&Box2D&选项,勾选&Simple Audio Engine in Cocos Denshion&选项;
2.下载本游戏所需的资源,将资源放置&Resources&目录下;
3.为场景添加背景图片。打开HelloWorldScene.cpp文件,修改init函数,如下:
bool&HelloWorld::init()
&&&&bool&bRet&=&false;
&&&&&&&&CC_BREAK_IF(!&CCLayer::init());
&&&&&&&&this-&setTouchEnabled(true);
&&&&&&&&CCSize&wins&=&CCDirector::sharedDirector()-&getWinSize();
&&&&&&&&CCSprite&*background&=&CCSprite::create(&Bg.png&);
&&&&&&&&this-&addChild(background);
&&&&&&&&background-&setPosition(ccp(wins.width&/&2,&wins.height&/&2));
&&&&&&&&bRet&=&true;
&&&&}&while&(0);
&&&&return&bR
通过放置的背景图片,可以直观的看出哪些地方允许玩家放置炮塔。编译运行,如下图所示:
4.接着,需要沿路设置一些点,在这些点上能够让玩家触摸和建立炮塔。为了方便管理,使用.plist文件来存储炮塔的放置点,这样就可以很容易的改变它们。TowersPosition.plist已经在资源文件夹中,其中已经有了一些炮塔的位置。查看这个文件,可以看到一个字典数组,字典只包含两个键&x&和&y&。每个字典条目代表一个炮塔位置的x和y坐标。现在需要读取这个文件,并且放置塔基到地图上。打开HelloWorldScene.h文件,添加以下变量:
cocos2d::CCArray*&towerB
打开HelloWorldScene.cpp文件,添加如下方法:
void&HelloWorld::loadTowerPositions()
&&&&CCArray*&towerPositions&=&CCArray::createWithContentsOfFile(&TowersPosition.plist&);
&&&&towerBases&=&CCArray::createWithCapacity(10);
&&&&towerBases-&retain();
&&&&CCObject&*pObject&=&NULL;
&&&&CCARRAY_FOREACH(towerPositions,&pObject)
&&&&&&&&CCDictionary*&towerPos&=&(CCDictionary*)pO
&&&&&&&&CCSprite*&towerBase&=&CCSprite::create(&open_spot.png&);
&&&&&&&&this-&addChild(towerBase);
&&&&&&&&towerBase-&setPosition(ccp(((CCString*)towerPos-&objectForKey(&x&))-&intValue(),
&&&&&&&&&&&&((CCString*)towerPos-&objectForKey(&y&))-&intValue()));
&&&&&&&&towerBases-&addObject(towerBase);
在init函数里面,添加背景图片代码之后,添加如下代码:
this-&loadTowerPositions();
在析构函数里面,添加如下代码:
towerBases-&release();
编译运行,就可以看到道路两侧的方块,这些是做为玩家炮塔的基座。如下图所示:
5.开始建立炮塔。打开HelloWorldScene.h文件,添加如下代码:
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*,&_towers,&Towers);
添加Tower类,派生自CCNode类,Tower.h文件代码如下:
#ifndef&__TOWER_H__
#define&__TOWER_H__
#include&&cocos2d.h&
#include&&HelloWorldScene.h&
#define&kTOWER_COST&<span style="color:#ff
class&Tower&:&public&cocos2d::CCNode
&&&&Tower(void);
&&&&~Tower(void);
&&&&static&Tower*&nodeWithTheGame(HelloWorld*&game,&cocos2d::CCPoint&location);
&&&&bool&initWithTheGame(HelloWorld*&game,&cocos2d::CCPoint&location);
&&&&void&update(float&dt);
&&&&void&draw(void);
&&&&CC_SYNTHESIZE(HelloWorld*,&_theGame,&TheGame);
&&&&CC_SYNTHESIZE(cocos2d::CCSprite*,&_mySprite,&MySprite);
&&&&int&attackR
&&&&float&fireR
#endif&&//&__TOWER_H__
打开Tower.cpp文件,代码如下:
#include&&Tower.h&
using&namespace&cocos2d;
Tower::Tower(void)
Tower::~Tower(void)
Tower*&Tower::nodeWithTheGame(HelloWorld*&game,&CCPoint&location)
&&&&Tower&*pRet&=&new&Tower();
&&&&if&(pRet&&&&pRet-&initWithTheGame(game,&location))
&&&&&&&&return&pR
&&&&&&&&delete&pR
&&&&&&&&pRet&=&NULL;
&&&&&&&&return&NULL;
bool&Tower::initWithTheGame(HelloWorld*&game,&CCPoint&location)
&&&&bool&bRet&=&false;
&&&&&&&&attackRange&=&70;
&&&&&&&&damage&=&10;
&&&&&&&&fireRate&=&1;
&&&&&&&&_mySprite&=&CCSprite::create(&tower.png&);
&&&&&&&&this-&addChild(_mySprite);
&&&&&&&&_mySprite-&setPosition(location);
&&&&&&&&_theGame&=&
&&&&&&&&_theGame-&addChild(this);
&&&&&&&&this-&scheduleUpdate();
&&&&&&&&bRet&=&true;
&&&&}&while&(0);
&&&&return&bR
void&Tower::update(float&dt)
void&Tower::draw(void)
#ifdef&COCOS2D_DEBUG
&&&&ccDrawColor4F(<span style="color:#ff,&<span style="color:#ff,&<span style="color:#ff,&<span style="color:#ff);
&&&&ccDrawCircle(_mySprite-&getPosition(),&attackRange,&<span style="color:#ff,&30,&false);
&&&&CCNode::draw();
这个Tower类包含几个属性:一个精灵对象,这是炮塔的可视化表现;一个父层的引用,方便访问父层;还有三个变量:
attackRange: 炮塔可以攻击敌人的距离。damage: 炮塔对敌人造成的伤害&#20540;。fireRate: 炮塔再次攻击敌人的时间间隔。
有了这三个变量,就可以创建各种不同攻击属性的炮塔,比如需要很长时间来重新加载的远程重击,或者范围有限的快速攻击。最后,代码中的draw方法,用于在炮塔周围绘制一个圆,以显示出它的攻击范围,这将方便调试。
6.让玩家添加炮塔。打开HelloWorldScene.cpp文件,加入以下头文件声明:
#include&&Tower.h&
在析构函数中添加如下代码:
_towers-&release();
在init函数,添加如下代码:
_towers&=&CCArray::create();
_towers-&retain();
添加如下两个方法,代码如下:
bool&HelloWorld::canBuyTower()
&&&&return&true;
void&HelloWorld::ccTouchesBegan(CCSet&*pTouches,&CCEvent&*pEvent)
&&&&CCSetIterator&iter&=&pTouches-&begin();
&&&&for&(;&iter&!=&pTouches-&end();&iter&#43;&#43;)
&&&&&&&&CCTouch*&pTouch&=&(CCTouch*)(*iter);
&&&&&&&&CCPoint&location&=&pTouch-&getLocation();
&&&&&&&&CCObject&*pObject&=&NULL;
&&&&&&&&CCARRAY_FOREACH(towerBases,&pObject)
&&&&&&&&&&&&CCSprite&*tb&=&(CCSprite*)pO
&&&&&&&&&&&&if&(this-&canBuyTower()&&&&tb-&boundingBox().containsPoint(location)&&&&!tb-&getUserData())
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&//We&will&spend&our&gold&later.
&&&&&&&&&&&&&&&&Tower*&tower&=&Tower::nodeWithTheGame(this,&tb-&getPosition());
&&&&&&&&&&&&&&&&_towers-&addObject(tower);
&&&&&&&&&&&&&&&&tb-&setUserData(tower);
&&&&&&&&&&&&}&&&&&&&&&&&
方法ccTouchesBegan检测当用户触摸屏幕上任何点时,遍历towerBases数组,检查触摸点是否包含在任何一个塔基上。不过在创建炮塔前,还有两件事需要检查:
①玩家是否买得起炮塔?canBuyTower方法用来检查玩家是否有足够的金币来购买炮塔。在这里先假设玩家有很多金币,方法返回true。
②玩家是否违法了建筑规则?如果tb的UserData已经设置了,那么这个塔基已经有了炮塔,不能再添加一个新的了。
如果一切检查都通过,那么就创建一个新的炮塔,放置在塔基上,并将它添加到炮塔数组中。编译运行,触摸塔基,就可以看到炮塔放置上去了,并且它的周围还有白色的圆圈显示攻击范围,如下图所示:
7.添加路点。敌人将会沿着一系列的路点前进,这些简单相互连接的点构成了一条路径,敌人在这条路径上进行行走。敌人会出现在第一个路点,搜寻列表中的下一个路点,移动到那个位置,重复这个过程,直到他们到达列表中的最后一个路点——玩家基地。如果被敌人到达基地,那么玩家就会受到损害。添加Waypoint类,派生自CCNode类,Waypoint.h文件代码如下:
#ifndef&__WAYPOINT_H__
#define&__WAYPOINT_H__
#include&&cocos2d.h&
#include&&HelloWorldScene.h&
class&Waypoint&:&public&cocos2d::CCNode
&&&&Waypoint(void);
&&&&~Waypoint(void);
&&&&static&Waypoint*&nodeWithTheGame(HelloWorld*&game,&cocos2d::CCPoint&location);
&&&&bool&initWithTheGame(HelloWorld*&game,&cocos2d::CCPoint&location);
&&&&void&draw(void);
&&&&CC_SYNTHESIZE(cocos2d::CCPoint,&_myPosition,&MyPosition);
&&&&CC_SYNTHESIZE(Waypoint*,&_nextWaypoint,&NextWaypoint);
&&&&HelloWorld*&theG
#endif&&//&__WAYPOINT_H__
打开Waypoint.cpp文件,代码如下:
#include&&Waypoint.h&
using&namespace&cocos2d;
Waypoint::Waypoint(void)
&&&&_nextWaypoint&=&NULL;
Waypoint::~Waypoint(void)
Waypoint*&Waypoint::nodeWithTheGame(HelloWorld*&game,&CCPoint&location)
&&&&Waypoint&*pRet&=&new&Waypoint();
&&&&if&(pRet&&&&pRet-&initWithTheGame(game,&location))
&&&&&&&&return&pR
&&&&&&&&delete&pR
&&&&&&&&pRet&=&NULL;
&&&&&&&&return&NULL;
bool&Waypoint::initWithTheGame(HelloWorld*&game,&CCPoint&location)
&&&&bool&bRet&=&false;
&&&&&&&&theGame&=&
&&&&&&&&_myPosition&=&
&&&&&&&&this-&setPosition(CCPointZero);
&&&&&&&&theGame-&addChild(this);
&&&&&&&&bRet&=&true;
&&&&}&while&(0);
&&&&return&bR
void&Waypoint::draw(void)
#ifdef&COCOS2D_DEBUG
&&&&ccDrawColor4F(0,&<span style="color:#ff,&0,&<span style="color:#ff);
&&&&ccDrawCircle(_myPosition,&6,&<span style="color:#ff,&30,&false);
&&&&ccDrawCircle(_myPosition,&2,&<span style="color:#ff,&30,&false);
&&&&if&(_nextWaypoint)
&&&&&&&&ccDrawLine(_myPosition,&_nextWaypoint-&_myPosition);
&&&&CCNode::draw();
首先,通过传入的HelloWorld对象引用和路点位置坐标,进行初始化一个waypoint对象。每个路点都包含下一个路点的引用,这将会创建一个路点链接列表。每个路点知道列表中的下一个路点。通过这种方式,可以引导敌人沿着链表上的路点到达他们的最终目的地。最后,draw方法绘制显示路点的位置,并且绘制一条直线将其与下一个路点进行连接,这仅仅用于调试目的。
8.创建路点列表。打开HelloWorldScene.h文件,添加以下代码:
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*,&_waypoints,&Waypoints);
打开HelloWorldScene.cpp文件,加入以下头文件声明:
#include&&Waypoint.h&
在析构函数中添加如下代码:
_waypoints-&release();
添加以下方法:
void&HelloWorld::addWaypoints()
&&&&_waypoints&=&CCArray::create();
&&&&_waypoints-&retain();
&&&&Waypoint&*waypoint1&=&Waypoint::nodeWithTheGame(this,&ccp(<span style="color:#ff,&35));
&&&&_waypoints-&addObject(waypoint1);
&&&&Waypoint&*waypoint2&=&Waypoint::nodeWithTheGame(this,&ccp(35,&35));
&&&&_waypoints-&addObject(waypoint2);
&&&&waypoint2-&setNextWaypoint(waypoint1);
&&&&Waypoint&*waypoint3&=&Waypoint::nodeWithTheGame(this,&ccp(35,&<span style="color:#ff));
&&&&_waypoints-&addObject(waypoint3);
&&&&waypoint3-&setNextWaypoint(waypoint2);
&&&&Waypoint&*waypoint4&=&Waypoint::nodeWithTheGame(this,&ccp(<span style="color:#ff,&<span style="color:#ff));
&&&&_waypoints-&addObject(waypoint4);
&&&&waypoint4-&setNextWaypoint(waypoint3);
&&&&Waypoint&*waypoint5&=&Waypoint::nodeWithTheGame(this,&ccp(<span style="color:#ff,&<span style="color:#ff));
&&&&_waypoints-&addObject(waypoint5);
&&&&waypoint5-&setNextWaypoint(waypoint4);
&&&&Waypoint&*waypoint6&=&Waypoint::nodeWithTheGame(this,&ccp(-40,&<span style="color:#ff));
&&&&_waypoints-&addObject(waypoint6);
&&&&waypoint6-&setNextWaypoint(waypoint5);
在init函数,添加如下代码:
this-&addWaypoints();
编译运行,效果如下图所示:
在地图上有6个路点,这是敌人的行走路线。在让敌人出现在游戏中前,还需要添加一个辅助方法。打开HelloWorldScene.cpp文件,添加方法如下:
bool&HelloWorld::collisionWithCircle(CCPoint&circlePoint,&float&radius,&CCPoint&circlePointTwo,&float&radiusTwo)
&&&&float&xdif&=&circlePoint.x&-&circlePointTwo.x;
&&&&float&ydif&=&circlePoint.y&-&circlePointTwo.y;
&&&&float&distance&=&sqrt(xdif&*&xdif&&#43;&ydif&*&ydif);
&&&&if(distance&&=&radius&&#43;&radiusTwo)&
&&&&&&&&return&true;
&&&&return&false;
方法collisionWithCircle用于判断两个圆是否碰撞或者相交。这将用于判断敌人是否到达一个路点,同时也可以检测敌人是否在炮塔的攻击范围之内。
9.添加敌人。打开HelloWorldScene.h文件,添加以下代码:
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*,&_enemies,&Enemies);
cocos2d::CCLabelBMFont*&ui_wave_
打开HelloWorldScene.cpp文件,在析构函数里,添加如下代码:
_enemies-&release();
添加Enemy类,派生自CCNode类,Enemy.h文件代码如下:
#ifndef&__ENEMY_H__
#define&__ENEMY_H__
#include&&cocos2d.h&
#include&&HelloWorldScene.h&
#include&&Waypoint.h&
class&Enemy&:&public&cocos2d::CCNode
&&&&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);
&&&&cocos2d::CCPoint&myP
&&&&int&maxHp;
&&&&int&currentHp;
&&&&float&walkingS
&&&&Waypoint&*destinationW
#endif&&//&__ENEMY_H__
打开Enemy.cpp文件,代码如下:
#include&&Enemy.h&
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&pR
&&&&&&&&delete&pR
&&&&&&&&pRet&=&NULL;
&&&&&&&&return&NULL;
bool&Enemy::initWithTheGame(HelloWorld*&game)
&&&&bool&bRet&=&false;
&&&&&&&&maxHp&=&40;
&&&&&&&&currentHp&=&maxHp;
&&&&&&&&active&=&false;
&&&&&&&&walkingSpeed&=&0.5;
&&&&&&&&_theGame&=&
&&&&&&&&_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&=&
&&&&&&&&_mySprite-&setPosition(pos);
&&&&&&&&_theGame-&addChild(this);
&&&&&&&&this-&scheduleUpdate();
&&&&&&&&bRet&=&true;
&&&&}&while&(0);
&&&&return&bR
void&Enemy::doActivate(float&dt)
&&&&active&=&true;
void&Enemy::getRemoved()
&&&&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&=&walkingS
&&&&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&&#43;&normalized.x&*&movementSpeed,&myPosition.y&&#43;&normalized.y&*&movementSpeed);
&&&&_mySprite-&setPosition(myPosition);
void&Enemy::draw(void)
&&&&CCPoint&healthBarBack[]&=&{
&&&&&&&&ccp(_mySprite-&getPosition().x&-&10,&_mySprite-&getPosition().y&&#43;&16),
&&&&&&&&ccp(_mySprite-&getPosition().x&&#43;&10,&_mySprite-&getPosition().y&&#43;&16),
&&&&&&&&ccp(_mySprite-&getPosition().x&&#43;&10,&_mySprite-&getPosition().y&&#43;&14),
&&&&&&&&ccp(_mySprite-&getPosition().x&-&10,&_mySprite-&getPosition().y&&#43;&14)
&&&&ccDrawSolidPoly(healthBarBack,&4,&ccc4f(<span style="color:#ff,&0,&0,&<span style="color:#ff));
&&&&CCPoint&healthBar[]&=&{
&&&&&&&&ccp(_mySprite-&getPosition().x&&#43;&HEALTH_BAR_ORIGIN,&_mySprite-&getPosition().y&&#43;&16),
&&&&&&&&ccp(_mySprite-&getPosition().x&&#43;&HEALTH_BAR_ORIGIN&&#43;&(float)(currentHp&*&HEALTH_BAR_WIDTH)&/&maxHp,&_mySprite-&getPosition().y&&#43;&16),
&&&&&&&&ccp(_mySprite-&getPosition().x&&#43;&HEALTH_BAR_ORIGIN&&#43;&(float)(currentHp&*&HEALTH_BAR_WIDTH)&/&maxHp,&_mySprite-&getPosition().y&&#43;&14),
&&&&&&&&ccp(_mySprite-&getPosition().x&&#43;&HEALTH_BAR_ORIGIN,&_mySprite-&getPosition().y&&#43;&14)
&&&&ccDrawSolidPoly(healthBar,&4,&ccc4f(0,&<span style="color:#ff,&0,&<span style="color:#ff));
&&&&CCNode::draw();
首先,通过传递一个HelloWorld对象引用进行初始化。在初始化函数里面,对一些重要的变量进行设置:
maxHP: 敌人的生命&#20540;。walkingSpeed: 敌人的移动速度。mySprite: 存储敌人的可视化表现。destinationWaypoint: 存储下一个路点的引用。
update方法每帧都会被调用,它首先通过collisionWithCircle方法检查是否到达了目的路点。如果到达了,则前进到下一个路点,直到敌人到达终点,玩家也就受到伤害。接着,它根据敌人的行走速度,沿着一条直线移动精灵到达下一个路点。它通过以下算法:
①计算出从当前位置到目标位置的向量,然后将其长度设置为1(向量标准化)
②将移动速度乘以标准化向量,得到移动的距离,将它与当前坐标进行相加,得到新的坐标位置。
最后,draw方法在精灵上面简单的实现了一条血量条。它首先绘制一个红色背景,然后根据敌人的当前生命&#20540;用绿色进行覆盖血量条。
10.显示敌人。打开HelloWorldScene.cpp文件,添加头文件声明:
#include&&Enemy.h&
添加如下方法:
bool&HelloWorld::loadWave()
&&&&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*)pO
&&&&&&&&Enemy&*enemy&=&Enemy::nodeWithTheGame(this);
&&&&&&&&_enemies-&addObject(enemy);
&&&&&&&&enemy-&schedule(schedule_selector(Enemy::doActivate),&((CCString*)enemyData-&objectForKey(&spawnTime&))-&floatValue());
&&&&wave&#43;&#43;;
&&&&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函数里面,添加如下代码:
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(<span style="color:#ff,&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方法检查当前屏幕上的敌人数量,如果已经没有敌人的话,那么就让下一波敌人出现。之后,还使用这个方法来判断玩家是否赢得了游戏。编译运行,敌人正向玩家基地前进,如下图所示:
10.炮塔攻击。每座塔进行检查是否有敌人出现在攻击范围之内,如果有的话,对敌人进行开火,直到以下两种情况之一发生:敌人移动出范围;敌人被消灭。那么炮塔就会寻找下一个敌人。打开Tower.h文件,添加以下代码:
添加以下变量:
Enemy&*chosenE
打开Tower.cpp文件,添加头文件声明:
#include&&Enemy.h&
在initWithTheGame函数开头if条件之后,添加如下代码:
chosenEnemy&=&NULL;
添加以下方法:
void&Tower::attackEnemy()
&&&&this-&schedule(schedule_selector(Tower::shootWeapon),&fireRate);
void&Tower::chosenEnemyForAttack(Enemy&*enemy)
&&&&chosenEnemy&=&NULL;
&&&&chosenEnemy&=&
&&&&this-&attackEnemy();
&&&&enemy-&getAttacked(this);
void&Tower::shootWeapon(float&dt)
&&&&CCSprite&*bullet&=&CCSprite::create(&bullet.png&);
&&&&_theGame-&addChild(bullet);
&&&&bullet-&setPosition(_mySprite-&getPosition());
&&&&bullet-&runAction(CCSequence::create(
&&&&&&&&CCMoveTo::create(0.1,&chosenEnemy-&getMySprite()-&getPosition()),
&&&&&&&&CCCallFunc::create(this,&callfunc_selector(Tower::damageEnemy)),
&&&&&&&&CCCallFuncN::create(this,&callfuncN_selector(Tower::removeBullet)),
&&&&&&&&NULL));
void&Tower::removeBullet(CCSprite&*bullet)
&&&&bullet-&getParent()-&removeChild(bullet,&true);
void&Tower::damageEnemy()
&&&&if&(chosenEnemy)
&&&&&&&&chosenEnemy-&getDamaged(damage);
void&Tower::targetKilled()
&&&&if&(chosenEnemy)
&&&&&&&&chosenEnemy&=&NULL;
&&&&this-&unschedule(schedule_selector(Tower::shootWeapon));
void&Tower::lostSightOfEnemy()
&&&&chosenEnemy-&gotLostSight(this);
&&&&if&(chosenEnemy)
&&&&&&&&chosenEnemy&=&NULL;
&&&&this-&unschedule(schedule_selector(Tower::shootWeapon));
最后,更新update方法为如下:
void&Tower::update(float&dt)
&&&&if&(chosenEnemy)
&&&&&&&&//We&make&it&turn&to&target&the&enemy&chosen
&&&&&&&&CCPoint&normalized&=&ccpNormalize(ccp(chosenEnemy-&getMySprite()-&getPosition().x&-&_mySprite-&getPosition().x,
&&&&&&&&&&&&chosenEnemy-&getMySprite()-&getPosition().y&-&_mySprite-&getPosition().y));
&&&&&&&&_mySprite-&setRotation(CC_RADIANS_TO_DEGREES(atan2(normalized.y,&-&normalized.x))&&#43;&90);
&&&&&&&&if&(!_theGame-&collisionWithCircle(_mySprite-&getPosition(),&attackRange,&chosenEnemy-&getMySprite()-&getPosition(),&1))
&&&&&&&&&&&&this-&lostSightOfEnemy();
&&&&&&&&CCObject&*pObject&=&NULL;
&&&&&&&&CCARRAY_FOREACH(_theGame-&getEnemies(),&pObject)
&&&&&&&&&&&&Enemy&*enemy&=&(Enemy*)pO
&&&&&&&&&&&&if&(_theGame-&collisionWithCircle(_mySprite-&getPosition(),&attackRange,&enemy-&getMySprite()-&getPosition(),&1))
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&this-&chosenEnemyForAttack(enemy);
&&&&&&&&&&&&&&&&break;
&&&&&&&&&&&&}&&&&&&&&&&&
打开Enemy.h文件,添加以下代码:
cocos2d::CCArray&*attackedBy;
打开Enemy.cpp文件,在initWithTheGame函数开头if条件之后,添加如下代码:
attackedBy&=&CCArray::createWithCapacity(5);
attackedBy-&retain();
在getRemoved函数开头,添加如下代码:
CCObject&*pObject&=&NULL;
CCARRAY_FOREACH(attackedBy,&pObject)
&&&&Tower&*attacker&=&(Tower*)pO
&&&&attacker-&targetKilled();
添加如下方法:
void&Enemy::getAttacked(Tower*&attacker)
&&&&attackedBy-&addObject(attacker);
void&Enemy::gotLostSight(Tower*&attacker)
&&&&attackedBy-&removeObject(attacker);
void&Enemy::getDamaged(int&damage)
&&&&currentHp&-=&
&&&&if&(currentHp&&=&0)
&&&&&&&&this-&getRemoved();
代码中最重要的部分是在Tower类的update方法。炮塔不断检查敌人是否在攻击范围内,如果是的话,炮塔将旋转朝向敌人,开火攻击。一个敌人一旦被标记为被攻击,将会调用方法让炮塔以攻击间隔发射子弹。反过来,每个敌人都存储有向其攻击的炮塔列表,所以如果敌人被杀死了,那么炮塔就会被通知停止攻击。编译运行,放置几个炮塔在地图上,将会看到一旦敌人进入炮塔的攻击范围,炮塔就会向它们开火攻击,敌人的血量条就会减少,直到被消灭。如下图所示:
11.显示玩家血量。打开HelloWorldScene.h文件,添加以下代码:
int&playerHp;
cocos2d::CCLabelBMFont&*ui_hp_
bool&gameE
变量playerHp表示玩家的生命&#20540;,CCLabelBMFont对象是一个标签,用来显示生命数&#20540;。gameEnded用来表示游戏是否结束。打开HelloWorldScene.cpp文件,在init函数里面,添加如下代码:
gameEnded&=&false;
playerHp&=&5;
ui_hp_lbl&=&CCLabelBMFont::create(CCString::createWithFormat(&HP:&%d&,&playerHp)-&getCString(),&&font_red_14.fnt&);
this-&addChild(ui_hp_lbl,&10);
ui_hp_lbl-&setPosition(ccp(35,&wins.height&-&12));
添加如下方法:
void&HelloWorld::getHpDamage()
&&&&playerHp--;
&&&&ui_hp_lbl-&setString(CCString::createWithFormat(&HP:&%d&,&playerHp)-&getCString());
&&&&if&(playerHp&&=&0)
&&&&&&&&this-&doGameOver();
void&HelloWorld::doGameOver()
&&&&if&(!gameEnded)
&&&&&&&&gameEnded&=&true;
&&&&&&&&CCDirector::sharedDirector()-&replaceScene(CCTransitionRotoZoom::create(1,&HelloWorld::scene()));
添加的方法为减少玩家生命&#20540;,更新标签,并检查玩家生命是否耗尽,如果是的话,游戏就结束了。当敌人到达基地的时候,getHpDamage方法被调用。编译运行,让敌人到达基地,你将会看到玩家的生命在减少,直到游戏失败。如下图所示:
12.限制金币供应量。大多数游戏都实现了“零和”功能,建造每座炮塔需要一定的资源,并给玩家有限的资源进行分配。打开HelloWorldScene.h文件,添加如下代码:
int&playerG
cocos2d::CCLabelBMFont&*ui_gold_
就像显示生命数&#20540;一样,一个变量表示玩家的金币数,一个标签对象显示金币数&#20540;。打开HelloWorldScene.cpp文件,在init函数里面,添加如下代码:
playerGold&=&<span style="color:#ff;
ui_gold_lbl&=&CCLabelBMFont::create(CCString::createWithFormat(&GOLD:&%d&,&playerGold)-&getCString(),&&font_red_14.fnt&);
this-&addChild(ui_gold_lbl,&10);
ui_gold_lbl-&setPosition(ccp(<span style="color:#ff,&wins.height&-&12));
ui_gold_lbl-&setAnchorPoint(ccp(0,&0.5));
添加如下方法:
void&HelloWorld::awardGold(int&gold)
&&&&playerGold&&#43;=&
&&&&ui_gold_lbl-&setString(CCString::createWithFormat(&GOLD:&%d&,&playerGold)-&getCString());
替换canBuyTower方法,代码如下:
bool&HelloWorld::canBuyTower()
&&&&if&(playerGold&-&kTOWER_COST&&=&0)
&&&&&&&&return&true;
&&&&return&false;
在ccTouchesBegan函数里面,语句//We will spend our gold later.的后面,添加如下代码:
playerGold&-=&kTOWER_COST;
ui_gold_lbl-&setString(CCString::createWithFormat(&GOLD:&%d&,&playerGold)-&getCString());
上述的代码在玩家尝试放置炮塔时,检查是否有足够的金币。如果足够的话,炮塔就会放置上去,并从玩家的金币数中减去炮塔的费用。每次杀死敌人的时候也应该奖励玩家一些金币。打开Enemy.cpp文件,在getDamaged函数里面,if条件后面,添加如下语句:
_theGame-&awardGold(<span style="color:#ff);
编译运行,会看到不能随意的放置炮塔了,因为每个炮塔都要花费金币。当然,杀死敌人就可以获得金币奖励,这样就可以继续购买炮塔。这是一个很好的系统。如下图所示:
13.加入背景音乐和音效。打开HelloWorldScene.cpp文件,添加头文件声明:
#include&&SimpleAudioEngine.h&
在init函数,if条件之后,添加如下代码:
CocosDenshion::SimpleAudioEngine::sharedEngine()-&playBackgroundMusic(&8bitDungeonLevel.mp3&,&true);
在ccTouchesBegan函数,添加一个新的Tower对象前,添加如下代码:
CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect(&tower_place.wav&);
在getHpDamage函数里,添加如下代码:
CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect(&life_lose.wav&);
打开Enemy.cpp文件,添加头文件声明:
#include&&SimpleAudioEngine.h&
在getDamaged函数里,添加如下代码:
CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect(&laser_shoot.wav&);
编译运行,现在游戏将有配乐,关闭掉调试绘制后,效果图:
参考资料:
1.How To Make a Tower Defense Game&
2.钓龟岛保卫战-如何从零开始制作一款iOS塔防游戏(新)&
非常感谢以上资料,本例子源代码附加资源下载地址:
如文章存在错误之处,欢迎指出,以便改正。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1758657次
积分:21632
积分:21632
排名:第143名
原创:365篇
转载:86篇
评论:3016条
联系方式:
(3)(1)(1)(1)(1)(2)(1)(3)(3)(3)(1)(1)(3)(1)(1)(8)(1)(3)(2)(2)(3)(2)(3)(2)(1)(1)(2)(3)(6)(1)(4)(3)(1)(3)(5)(5)(5)(5)(1)(3)(5)(4)(4)(4)(5)(5)(1)(13)(11)(7)(5)(2)(5)(4)(5)(2)(7)(14)(18)(23)(19)(5)(35)(22)(21)(8)(10)(42)(49)}

我要回帖

更多关于 towerdefense hd 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信