cocos2d-x横版格斗游戏教程4

上一篇我们已经可以看到英雄和机器人都处于无敌状态,现在让他们互相残杀吧,所以接下来将要实现碰撞检测功能。先来看看下面这张图:


这里碰撞检测采用比较简单的矩形,可以看到英雄和机器人在攻击的时候会把拳头伸出去,我们可以把英雄分成两个矩形框,身体(被攻击的部分)矩形区域和拳头(攻击部分)的矩形区域,如上图的蓝色和红色区域,机器人是一样的。这样的话,英雄攻击机器人的时候,只需要检测英雄的红色区域跟机器人的蓝色区域是否有交集,如果这两个矩形有交集,则为击中;机器人攻击英雄也是一样的道理。既然原理弄明白了,现在就开始写代码吧。
首先在BaseSprite.h中添加:

1

2

3

4

5

typedef struct _BoundingBox

{

 cocos2d::Rect actual;

 cocos2d::Rect original;

}BoundingBox;

在BaseSprite类中添加:

1

2

3

4

5

6

CC_SYNTHESIZE(BoundingBox, m_bodyBox, BodyBox);

 CC_SYNTHESIZE(BoundingBox, m_hitBox, HitBox);

  

virtual void setPosition(const cocos2d::Point &position);

 BoundingBox createBoundingBox(cocos2d::Point origin, cocos2d::Size size);

 void updateBoxes();

声明结构体BoundingBox,表示碰撞盒,actual这个矩形是以屏幕左下角为原点的,在进行碰撞检测时就使用它;original用来保存精灵本身的矩形信息,以精灵左下角为起点,比如上图的蓝色或红色矩形,在每次更新actual时使用。这里还重写了setPosition函数,在更新精灵位置的时候也需要更新碰撞盒的坐标。下面看实现代码:

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

BoundingBox BaseSprite::createBoundingBox(cocos2d::Point origin, cocos2d::Size size)

{

 BoundingBox boundingBox;

 boundingBox.original.origin= origin;

 boundingBox.original.size= size;

 boundingBox.actual.origin = this->getPosition() + boundingBox.original.origin;

 boundingBox.actual.size= size;

 return boundingBox;

}

  

void BaseSprite::updateBoxes() {

 bool isFlippedX = this->isFlippedX();

 float x = 0.0f;

 if(isFlippedX) {

  x = this->getPosition().x - m_hitBox.original.origin.x;

 }else {

  x = this->getPosition().x + m_hitBox.original.origin.x;

 }

 m_hitBox.actual.origin = Point(x, this->getPosition().y + m_hitBox.original.origin.y);

    m_bodyBox.actual.origin = this->getPosition() + m_bodyBox.original.origin;

}

  

void BaseSprite::setPosition(const Point &position)

{

 Sprite::setPosition(position);

 this->updateBoxes();

}

需要注意:在更新碰撞盒的时候,攻击盒子需要判断精灵的朝向,面向左和面向右的坐标不一样。
现在来实现碰撞检测的代码,在GameLayer.cpp中添加:

1

2

3

4

5

6

7

8

9

10

bool collisionDetection(const BoundingBox &hitBox, const BoundingBox &bodyBox)

{

 Rect hitRect = hitBox.actual;

 Rect bodyRect = bodyBox.actual;

 if(hitRect.intersectsRect(bodyRect))

 {

  return true;

 }

 return false;

}

比较简单,就是判断攻击盒子跟身体盒子是否有交集而已。
接着更新GameLayer.cpp的onHeroAttack函数,添加下面的代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

if(m_pHero->getCurrActionState() == ACTION_STATE_ATTACK)

  {

   Object *enemyObj = NULL;

   CCARRAY_FOREACH(m_pEnemies, enemyObj)

   {

    Enemy *pEnemy = (Enemy*)enemyObj;

    if(fabsf(m_pHero->getPosition().y - pEnemy->getPosition().y) < 10)

    {

     BoundingBox heroHitBox = m_pHero->getHitBox();

     BoundingBox enemyBodyBox = pEnemy->getBodyBox();

     if(::collisionDetection(heroHitBox, enemyBodyBox))

     {

      pEnemy->runHurtAction();

     }

    }

   }

  }

这里只是进行了碰撞检测,打中了就执行受伤动画,依然处于不死状态,同理,更新GameLayer.cpp的onEnemyAttack函数,添加:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

Object *enemyObj = NULL;

 CCARRAY_FOREACH(m_pEnemies, enemyObj)

 {

  Enemy *pEnemy = (Enemy*)enemyObj;

  if(pEnemy->getCurrActionState() == ACTION_STATE_ATTACK)

  {

   pEnemy->setPositionY(m_pHero->getPositionY());

   BoundingBox heroBodyBox = m_pHero->getBodyBox();

   BoundingBox enemyHitBox = pEnemy->getHitBox();

   if(::collisionDetection(enemyHitBox, heroBodyBox))

   {

    m_pHero->runHurtAction();

   }

  }

 }

初始化英雄和机器人的碰撞盒,在Hero.cpp的init函数中添加:

1

2

3

Size heroShowSize = this->getDisplayFrame()->getRect().size;

  this->m_bodyBox = this->createBoundingBox(Point(-heroShowSize.width / 2, -heroShowSize.height / 2), heroShowSize);

  this->m_hitBox = this->createBoundingBox(Point(heroShowSize.width / 2, -5), Size(25, 20));

在Enemy.cpp的init函数中添加:

1

2

3

Size enemyShowSize = this->getDisplayFrame()->getRect().size;

  this->m_bodyBox = this->createBoundingBox(Point(-enemyShowSize.width / 2, -enemyShowSize.height / 2), enemyShowSize);

  this->m_hitBox = this->createBoundingBox(Point(enemyShowSize.width / 2, -5), Size(25, 20));

这里的25和20分别是精灵攻击盒子的宽和高,这些值从上图可以量出。
OK,编译运行项目,现在可以看到英雄和机器人被A的傻样了:


不过现在都打不死,接下来给英雄设置生命值和攻击力,然后在每次碰撞检测后更新精灵生命值和状态:
在GameLayer.cpp的init函数添加:

1

2

m_pHero->setAttack(5);

  m_pHero->setHP(100);

更新GameLayer.cpp的onHeroAttack函数:

1

2

3

4

5

6

7

8

9

10

11

if(::collisionDetection(heroHitBox, enemyBodyBox))

    {

     int damage = m_pHero->getAttack();

     pEnemy->runHurtAction();

     pEnemy->setHP(pEnemy->getHP() - damage);

 

     if(pEnemy->getHP() <= 0)

     {

      pEnemy->runDeadAction();

     }

    }

更新onEnemyAttack函数:

1

2

3

4

5

6

7

8

9

10

11

if(::collisionDetection(enemyHitBox, heroBodyBox))

 {

  int damage = pEnemy->getAttack();

  m_pHero->runHurtAction();

  m_pHero->setHP(m_pHero->getHP() - damage);

 

  if(m_pHero->getHP() <= 0)

  {

   m_pHero->runDeadAction();

  }

 }

重新编译运行,效果如下:


感觉还是少了点什么,发现太安静了,一款游戏怎么能少了背景音乐和音效呢,现在就给加上吧。
在GameLayer.h中添加音频文件路径:

1

2

3

4

5

6

#define PATH_BG_MUSIC "background-music-aac.wav"

#define PATH_HERO_HIT_EFFECT "pd_hit0.wav"

#define PATH_ENEMY_HIT_EFFECT "pd_hit1.wav"

#define PATH_HERO_DEAD_EFFECT "pd_herodeath.mp3"

#define PATH_ENEMY_DEAD_EFFECT "pd_botdeath.wav"

#define PATH_HERO_TALK_EFFECT "hero_talk.mp3"

在GameLayer.cpp的init函数最后添加:

1

2

CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic(PATH_BG_MUSIC, true);

CocosDenshion::SimpleAudioEngine::getInstance()->playEffect(PATH_HERO_TALK_EFFECT);

更新onHeroAttack函数:

1

2

3

4

5

     if(::collisionDetection(heroHitBox, enemyBodyBox))

     {

//......

      CocosDenshion::SimpleAudioEngine::getInstance()->playEffect(PATH_HERO_HIT_EFFECT);

     }

更新onEnemyAttack函数:

1

2

3

4

5

6

7

8

9

10

11

12

if(::collisionDetection(enemyHitBox, heroBodyBox))

  {

   int damage = pEnemy->getAttack();

   m_pHero->runHurtAction();

   m_pHero->setHP(m_pHero->getHP() - damage);

   CocosDenshion::SimpleAudioEngine::getInstance()->playEffect(PATH_ENEMY_HIT_EFFECT);

   if(m_pHero->getHP() <= 0)

   {

    m_pHero->runDeadAction();

    CocosDenshion::SimpleAudioEngine::getInstance()->playEffect(PATH_HERO_DEAD_EFFECT);

   }

  }

这里添加了LOL中大鳄鱼的“所有人都得死”的音效,英雄出场十分的霸气啊。
到目前为止,游戏的基本功能已完成了,不过现在如果英雄死了或者机器人死完之后游戏就没法继续下去了。可以在游戏结束后添加一个GameOver的提示,然后自动重新开始;还可以实现显示英雄的血条,机器人死后自动添加机器人等功能。。
下一篇就来实现把游戏移植到android上吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值