cocos2dx - 生成怪物及AI


接上一節內容: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、這是在靠近玩家后進行攻擊

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM