加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Cocos3.4 横版游戏制作-《KillBear》-添加敌人+简单AI实现

发布时间:2020-12-14 20:20:17 所属栏目:百科 来源:网络整理
导读:转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。 资源为网上寻找的,仅研究学习用,若是侵犯版权请通知本人整改。 上一篇: Cocos3.4 横版游戏制作-《KillBear》-添加血条 攻击按键 上篇在状态层加入了血条,并添加了一个攻击按键 本篇将在前面

转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。
资源为网上寻找的,仅研究学习用,若是侵犯版权请通知本人整改。


上一篇: Cocos3.4 横版游戏制作-《KillBear》-添加血条 攻击按键
上篇在状态层加入了血条,并添加了一个攻击按键
本篇将在前面的基础上添加敌人,并通过有限状态机(FSM)实现简单的AI


开发环境

win64 : vs2010
Cocos2d-x v3.4Final
TexturePackerGUI
MapEdit


代码A

角色Role

Enemy

创建一个继承基础Role类,作为敌人.

  • .h
typedef enum {
    AI_IDLE = 0,AI_PATROL,AI_ATTACK,AI_PURSUIT
}AiState;

作为AI类型

其他的代码和Hero大同小异

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类似

Animation *idleAnim = this->createNomalAnimation("bear_idle_%02d.png",3,6);
        this->setIdleAction(RepeatForever::create(Animate::create(idleAnim)));
        ...

然后是类似Hero的自更新函数updateSelf刷新自己状态

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的方向执行动画等.

void Enemy::execute(const Vec2& target,float targetBodyWidth)
{
    if(m_nextDecisionTime == 0)//lazy延时到0执行下一个动作判定
    {
        this->decide(target,targetBodyWidth);
    }else {
        -- m_nextDecisionTime;
    }
}

接下来通过FSM设定敌人的AI

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的判断,里面用到几个宏是随机一个数,更真实的表现敌人.

  • Attack,攻击,每次攻击延时50
  • Idle,发呆,延时随机一个0-1的数字*100
  • Patrol,巡逻,延时时间也似随机出来的
  • Pursuit,追击,当发现Hero追击的判断

主要的AI是:

  • 目标出现在正前方?(根据视野范围)发呆or巡逻
  • 是否在攻击范围内?(根据攻击范围)追击or攻击


这个图片更好理解下


接下来在GameLayer加入他们
我们每次加入多个敌人,方便起见需要使用数组来实现它(链表更好)

Game

GameLayer

  • .h
#include "Enemy.h"
    ...

    void addEnemies(int number);
    void updateEnemies(float dt);
    __Array *m_pEnemies;

实现addEnemies,一次加入多个敌人通过这个数组创建.最后别忘了将这个数组注册到Global,下一章做攻击判断用

  • .cpp
void GameLayer::addEnemies(int number)
{
    m_pEnemies = __Array::createWithCapacity(number);
    m_pEnemies->retain();
    for(int i=0;i<number;i++)
    {
        Enemy *pEnemy = Enemy::create();
        pEnemy->setPosition(Vec2(   random(_visibleSize.width/2,_visibleSize.width),70  ));
        pEnemy->runIdleAction();
        pEnemy->setLocalZOrder(_visibleSize.height - pEnemy->getPositionY());

        //属性设置
        pEnemy->setVelocity(Vec2(0.5f,0.5f));
        pEnemy->setEyeArea(300);
        pEnemy->setAttackArea(80);
        pEnemy->setDamageStrenth(5);
        pEnemy->setSumLifeValue(100);
        pEnemy->setCurtLifeValue(m_pHero->getSumLifeValue());

        m_pEnemies->addObject(pEnemy);
        this->addChild(pEnemy,0);
    }
    global->enemies= m_pEnemies;
}

init中

this->addEnemies(5);

结果图A

我们看到了几个敌人呆立在地图中.(截图帧数问题,刚好每次敌人都没动)

由于我们没有刷新敌人的状态,敌人是不会动的.

继续添加刷新敌人的代码

void GameLayer::updateEnemies(float dt)
{
    Ref *Obj = NULL;
    Vec2 distance = Vec2::ZERO;
    CCARRAY_FOREACH(m_pEnemies,Obj)//遍历所有的怪物
    {
        Enemy *pEnemy = (Enemy*)Obj;
        pEnemy->updateSelf();//自更新状态

        //如果死了就移除他们
        if(pEnemy->getDeadAction()->isDone())
            m_pEnemies->removeObject(pEnemy);
    }
}

跟新update方法

void GameLayer::update(float dt)
{
    this->updateHero(dt);
    this->updateEnemies(dt);
}

结果B

他们开始乱跑和执行攻击动画了

ok,我们已经达到了想要的效果

结论

本片实现了添加多个敌人,并给敌人设定简单的AI让其何以自动随机巡逻或者追击或是攻击Hero. 但是目前只有各种动画,没有实现攻击判定. 下一章我们通过攻击判定,让Hero或者是Enemy受伤,生命到底还会死亡.具体的属性比如生命值,攻击力之类的.参阅上面的代码

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读