本文实践自 Pablo Ruiz 的文章《How To Make a Tower Defense Game》,文中使用Cocos2D,我在这里使用Cocos2d-x 3.0alpha0进行学习和移植。在这篇文章,将会学习到如何制作一个塔防游戏。在这当中,学习如何在设定的时间内出现一波波的敌人,使这些敌人沿着指定的路点前进,如何在地图上指定的位置创建炮塔,如何使炮塔射击敌人,如何可视化调试路点和炮塔的攻击范围。
步骤如下:
1.新建Cocos2d-win32工程,工程名为"TowerDefense",去除"Box2D"选项,勾选"Simple Audio Engine in Cocos Denshion"选项;
2.下载本游戏所需的资源,将资源放置"Resources"目录下;
3.为场景添加背景图片。打开HelloWorldScene.cpp文件,修改init函数,如下:
bool HelloWorld::init()
{
bool bRet = false;
do {
CC_BREAK_IF(!Layer::init());
this->setTouchEnabled(true);
Size winSize = Director::getInstance()->getWinSize();
Sprite* background = Sprite::create("Bg.png");
this->addChild(background);
background->setPosition(Point(winSize.width / 2, winSize.height / 2));
bRet = true;
} while (0);
return bRet;
}
通过放置的背景图片,可以直观的看出哪些地方允许玩家放置炮塔。编译运行,如下图所示:
4.接着,需要沿路设置一些点,在这些点上能够让玩家触摸和建立炮塔。为了方便管理,使用.plist文件来存储炮塔的放置点,这样就可以很容易的改变它们。TowersPosition.plist已经在资源文件夹中,其中已经有了一些炮塔的位置。查看这个文件,可以看到一个字典数组,字典只包含两个键"x"和"y"。每个字典条目代表一个炮塔位置的x和y坐标。现在需要读取这个文件,并且放置塔基到地图上。打开HelloWorldScene.h文件,添加以下变量:
Array* towerBases;
void loadTowerPositions();
打开
HelloWorldScene.cpp
文件,添加如下方法:
void HelloWorld::loadTowerPositions()
{
Array* towerPositions = Array::createWithContentsOfFile("TowersPosition.plist");
towerBases = Array::create();
towerBases->retain();
Object* pObject = NULL;
CCARRAY_FOREACH(towerPositions, pObject)
{
Dictionary* towerPos = (Dictionary*)pObject;
Sprite* towerBase = Sprite::create("open_spot.png");
towerBase->setScale(2);
this->cocos2d::Node::addChild(towerBase);
towerBase->setPosition(Point(((String*)towerPos->objectForKey("x"))->intValue(), ((String*)towerPos->objectForKey("y"))->intValue()) * 2);
towerBases->addObject(towerBase);
}
}
在
init
函数里面,添加背景图片代码之后,添加如下代码:
this->loadTowerPositions();
在析构函数里面,添加如下代码:
HelloWorld::~HelloWorld()
{
towerBases->release();
}
编译运行,就可以看到道路两侧的方块,这些是做为玩家炮塔的基座。如下图所示:
5.开始建立炮塔。打开HelloWorldScene.h文件,添加如下代码:
CC_SYNTHESIZE_RETAIN(Array*, _towers, Towers);
添加
Tower
类,派生自
CCNode
类,
Tower.h
文件代码如下:
#include "cocos2d.h"
#include "HelloWorldScene.h"
USING_NS_CC;
#define kTOWER_COST 300
class Tower:public Node {
public:
Tower();
~Tower();
static Tower* nodeWithTheGame(HelloWorld* game,Point location);
bool initWithTheGame(HelloWorld* game,Point location);
void update(float delta);
void draw();
CC_SYNTHESIZE(HelloWorld*, _theGame, TheGame);
CC_SYNTHESIZE(Sprite*, _mySprite, MySprite);
private:
int attackRange;
int damage;
float fireRate;
};
打开Tower.cpp文件,代码如下:
//
// Tower.cpp
// HelloCpp
//
// Created by 杜甲 on 13-11-30.
//
//
#include "Tower.h"
Tower::Tower()
{
}
Tower::~Tower()
{
}
Tower* Tower::nodeWithTheGame(HelloWorld *game, cocos2d::Point location)
{
Tower* pRet = new Tower();
if (pRet && pRet->initWithTheGame(game, location)) {
return pRet;
}
else
{
CC_SAFE_RELEASE_NULL(pRet);
return pRet;
}
}
bool Tower::initWithTheGame(HelloWorld *game, cocos2d::Point location)
{
bool bRet = false;
do {
attackRange = 70;
damage = 10;
fireRate = 1;
_mySprite = Sprite::create("tower.png");
_mySprite->setScale(2);
this->addChild(_mySprite);
_mySprite->setPosition(location);
_theGame = game;
_theGame->addChild(this);
this->scheduleUpdate();
bRet = true;
} while (0);
return bRet;
}
void Tower::update(float delta)
{
}
void Tower::draw()
{
Node::draw();
#ifdef COCOS2D_DEBUG
DrawPrimitives::setDrawColor4F(255.0f, 255.0f, 255.0f, 255.0f);
DrawPrimitives::drawCircle(_mySprite->getPosition(), attackRange, 360, 30, false);
#endif
}
这个
Tower
类包含几个属性:一个精灵对象,这是炮塔的可视化表现;一个父层的引用,方便访问父层;还有三个变量:
-
attackRange: 炮塔可以攻击敌人的距离。
-
damage: 炮塔对敌人造成的伤害值。
-
fireRate: 炮塔再次攻击敌人的时间间隔。