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、付费专栏及课程。

余额充值