接上一節內容: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); } }
附上實現后的效果圖:
1、這是自動朝着玩家走
2、這是在靠近玩家后進行攻擊