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

来源:互联网 发布:fcm图像分割算法 编辑:程序博客网 时间:2024/05/09 14:19


这一篇要为英雄创造一些小伙伴了,并且需要让机器人会巡逻,会偷懒,会行走,还会攻击英雄,当然也能受伤。其实机器人和英雄有一些共同的属性:攻击力、生命值和行走速度。但机器人是由电脑控制,状态是随机切换的,所以还需要指定巡逻区域、攻击区域、行走方向、决策时间等。

1. 添加机器人

首先更新BaseSprite类,添加攻击力和生命值属性,在BaseSprite.h中添加:

1
2
CC_SYNTHESIZE(unsignedint, m_hp, HP);
CC_SYNTHESIZE(unsignedint, m_attack, Attack);

创建Enemy类,代表敌方机器人,这里需要实现简单的AI,让机器人能自动思考,根据具体环境切换自己的状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Enemy.h
typedefenum {
 AI_IDLE = 0,
 AI_PATROL,
 AI_ATTACK,
 AI_PURSUIT
}AiState;
classEnemy : publicBaseSprite
{
public:
 Enemy();
 ~Enemy();
 boolinit();
 CREATE_FUNC(Enemy);
 CC_SYNTHESIZE(cocos2d::Point, m_moveDirection, MoveDirection);
 CC_SYNTHESIZE(float, m_eyeArea, EyeArea);
 CC_SYNTHESIZE(float, m_attackArea, AttackArea)
 CC_SYNTHESIZE(AiState, m_aiState, AiState);
 voidexecute(constcocos2d::Point& target, floattargetBodyWidth);
private:
 voiddecide(constcocos2d::Point& target, floattargetBodyWidth);
 unsignedint m_nextDecisionTime;
};

AiState表示机器人的四种状态:休闲、巡逻、攻击、跟随。机器人还有几个变量,分别表示:行走方向、巡逻范围、攻击范围、当前AI状态。
m_nextDecisionTime表示机器人距离下一次决策的时间,execute函数是在GameLayer.cpp中update函数调用的,定期执行更新机器人状态。decide函数实现机器人怎么决策,是机器人的内心世界。
这里重点分析机器人AI该怎么实现,因为只是一个demo,所以就尽可能的简单些吧。首先机器人需要根据自己的朝向和英雄的位置来思考,如果机器人背对着英雄或者英雄处于机器人巡逻范围之外,那么此时机器人是看不到英雄的,机器人就会随机的选择继续巡逻或者站着偷懒。如果英雄在机器人的巡逻范围内,且被机器人看到了,则机器人需要判断英雄是否处于自己的攻击范围,来决策是攻击还是追过去。每种状态下的思考时间最好设置成随机的,这样更真实。看源码实现:

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
Enemy.cpp
Enemy::Enemy():
 m_nextDecisionTime(0)
{}
  
Enemy::~Enemy()
{}
  
bool Enemy::init()
{
 boolret = false;
 do{
  this->initWithSpriteFrameName("robot_idle_00.png");
  Animation *pIdleAnim =this->createAnimation("robot_idle_%02d.png", 5, 12);
  this->setIdleAction(RepeatForever::create(Animate::create(pIdleAnim)));
  Animation *pWalkAnim =this->createAnimation("robot_walk_%02d.png", 6, 12);
  this->setWalkAction(RepeatForever::create(Animate::create(pWalkAnim)));
  
  Animation *pAttackAnim =this->createAnimation("robot_attack_%02d.png", 5, 20);
  this->setAttackAction(Sequence::create(Animate::create(pAttackAnim), BaseSprite::createIdleCallbackFunc(), NULL));
  Animation *pHurtAnim =this->createAnimation("robot_hurt_%02d.png", 3, 20);
  this->setHurtAction(Sequence::create(Animate::create(pHurtAnim), BaseSprite::createIdleCallbackFunc(), NULL));
  Animation *pDeadAnim =this->createAnimation("robot_knockout_%02d.png", 5, 12);
  this->setDeadAction(Sequence::create(Animate::create(pDeadAnim), Blink::create(3, 9), NULL));
  ret =true;
 }while(0);
 returnret;
}
  
voidEnemy::execute(constPoint& target, floattargetBodyWidth)
{
 if(m_nextDecisionTime == 0)
 {
  this->decide(target, targetBodyWidth);
 }else{
  -- m_nextDecisionTime;
 }
}
  
voidEnemy::decide(constPoint& target, floattargetBodyWidth)
{
 Point location =this->getPosition();
 floatdistance = location.getDistance(target);
 distance = distance - (targetBodyWidth / 2 +this->getDisplayFrame()->getRect().size.width / 2) + 30;
 //log("distance=%f, m_fVelocity=%f", distance, m_fVelocity);
 boolisFlippedX = this->isFlippedX();
 boolisOnTargetLeft = (location.x < target.x ? true: false);
 if((isFlippedX && isOnTargetLeft) || (!isFlippedX && !isOnTargetLeft)) {
  this->m_aiState = CCRANDOM_0_1() > 0.5f ? AI_PATROL : AI_IDLE;
 }else{
  if(distance < m_eyeArea)
  {
   this->m_aiState = distance < m_attackArea ? AI_ATTACK : AI_PURSUIT;
  }else{
   this->m_aiState = CCRANDOM_0_1() > 0.5f ? AI_PATROL : AI_IDLE;
  }
 }
 switch(m_aiState)
 {
 caseAI_ATTACK:
  {
   this->runAttackAction();
   this->attack();
   this->m_nextDecisionTime = 50;
  }
  break;
 caseAI_IDLE:
  {
   this->runIdleAction();
   this->m_nextDecisionTime = CCRANDOM_0_1() * 100;
  }
  break;
 caseAI_PATROL:
  {
   this->runWalkAction();
   this->m_moveDirection.x = CCRANDOM_MINUS1_1();
   this->m_moveDirection.y = CCRANDOM_MINUS1_1();
   m_moveDirection.x = m_moveDirection.x > 0 ? (m_moveDirection.x + m_fVelocity.x) : (m_moveDirection.x - m_fVelocity.x);
   m_moveDirection.y = m_moveDirection.y > 0 ? (m_moveDirection.y + m_fVelocity.y) : (m_moveDirection.y - m_fVelocity.y);
   this->m_nextDecisionTime = CCRANDOM_0_1() * 100;
  }
  break;
 caseAI_PURSUIT:
  {
   this->runWalkAction();
   //v.normalize() function return the unit vector of v
   this->m_moveDirection = (target - location).normalize();
   this->setFlippedX(m_moveDirection.x < 0 ?true : false);
   m_moveDirection.x = m_moveDirection.x > 0 ? (m_moveDirection.x + m_fVelocity.x) : (m_moveDirection.x - m_fVelocity.x);
   m_moveDirection.y = m_moveDirection.y > 0 ? (m_moveDirection.y + m_fVelocity.y) : (m_moveDirection.y - m_fVelocity.y);
   this->m_nextDecisionTime = 10;
  }
  break;
 }
}

当机器人思考接下来该做什么时,就会执行相应的操作和动画。
机器人创造完成了,现在把它添加到游戏中去,修改GameLayer.h,添加下面的代码:

1
2
3
4
5
6
7
#define MIN_ENEMY_COUNT 5
  
 voidupdateEnemies(floatdt);
 voidaddEnemy();
 voidonEnemyAttack(BaseSprite *pSprite);
  
 cocos2d::Array *m_pEnemies;

updateEnemies表示每一次循环都会更新每个机器人的状态,onEnemyAttack是机器人攻击英雄时执行的函数,暂时不实现。m_pEnemies为保存机器人的容器。
修改GameLayer.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
void GameLayer::addEnemy()
{
 Size winSize = Director::getInstance()->getWinSize();
 Point location = Point::ZERO;
 Enemy *pEnemy = Enemy::create();
 //log("m_pTiledMap->getMapSize() mapSize=%f", m_pTiledMap->getMapSize().width);
 floathalfEnemyFrameHeight = (pEnemy->getDisplayFrame()->getRect().size.height) / 2;
 floatheroPosX = m_pHero->getPositionX();
 floathalfWinWidth = (winSize.width / 2);
 while(fabsf(heroPosX - location.x) < 150)
 {
  if(heroPosX < halfWinWidth)
  {
   location.x = m_pHero->getPositionX() + CCRANDOM_0_1() * halfWinWidth;
  }elseif(heroPosX > (m_pTiledMap->getMapSize().width * m_fTileWidth - halfWinWidth)) {
   location.x = m_pHero->getPositionX() - CCRANDOM_0_1() * halfWinWidth;
  }else{
   location.x = m_pHero->getPositionX() + CCRANDOM_MINUS1_1() * halfWinWidth;
  }
 }
 floatmaxY = m_fTileHeight * 3 + halfEnemyFrameHeight;
 location.y = CCRANDOM_0_1() * maxY;
 if(location.y < halfEnemyFrameHeight)
 {
  location.y = halfEnemyFrameHeight;
 }
 pEnemy->attack = CC_CALLBACK_0(GameLayer::onEnemyAttack,this, pEnemy);
 pEnemy->setPosition(m_origin + location);
 pEnemy->setZOrder(m_fScreenHeight - pEnemy->getPositionY());
 pEnemy->runIdleAction();
 pEnemy->setAttack(5);
 pEnemy->setHP(30);
 pEnemy->setVelocity(Point(0.5f, 0.5f));
 pEnemy->setEyeArea(200);
 pEnemy->setAttackArea(25);
 m_pEnemies->addObject(pEnemy);
 m_pSpriteNodes->addChild(pEnemy);
}
  
voidGameLayer::updateEnemies(floatdt) {
    Object *pObj = NULL;
 Point distance = Point::ZERO;
  
 Point heroLocation = m_pHero->getPosition();
 Array *pRemovedEnemies = Array::create();
    CCARRAY_FOREACH(m_pEnemies, pObj)
 {
  Enemy *pEnemy = (Enemy*)pObj;
  pEnemy->execute(heroLocation, m_pHero->getDisplayFrame()->getRect().size.width);
  if(pEnemy->getCurrActionState() == ACTION_STATE_WALK)
  {
   Point location = pEnemy->getPosition();
   Point direction = pEnemy->getMoveDirection();
  
   Point expect = location + direction;
   floathalfEnemyFrameHeight = (pEnemy->getDisplayFrame()->getRect().size.height) / 2;
   if(expect.y < halfEnemyFrameHeight || expect.y > (m_fTileHeight * 3 + halfEnemyFrameHeight) )
   {
    direction.y = 0;
   }
   pEnemy->setFlippedX(direction.x < 0 ?true : false);
   pEnemy->setPosition(location + direction);
   pEnemy->setZOrder(pEnemy->getPositionY());
  }
 }
 CCARRAY_FOREACH(pRemovedEnemies, pObj)
 {
  Enemy *pEnemy = (Enemy*)pObj;
  m_pEnemies->removeObject(pEnemy);
  m_pSpriteNodes->removeChild(pEnemy,true);
 }
 pRemovedEnemies->removeAllObjects();
}
  
voidGameLayer::onEnemyAttack(BaseSprite *pSprite)
{
}

在GameLayer.cpp的update函数中添加:

1
this->updateEnemies(dt);

在init函数中添加:

1
2
3
4
5
6
m_pEnemies = Array::createWithCapacity(MIN_ENEMY_COUNT);
 m_pEnemies->retain();
 for(inti = 0; i < MIN_ENEMY_COUNT; ++ i)
 {
  this->addEnemy();
 }

OK,现在编译运行项目,就可以看到屏幕上有5个机器人追着英雄打了,效果如下图:


目前机器人和英雄都没有攻击效果,都是无敌状态,不过他们好日子快到头了,下一篇我们就来让他们接受现实的残酷吧。

0 0
原创粉丝点击