转载至: http://cn.cocos2d-x.org/tutorial/show?id=1248
到上一章结束为止,我们的场景中都还只有一个移动的敌人,这样的游戏是非常乏味无聊的,它简直不像是一个游戏。所以接下来我们将添加更多的敌人,并加上分数系统,使我们的塔防游戏看起来更像一个真正的游戏、完整的游戏。
关于本部分教程的代码,点击下载。
运行该部分demo,你将看到如下所示的效果图:
从图中我们可以看到,本部分塔防游戏的功能已经是比较完善的了。在此部分游戏中,敌人会一个接一个连续的出现,而且是分时分批分类型依次进入场景的。不同类型敌人的生命值、移动速度等等都是不一样的,每批敌人的数量也是不相同的。同时,本游戏还拥有了完善的得分体系和购买机制。
得分体系:游戏刚开始的时候,玩家拥有满级的生命值(100),所以它拥有三颗星的分数,不过每当有敌人顺利通过时,玩家的生命值就会减少10,游戏会根据当前剩余的生命值判断玩家的分数(比如:生命值小于30时,玩家就只有一颗星的分数了)。当有10个敌人通过时,玩家就彻底失败了,游戏也就此结束。
购买机制:玩家射杀敌人获取金币,购买炮塔消耗金币。
同前面各章一样,由于本部分代码涉及的内容信息较多,所以本章教程会先教大家实现一个接一个的添加敌人。下一章则会实现游戏信息的读取。
GroupEnemy(一波敌人类)
在塔防游戏中有个很重要的概念就是:一波敌人。玩过植物大战僵尸、保卫萝卜等塔防游戏的玩家对这个名词肯定不会陌生。
同样地,我们的游戏也会这样设计,设计一个包含了一整波敌人种类、个数、生命值等信息的类。通俗点说就是:这个类存储了一波敌人的数据信息,它决定了一波敌人里Thief的个数和生命值;Pirate的个数和生命值;Bandit的个数和生命值。
定义这样的一个类可以帮助我们更加方便的设计出更多样的阵容和不同攻击强度的敌人战队。这样的话,在后续设计关卡的时候也会有很大的帮助。这个类我们把它叫做群组类(GroupEnemy)。下面就跟着我们来创建一个这样的类吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class
GroupEnemy:
public
cocos2d::Node
{
public
:
virtual
bool
init();
GroupEnemy* initGroupEnemy(
int
type1Total,
int
type1Hp,
int
type2Total,
int
type2Hp,
int
type3Total,
int
type3Hp );
CREATE_FUNC(GroupEnemy);
CC_SYNTHESIZE(
int
, type1Total, Type1Total);
CC_SYNTHESIZE(
int
, type2Total, Type2Total);
CC_SYNTHESIZE(
int
, type3Total, Type3Total);
CC_SYNTHESIZE(
int
, type1Hp, Type1Hp);
CC_SYNTHESIZE(
int
, type2Hp, Type2Hp);
CC_SYNTHESIZE(
int
, type3Hp, Type3Hp);
CC_SYNTHESIZE(
int
, enemyTotal, EnemyTotal);
CC_SYNTHESIZE(
bool
, isFinishedAddGroup, IsFinishedAddGroup);
};
|
在该类中,参数type1Total,type2Total,type3Total分别代表了小偷,土匪,海盗三种敌人的数目,type1Hp,type2Hp,type3Hp代表了它们的生命值,而enemyTotal则表示总敌人的总个数,isFinishedAddGroup用于判断该波敌人是否全部都添加到了场景。
1
2
3
4
5
6
7
8
9
10
11
12
|
GroupEnemy* GroupEnemy::initGroupEnemy(
int
type1Total,
int
type1Hp,
int
type2Total,
int
type2Hp,
int
type3Total,
int
type3Hp)
{
this
->type1Total = type1Total;
this
->type2Total = type2Total;
this
->type3Total = type3Total;
this
->type1Hp = type1Hp;
this
->type2Hp = type2Hp;
this
->type3Hp = type3Hp;
this
->enemyTotal = type1Total + type2Total + type3Total;
this
->isFinishedAddGroup =
false
;
return
this
;
}
|
initGroupEnemy方法用于初始化本波敌人信息。
挨个添加敌人
现在,GroupEnemy类我们已经创建好了,接下来就让我们回到PlayLayer中,在这儿我们将完成一项丰功伟业:在游戏中添加一波又一波的敌人。不过在此之前,还是先让我们来整理整理思路吧。
在游戏中,我们会先创建并初始化不同的GroupEnemy,并会把这些创建好的GroupEnemy添加到GameManager对象的一个向量中,方便统一管理。这和管理敌人、子弹的方法一样。
接着我们会一波一波的添加敌人,当一波敌人被全部添加,并且这波的敌人数量为0(被消灭或顺利过关)时,再接着添加下一波、再下一波......。
添加每波的敌人时,我们会根据这波敌人给出的信息依次创建不同种类,不同数量,不同生命值的敌人。这样一来,我们就实现了一个接一个添加敌人的功能。
在GameManager中管理着我们的游戏信息,
游戏中管理GroupEnemy对象的向量是`Vector<GroupEnemy*> groupVector;`,我们可以通过遍历这个向量来获取当前波和下一波的敌人信息。下面是实现方法:
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
|
GroupEnemy* PlayLayer::currentGroup()
{
GroupEnemy* groupEnemy;
if
(!instance->groupVector.empty() )
{
groupEnemy = (GroupEnemy*)instance->groupVector.at(groupCounter);
}
else
groupEnemy =NULL;
return
groupEnemy;
}
GroupEnemy* PlayLayer::nextGroup()
{
if
(groupCounter < GroupToTal - 1)
{
groupCounter++;
}
else
{
isSuccessful =
true
;
}
GroupEnemy* groupEnemy = (GroupEnemy*)instance->groupVector.at(groupCounter);
return
groupEnemy;
}
|
这里我们通过变量groupCounter获得当前波和下一波敌人信息。向量groupVector中第groupCounter项就是当前波的敌人信息,当我们想获取下一波敌人信息的时候,我们就递增groupCounter的值。当groupCounter达到游戏的总波数时,证明游戏已经添加完所有的敌人,玩家挺过了所以进攻的敌人。
接下来让我们来看看添加敌人的方法,如下所示。logic是一个需要定时执行的函数。它会由定时器函数`schedule(SEL_SCHEDULE selector, float interval)` 方法启用,interval是每次调用的间隔时间。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
void
PlayLayer::logic(
float
dt)
{
// 1
GroupEnemy* groupEnemy =
this
->currentGroup();
if
(groupEnemy == NULL){
return
; }
// 2
if
(groupEnemy->getIsFinishedAddGroup() ==
true
&& instance->enemyVector.size() == 0 && groupCounter < instance->getGroupNum())
{
groupEnemy =
this
->nextGroup();
}
// 3
this
->addEnemy();
}
|
1. 获取当前波次的敌人信息,如果为空,则返回。
2. 当当前波的敌人全部都添加到了场景,并且此时场景中敌人数量变为0(被消灭或顺利过关)时,添加下一波的敌人。
3. 添加敌人。也就是说,只要groupEnemy不为空,那么每dt秒就会执行一次addEnemy方法来添加敌人。
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
|
void
PlayLayer::addEnemy()
{
GameManager *instance = GameManager::getInstance();
GroupEnemy* groupEnemy =
this
->currentGroup();
if
(groupEnemy == NULL)
{
return
;
}
// 1
auto restEnemyNum = groupEnemy->getEnemyTotal();
if
( restEnemyNum <= 0){
groupEnemy->setIsFinishedAddGroup(
true
);
return
;
}
// 2
restEnemyNum--;
groupEnemy->setEnemyTotal(restEnemyNum);
// 3
EnemyBase* enemy = NULL;
if
(groupEnemy->getType1Total() > 0){
enemy = Thief::createThief(pointsVector, groupEnemy->getType1Hp());
groupEnemy->setType1Total(groupEnemy->getType1Total() - 1);
}
else
if
(groupEnemy->getType2Total() > 0){
enemy = Pirate::createPirate(pointsVector, groupEnemy->getType2Hp());
groupEnemy->setType2Total(groupEnemy->getType2Total() - 1);
}
else
if
(groupEnemy->getType3Total() > 0){
enemy = Bandit::createBandit(pointsVector, groupEnemy->getType3Hp());
groupEnemy->setType3Total(groupEnemy->getType3Total() - 1);
}
// 4
this
->addChild(enemy, 10);
instance->enemyVector.pushBack(enemy);
}
|
addEnemy方法会取出当前波敌人的信息挨个添加敌人,下面是该函数的具体讲解。
1. 判断当前波剩余的敌人(待添加)总数是否为0,如果为0,则表示该波敌人全部都被添加到了场景。
2. 因为该函数会添加一个敌人,敌人的剩余总数也会减少,所以此处需要重新设置剩余敌人的总数。
3. 根据当前波敌人信息,依次添加Thief,Pirate和Bandit。添加相应敌人的同时需要减少它的总数值。
4. 把创建好的敌人添加到场景,并且添加到敌人列表中。