cocos2dx

接上一节内容:cocos2dx - tmx地图分层移动处理

 本节怪物及简单AI实现

一、怪物

  cocos2dx - v2.3.3编辑器骨骼动画 里创建的CPlalyer一样,新建一个CMonster类,也可以提出一个公共基类IEntity,为了以后扩展其他类型的实体做准备。

这里怪物还要有一个AI的实体类CAIModule类。

相对玩家主要添加以下:

//init方法中添加如下

        m_pAIModule = CAIModule::create();
        this->addChild(m_pAIModule);

同时对移动和攻击进行修改

void CMonster::Move(float delta)
{
    if (isFloatZero(delta))
    {
        PlayAction(enAction::ACT_DEFAULT);
        if (m_pAIModule)
        {
            // 移动结束回调
            m_pAIModule->OnActionCallback();
        }
    }
    else
    {
        float fspeed = 4; // px/fps
        float dx = delta;
        if (isFartherThan(dx, fspeed))
        {
            dx = dx > 0 ? fspeed : -fspeed;
        }
        setPositionX(getPositionX() + dx);
        m_deltaX = delta - dx;
        PlayAction(ACT_RUN);
        // 设置当前的方向
        setScaleX(delta >0? -1 : 1);
    }
}
// 攻击结束动作处理
      m_pAction->play("attack", false);
std::function<void()> func = [this](){
    m_pAction->play("default", true);
      m_nActType = ACT_DEFAULT;
      if (m_pAIModule)
      {
          // 攻击结束回调
           m_pAIModule->OnActionCallback();
       }
      };
       m_pAction->setLastFrameCallFunc(func);  

 以上主要在移动和攻击结束的时候调用了AI模块的动作回调

二、AI模块

     本节只是实现最简单的AI机制,直接将策略写在了update中,在间隔一个THINKTIME时间后判断当前最优行动,并执行动作。

如下:

void CAIModule::update(float dt)
{
    m_nThinkTime += int(dt*1000);
    if (m_nThinkTime>THINKTIME)
    {
        if (CMonster* pMonster = dynamic_cast<CMonster*>(getParent()))
        {
            m_stepFunc = nullptr;
            if (CPlayer* pPlayer = CBattleMgr::getInstance()->GetPlayer())
            {
                if (m_pTarget)
                {
                    m_pTarget->release();
                    m_pTarget = nullptr;
                }
                m_pTarget = pPlayer;
                m_pTarget->retain();

                m_nStep = 0;
                m_nThinkTime = 0;
                float dis = pMonster->getPosition().distance(pPlayer->getPosition());
                if (dis <200)
                {
                    m_stepFunc = std::bind(&CAIModule::Attack, this, std::placeholders::_1, std::placeholders::_2);
                    Attack(pPlayer, m_nStep);
                    //attack
                }
                else
                {
                    m_stepFunc = std::bind(&CAIModule::Move2Node, this, std::placeholders::_1, std::placeholders::_2);
                    Move2Node(pPlayer, m_nStep);
                    //move to pos ;
                }
            }
        }
    }
}

以上直接设定AI执行对象为玩家,在距离小于200执行攻击,否则向玩家节点靠近移动。同时清楚当前 m_nThinkTime时间,在下一个决策时间到来之前怪物会根据动作步骤持续执行当前动作。

所以这里也需要设置一下动作的当前m_nStep为0,同时设置一下执行的函数m_stepFunc

为了能够在怪物攻击或者移动结束后m_nStep执行到下一步,实现了一个OnActionCallback函数。如下:

void CAIModule::OnActionCallback()
{
    if (m_stepFunc)
    {
        ++m_nStep;
        m_stepFunc(m_pTarget, m_nStep);
    }
}

这样即可实现简单的AI移动效果,让拥有该AI对象的怪物,不断靠近玩家并且攻击玩家。
CAIModule全代码如下:

#ifndef __CAIModule_H__
#define __CAIModule_H__
#include "IGameDef.h"
#include "cocos2d.h"
USING_NS_CC;
class CAIModule : public Node
{
public:
    // implement the "static create()" method manually
    CREATE_FUNC(CAIModule);

    virtual bool init();

    void update(float dt);
    
    void OnActionCallback();
private:
    
    void Attack(Node* pEnemy, int step);

    void Move2Node(Node* pNode,int step);

    CAIModule();
    ~CAIModule();


    int  m_nStep;            // 当前操作的步骤数
    float m_nThinkTime;        // 操作的间隔事件

    Node*        m_pTarget;

    std::function<void(Node* node, int step)>   m_stepFunc;
};


#endif __CAIModule_H__

#include "AIModule.h"
#include "Monster.h"
#include "BattleMgr.h"
#define THINKTIME (1000) // 决策时间
CAIModule::CAIModule() :m_nThinkTime(THINKTIME), m_pTarget(nullptr), m_stepFunc(nullptr), m_nStep(0)
{
}


CAIModule::~CAIModule()
{
    if (m_pTarget)
    {
        m_pTarget->release();
        m_pTarget = nullptr;
    }
}

bool CAIModule::init()
{
    scheduleUpdate();
    return true;
}

void CAIModule::update(float dt)
{
    m_nThinkTime += int(dt*1000);
    if (m_nThinkTime>THINKTIME)
    {
        if (CMonster* pMonster = dynamic_cast<CMonster*>(getParent()))
        {
            m_stepFunc = nullptr;
            if (CPlayer* pPlayer = CBattleMgr::getInstance()->GetPlayer())
            {
                if (m_pTarget)
                {
                    m_pTarget->release();
                    m_pTarget = nullptr;
                }
                m_pTarget = pPlayer;
                m_pTarget->retain();

                m_nStep = 0;
                m_nThinkTime = 0;
                float dis = pMonster->getPosition().distance(pPlayer->getPosition());
                if (dis <200)
                {
                    m_stepFunc = std::bind(&CAIModule::Attack, this, std::placeholders::_1, std::placeholders::_2);
                    Attack(pPlayer, m_nStep);
                    //attack
                }
                else
                {
                    m_stepFunc = std::bind(&CAIModule::Move2Node, this, std::placeholders::_1, std::placeholders::_2);
                    Move2Node(pPlayer, m_nStep);
                    //move to pos ;
                }
            }
        }
    }
}

void CAIModule::Attack(Node* pEnemy, int step)
{
    CMonster* pMonster = dynamic_cast<CMonster*>(getParent());
    if (!pMonster)
    {
        return;
    }
     
    switch (step)
    {
    case 0:
    { 
        if (!pEnemy)
        {
            return;
        }
        // 立即攻击
        if (isFartherThan(pMonster->getPositionX() < pEnemy->getPositionX(), 30))
        {
            Attack(pEnemy,1);
        }
        else
        {
            //  太近,移动远一点
            m_nStep =1;
            Move2Node(pEnemy,0);
        }
    }
        break;
    case 1:
    {
              if (!pEnemy)
              {
                  return;
              }
              // 调整方向准备攻击 
              pMonster->setScaleX(pMonster->getPositionX() < pEnemy->getPositionX() ? -1 : 1);
              ++m_nStep;
              std::function<void()> func = [this]()
              {
                  Attack(nullptr,2);
              };
              this->runAction(Sequence::create(DelayTime::create(0.3), CallFunc::create(func), NULL));
    }
        break;
    case 2:
    {
         // 直接攻击
         pMonster->Attack();
    }
        break;
    default:
    //    m_nThinkTime = THINKTIME;
        break;
    }
}

void CAIModule::Move2Node(Node* pNode, int step)
{
    if (!pNode)
    {
        return;
    }
    CMonster* pMonster = dynamic_cast<CMonster*>(getParent());
    if (!pMonster)
    {
        return;
    }
    
    switch (step)
    {
    case 0:
        {
            if (pMonster->getPositionX() < pNode->getPositionX())
            {
                pMonster->Move(pNode->getPositionX() - 50 - pMonster->getPositionX());
            }
            else
            {
                pMonster->Move(pNode->getPositionX() + 50 - pMonster->getPositionX());
            }
        }
        break;
    default:
    //    m_nThinkTime = THINKTIME;
        break;
    }
}


void CAIModule::OnActionCallback()
{
    if (m_stepFunc)
    {
        ++m_nStep;
        m_stepFunc(m_pTarget, m_nStep);
    }
}
View Code


附上实现后的效果图:

1、这是自动朝着玩家走

  

2、这是在靠近玩家后进行攻击

  

原文地址:https://www.cnblogs.com/stratrail/p/5057745.html