Search…

Trí Tuệ Nhân Tạo Trong Games - Phần 2: Thiết Kế AI Với FSM

28/09/20204 min read
Hướng dẫn thiết kế AI với FSM sử dụng framework Cocos2d-x.

Nâng cấp AI với FSM từ bài viết trước để:

  • Dễ sửa chữa, bảo trì, thêm trạng thái.
  • Giảm thời gian xử lý.

Thiết kế State

Class State

Header:

// State.h  
//  
#ifndef __State_h__
#define __State_h__
  
class Monkey;  
  
template <class entity_type>

class State {
public:
    virtual ~State() {}

    //hàm này sẽ được thực hiện khi state được nhập
    virtual void Enter(entity_type*) = 0;

    //đây là hàm update của state
    virtual void Execcute(entity_type*) = 0;

    //hàm này sẽ thực hiện khi state đã thoát để chuyển state   
    virtual void Exit(entity_type*) = 0;
};  
  
#endif // __State_h__

Các trạng thái của Monkey bao gồm stop, walk, và turn sẽ kế thừa từ class State. Với việc thiết kế mỗi trạng thái của AI là 1 class State, việc bảo trì, sữa chữa, hay thêm 1 trạng thái cho AI sẽ tốt hơn rất nhiều vì:

  • Các đoạn code của trạng thái không liên quan gì tới các đoạn code của trạng thái khác.
  • Dễ dàng tìm đến nơi muốn sửa chữa hay bảo trì.
  • Nếu muốn thêm 1 trạng thái cho AI chỉ việc tạo ra 1 trạng thái mới cho AI được kết thừa từ class State.

Vòng đời của 1 trạng thái của AI là:

  • Enter.
  • Execcute.
  • Exit.

Monkey states

Header

#ifndef __Monkey_State_h__
#define __Monkey_State_h__

#include "State.h"

class Monkey

class Stop: public State<Monkey> {
private:
    Stop(){}
  
    Stop(const Stop&);
    Stop& operator=(const Stop&);

protected: 
public:
    static EnterMineAndDigForNugget* Instance();
  
    virtual void Enter(Monkey* _monkey);
    virtual void Execute(Monkey* _monkey);
    virtual void Exit(Monkey* _monkey);
};

class Walk: public State<Monkey> {
private:
    Walk(){}

    Walk(const Walk&);
    Walk& operator=(const Walk&);
 
protected:
public:
    static Walk* Instance();

    virtual void Enter(Monkey* _monkey);
    virtual void Execute(Monkey* _monkey);
    virtual void Exit(Monkey* _monkey);
};

class Turn: public State<Monkey> {
private:
    Turn(){}

    Turn(const Turn&);
    Turn& operator=(const Turn&);
 
protected:
public:
    static Turn* Instance();

    virtual void Enter(Monkey* _monkey);
    virtual void Execute(Monkey* _monkey);
    virtual void Exit(Monkey* _monkey);
};

#endif // __Monkey_State_h__

Source

#include "MonkeyState.h"
#include "State.h"
#include "Monkey.h"

//********************Stop*****************************

Stop* Stop::Instance() {

    static Stop instance;

    return &instance;
}

void Stop::Enter(Monkey* _monkey) {}

void Stop::Execute(Monkey* _monkey) {  

    if (_monkey->isStopTimeout())
        _monkey->GetFSM()->ChangeState(stWALK::Instance());
}

void Stop::Exit(Monkey* _monkey) {}

//*****************************************************

//*******************Walk******************************

Walk* Walk::Instance() {

    static Walk instance;

    return &instance;
}

void Walk::Enter(Monkey* _monkey) {}

void Walk::Execute(Monkey* _monkey) {

    pMiner->walk();  
    
    if (_monkey->isWalkOutBorder())
        _monkey->GetFSM()->ChangeState(Turn::Instance());      

    else if (_monkey->isWalkTimeout())
        _monkey->GetFSM()->ChangeState(Stop::Instance());      
}

void Walk::Exit(Monkey* _monkey) {}

//*****************************************************

//*********************Turn****************************

Turn* Turn::Instance() {

    static Turn instance;

    return &instance;
}

void Turn::Enter(Monkey* _monkey) {}

void Turn::Execute(Monkey* _monkey) { 

    _monkey->turn();
    _monkey->GetFSM()->ReVertToPriviousState();
}

void Turn::Exit(Monkey* _monkey) {}

//*****************************************************

State Machine

Bây giờ cần 1 FSM có nhiệm vụ chạy trạng thái hiện tại và chuyển trạng thái của AI.

Header

#ifndef __State_Machine_h__
#define __State_Machine_h__

#include "State.h"

template <class entity_type>

class StateMachine {
public:
    StateMachine(entity_type* m_Owner):m_pOwner(m_Owner),
                                   m_pCurrentState(NULL),
                                   m_pPreviousState(NULL),
    {}

    virtual ~StateMachine(){}

    void SetCurrentState(State<entity_type>* _state){m_pCurrentState = _state;}
    void SetPreviousState(State<entity_type>* _state){m_pPreviousState = _state;}
  
    void  Update() const {
        m_pCurrentState->Execute(m_pOwner);
    }

    void  ChangeState(State<entity_type>* _newState) {
    
        m_pPreviousState = m_pCurrentState;

        m_pCurrentState->Exit(m_pOwner);
        m_pCurrentState = _newState;
        m_pCurrentState->Enter(m_pOwner);
    }

    void RevertToPreviousState() {
        ChangeState(m_pPreviousState);
    }

    State<entity_type>*  CurrentState()  const{return m_pCurrentState;}
    State<entity_type>*  PreviousState() const{return m_pPreviousState;}
protected:
private:
    entity_type* m_pOwner;
    
    State<entity_type>* m_pCurrentState;
    State<entity_type>* m_pPreviousState; 
};

#endif // __State_Machine_h__

Với class StateMachine ở trên chỉ cần thật sự chú ý những điều sau:

  • Trạng thái hiện tại của AI là m_pCurrentState
  • Trạng thái trước đó của AI là m_pPreviosState

Monkey

Header

#ifndef __Monkey_h__
#define __Monkey_h__
  
#include <time.h>  
#include "StateMachine.h"
#include "State.h"
#include "MonkeyState.h"
  
#include "cocos2d.h"  
USING_NS_CC;   
  
#define MAX_STOP_TIME  10  
#define MAX_WALK_TIME  20  
  
#define MAX_WALK_DIST  100  
    
class Monkey : public Node {  
public:
    Monkey()  {  
        log("Monkey()");  
    }  

    ~Monkey() {
        delete m_monkeyFSM;
    }
  
    CREATE_FUNC(Monkey);  
    
    virtual bool init() {  
        _curPos = 0;  
        _step = 1;  
        
        m_monkeyFSM = new StateMachine<Monkey>(this);
        m_monkeyFSM->SetCurrentState(Stop::Instance());
  
        this->scheduleUpdate();  
  
        return true;  
    }  
  
    void stop() {  
        cocos2d::log("stop()");  
    }  
  
    void walk() {  
        _curPos += _step;  
        cocos2d::log("walk(): pos=%d", _curPos);  
    }  
  
    void turn() {  
        _step *= -1;  
        cocos2d::log("turn(): step=%d", _step);          
    }  
  
    void update(float dt) {  
         m_monkeyFSM->update();
    }  
  
 
protected: 
private:  
    StateMachine * m_monkeyFSM;  
  
    time_t _curTime;  
  
    int      _curPos;  
    int      _step;  
    
public:  
    bool isStopTimeout() {  
        return (time(0) - _curTime > MAX_STOP_TIME);  
    }  
   
    bool isWalkTimeout() {  
        return (time(0) - _curTime > MAX_WALK_TIME);  
    }  
  
    bool isWalkOutBorder() {  
        return (_curPos > MAX_WALK_DIST || _curPos < -MAX_WALK_DIST);  
    }  
};  
  
#endif // __Monkey_h__

Trong class Monkey lúc này gồm có:

  • Monkey FSM: Là FSM dùng để chuyển qua lại giữa các trạng thái stop, walk, turn và execcute trạng thái hiện tại của Monkey
  • Các sự kiện và quy luật.

Tham khảo

Bài chung series

IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024