转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。
资源为网上寻找的,仅研究学习用,若是侵犯版权请通知本人整改。
上一篇: Cocos3.4 横版游戏制作-《KillBear》-添加血条 攻击按键
上篇在状态层加入了血条,并添加了一个攻击按键
本篇将在前面的基础上添加敌人,并通过有限状态机(FSM)实现简单的AI
开发环境
win64 : vs2010
Cocos2d-x v3.4Final
TexturePackerGUI
MapEdit
代码A
角色Role
Enemy
创建一个继承基础Role类,作为敌人.
- .h
作为AI类型
其他的代码和Hero大同小异
@H_403_30@public: Enemy(); ~Enemy(); bool init(); void updateSelf(); CREATE_FUNC(Enemy); CC_SYNTHESIZE(cocos2d::Vec2,m_moveDirection,MoveDirection); CC_SYNTHESIZE(float,m_eyeArea,EyeArea); CC_SYNTHESIZE(float,m_attackArea,AttackArea) CC_SYNTHESIZE(AiState,m_aiState,AiState); private: void decide(const cocos2d::Vec2& target,float targetBodyWidth); void execute(const cocos2d::Vec2& target,float targetBodyWidth); unsigned int m_nextDecisionTime;由于敌人是AI,我们要给它设定一些区域,视野,最大攻击判定区,AI状态等.
- .cpp
init类似
@H_403_30@Animation *idleAnim = this->createNomalAnimation("bear_idle_%02d.png",3,6); this->setIdleAction(RepeatForever::create(Animate::create(idleAnim))); ...然后是类似Hero的自更新函数updateSelf刷新自己状态
@H_403_30@void Enemy::updateSelf() { //this->execute(global->hero->getPosition() + global->hero->getBodyBox().actual.origin,global->hero->getBodyBox().actual.size.width); this->execute(global->hero->getPosition(),global->hero->getBodyBox().actual.size.width);//对象坐标及body宽度 if(this->getCurrActionState() == ACTION_STATE_WALK) { Vec2 location = this->getPosition(); Vec2 direction = this->getMoveDirection(); Vec2 expectP = location + direction; float maptileHeight = global->tileMap->getTileSize().height; //if(expectP.y<0 || !global->tileAllowMove(expectP)) //这群笨蛋AI总跑出地图报错.后续再去改进吧 if(expectP.y < 0 || expectP.y > maptileHeight * 3 ) { direction.y =0; } this->setFlippedX(direction.x < 0 ? true : false); this->setPosition(location + direction); this->updateBoxes(); this->setLocalZOrder(this->getPositionY()); } if(this->getCurrActionState() == ACTION_STATE_NOMAL_ATTACK_A) { this->runNomalAttackA(); } }之后我们给它一个执行延时,这是为了防止过快判断,判读的太过频繁不止给cpu压力,还会使效果出现问题.比如我们的敌人死追着Hero不放,撞了墙还在向着Hero的方向执行动画等.
@H_403_30@void Enemy::execute(const Vec2& target,float targetBodyWidth) { if(m_nextDecisionTime == 0)//lazy延时到0执行下一个动作判定 { this->decide(target,targetBodyWidth); }else { -- m_nextDecisionTime; } }接下来通过FSM设定敌人的AI
@H_403_30@void Enemy::decide(const Vec2& target,float targetBodyWidth) { //Vec2 location = this->getPosition()+ this->getBodyBox().actual.origin;//获得自己的身体中心的坐标 Vec2 location = this->getPosition();//获得脚下坐标 float distance = location.getDistance(target);//与对象Body的距离 distance = distance - targetBodyWidth / 2;//距离范围应该减去body宽度 bool isFlippedX = this->isFlippedX(); bool isOnTargetLeft = (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)&&((fabsf(location.y - target.y) < 15)) ? AI_ATTACK : AI_PURSUIT; }else { this->m_aiState = CCRANDOM_0_1() > 0.5f ? AI_PATROL : AI_IDLE; } } switch(m_aiState) { case AI_ATTACK: { this->runNomalAttackA(); //this->attack(); this->m_nextDecisionTime = 50; } break; case AI_IDLE: { this->runIdleAction(); this->m_nextDecisionTime = CCRANDOM_0_1() * 100; } break; case AI_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 + velocity.x) : (m_moveDirection.x -velocity.x); m_moveDirection.y = m_moveDirection.y > 0 ? (m_moveDirection.y +velocity.y) : (m_moveDirection.y -velocity.y); this->m_nextDecisionTime = CCRANDOM_0_1() * 100; } break; case AI_PURSUIT: { this->runWalkAction(); this->m_moveDirection = (target - location).getNormalized(); this->setFlippedX(m_moveDirection.x < 0 ? true : false); m_moveDirection.x = m_moveDirection.x > 0 ? (m_moveDirection.x +velocity.x) : (m_moveDirection.x -velocity.x); m_moveDirection.y = m_moveDirection.y > 0 ? (m_moveDirection.y +velocity.y) : (m_moveDirection.y -velocity.y); this->m_nextDecisionTime = 10; } break; } }distance是为了判断这个敌人和目标Body之间的距离.
下面是几个AI的判断,里面用到几个宏是随机一个数,更真实的表现敌人.
主要的AI是:
- 目标出现在正前方?(根据视野范围)发呆or巡逻
- 是否在攻击范围内?(根据攻击范围)追击or攻击
这个图片更好理解下
接下来在GameLayer加入他们
我们每次加入多个敌人,方便起见需要使用数组来实现它(链表更好)
Game
GameLayer
- .h
实现addEnemies,一次加入多个敌人通过这个数组创建.最后别忘了将这个数组注册到Global,下一章做攻击判断用
- .cpp
init中
@H_403_30@this->addEnemies(5);结果图A
我们看到了几个敌人呆立在地图中.(截图帧数问题,刚好每次敌人都没动)
由于我们没有刷新敌人的状态,敌人是不会动的.
跟新update方法
@H_403_30@void GameLayer::update(float dt) { this->updateHero(dt); this->updateEnemies(dt); }结果B
他们开始乱跑和执行攻击动画了
ok,我们已经达到了想要的效果
结论
本片实现了添加多个敌人,并给敌人设定简单的AI让其何以自动随机巡逻或者追击或是攻击Hero. 但是目前只有各种动画,没有实现攻击判定. 下一章我们通过攻击判定,让Hero或者是Enemy受伤,生命到底还会死亡.具体的属性比如生命值,攻击力之类的.参阅上面的代码