如何制作一个塔防游戏 Cocos2d x 2 0 4

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

      本文实践自 Pablo Ruiz 的文章《How To Make a Tower Defense Game》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.0.4进行学习和移植。在这篇文章,将会学习到如何制作一个塔防游戏。在这当中,学习如何在设定的时间内出现一波波的敌人,使这些敌人沿着指定的路点前进,如何在地图上指定的位置创建炮塔,如何使炮塔射击敌人,如何可视化调试路点和炮塔的攻击范围。

步骤如下:
1.新建Cocos2d-win32工程,工程名为"TowerDefense",去除"Box2D"选项,勾选"Simple Audio Engine in Cocos Denshion"选项;
2.下载本游戏所需的资源,将资源放置"Resources"目录下;

3.为场景添加背景图片。打开HelloWorldScene.cpp文件,修改init函数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bool HelloWorld::init()
{
     bool bRet =  false;
     do 
    {
        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 bRet;
}

通过放置的背景图片,可以直观的看出哪些地方允许玩家放置炮塔。编译运行,如下图所示:

4.接着,需要沿路设置一些点,在这些点上能够让玩家触摸和建立炮塔。为了方便管理,使用.plist文件来存储炮塔的放置点,这样就可以很容易的改变它们。TowersPosition.plist已经在资源文件夹中,其中已经有了一些炮塔的位置。查看这个文件,可以看到一个字典数组,字典只包含两个键"x"和"y"。每个字典条目代表一个炮塔位置的x和y坐标。现在需要读取这个文件,并且放置塔基到地图上。打开HelloWorldScene.h文件,添加以下变量:

1
cocos2d::CCArray* towerBases;
打开 HelloWorldScene.cpp文件,添加如下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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*)pObject;
        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函数里面,添加背景图片代码之后,添加如下代码:

1
this->loadTowerPositions();
在析构函数里面,添加如下代码:
1
towerBases->release();
编译运行,就可以看到道路两侧的方块,这些是做为玩家炮塔的基座。如下图所示:

5.开始建立炮塔。打开 HelloWorldScene.h文件,添加如下代码:
1
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*, _towers, Towers);
添加 Tower类,派生自 CCNode类, Tower.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
#ifndef __TOWER_H__
#define __TOWER_H__

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

#define kTOWER_COST  300

class Tower :  public cocos2d::CCNode
{
public:
    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);

private:
     int attackRange;
     int damage;
     float fireRate;
};

#endif   // __TOWER_H__

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

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#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 pRet;
    }
     else
    {
         delete pRet;
        pRet =  NULL;
         return  NULL;
    }
}

bool Tower::initWithTheGame(HelloWorld* game, CCPoint location)
{
     bool bRet =  false;
     do 
    {
        attackRange =  70;
        damage =  10;
        fireRate =  1;
        
        _mySprite = CCSprite::create( "tower.png");
         this->addChild(_mySprite);
        _mySprite->setPosition(location);
        _theGame = game;
        _theGame->addChild( this);

         this->scheduleUpdate();

        bRet =  true;
    }  while ( 0);

     return bRet;
}

void Tower::update( float dt)
{

}

void Tower::draw( void)
{
#ifdef COCOS2D_DEBUG
    ccDrawColor4F( 255,  255,  255,  255);
    ccDrawCircle(_mySprite->getPosition(), attackRange,  360,  30,  false);
#endif
    CCNode::draw();
}

这个Tower类包含几个属性:一个精灵对象,这是炮塔的可视化表现;一个父层的引用,方便访问父层;还有三个变量:

  • attackRange: 炮塔可以攻击敌人的距离。
  • damage: 炮塔对敌人造成的伤害值。
  • fireRate: 炮塔再次攻击敌人的时间间隔。
有了这三个变量,就可以创建各种不同攻击属性的炮塔,比如需要很长时间来重新加载的远程重击,或者范围有限的快速攻击。最后,代码中的 draw方法,用于在炮塔周围绘制一个圆,以显示出它的攻击范围,这将方便调试。
6.让玩家添加炮塔。打开 HelloWorldScene.cpp文件,加入以下头文件声明:
1
#include  "Tower.h"
在析构函数中添加如下代码:
1
_towers->release();
init函数,添加如下代码:
1
2
_towers = CCArray::create();
_towers->retain();
添加如下两个方法,代码如下:

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
bool HelloWorld::canBuyTower()
{
     return  true;
}

void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
{
    CCSetIterator iter = pTouches->begin();
     for (; iter != pTouches->end(); iter++)
    {
        CCTouch* pTouch = (CCTouch*)(*iter);
        CCPoint location = pTouch->getLocation();

        CCObject *pObject =  NULL;
        CCARRAY_FOREACH(towerBases, pObject)
        {
            CCSprite *tb = (CCSprite*)pObject;
             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文件代码如下:

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
#ifndef __WAYPOINT_H__
#define __WAYPOINT_H__

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

class Waypoint :  public cocos2d::CCNode
{
public:
    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);

private:
    HelloWorld* theGame;
};

#endif   // __WAYPOINT_H__
打开 Waypoint.cpp文件,代码如下:
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#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 pRet;
    }
     else
    {
         delete pRet;
        pRet =  NULL;
         return  NULL;
    }
}

bool Waypoint::initWithTheGame(HelloWorld* game, CCPoint location)
{
     bool bRet =  false;
     do 
    {
        theGame = game;
        _myPosition = location;

         this->setPosition(CCPointZero);
        theGame->addChild( this);

        bRet =  true;
    }  while ( 0);

     return bRet;
}

void Waypoint::draw( void)
{
#ifdef COCOS2D_DEBUG
    ccDrawColor4F( 0,  255,  0,  255);
    ccDrawCircle(_myPosition,  6,  360,  30,  false);
    ccDrawCircle(_myPosition,  2,  360,  30,  false);

     if (_nextWaypoint)
    {
        ccDrawLine(_myPosition, _nextWaypoint->_myPosition);
    }
#endif

    CCNode::draw();
}
首先,通过传入的 HelloWorld对象引用和路点位置坐标,进行初始化一个 waypoint对象。每个路点都包含下一个路点的引用,这将会创建一个路点链接列表。每个路点知道列表中的下一个路点。通过这种方式,可以引导敌人沿着链表上的路点到达他们的最终目的地。最后, draw方法绘制显示路点的位置,并且绘制一条直线将其与下一个路点进行连接,这仅仅用于调试目的。

8.创建路点列表。打开HelloWorldScene.h文件,添加以下代码:

1
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*, _waypoints, Waypoints);
打开 HelloWorldScene.cpp文件,加入以下头文件声明:
1
#include  "Waypoint.h"
在析构函数中添加如下代码:
1
_waypoints->release();
添加以下方法:

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
void HelloWorld::addWaypoints()
{
    _waypoints = CCArray::create();
    _waypoints->retain();

    Waypoint *waypoint1 = Waypoint::nodeWithTheGame( this, ccp( 420,  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,  130));
    _waypoints->addObject(waypoint3);
    waypoint3->setNextWaypoint(waypoint2);

    Waypoint *waypoint4 = Waypoint::nodeWithTheGame( this, ccp( 445,  130));
    _waypoints->addObject(waypoint4);
    waypoint4->setNextWaypoint(waypoint3);

    Waypoint *waypoint5 = Waypoint::nodeWithTheGame( this, ccp( 445,  220));
    _waypoints->addObject(waypoint5);
    waypoint5->setNextWaypoint(waypoint4);

    Waypoint *waypoint6 = Waypoint::nodeWithTheGame( this, ccp(- 40,  220));
    _waypoints->addObject(waypoint6);
    waypoint6->setNextWaypoint(waypoint5);
}

init函数,添加如下代码:

1
this->addWaypoints();
编译运行,效果如下图所示:

在地图上有6个路点,这是敌人的行走路线。在让敌人出现在游戏中前,还需要添加一个辅助方法。打开 HelloWorldScene.cpp文件,添加方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
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 + ydif * ydif);

     if(distance <= radius + radiusTwo) 
    {
         return  true;
    }
     return  false;
}

方法collisionWithCircle用于判断两个圆是否碰撞或者相交。这将用于判断敌人是否到达一个路点,同时也可以检测敌人是否在炮塔的攻击范围之内。
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文件,代码如下:
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include  "Enemy.h"
using  namespace cocos2d;

#define HEALTH_BAR_WIDTH  20
#define HEALTH_BAR_ORIGIN - 10

Enemy::Enemy(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值