Finite State Machine
The finite state machine is nothing like the creature AI in Black and White. A FSM is an architecture or a mindset that allows you to break down an agents behavior into different states. An agent is an NPC or a monster. Each of these states are usually mutually exclusive, which means an agent is only in one state at a time. For example if you have the states Attacking, Fleeing and Searching then the agent would only be doing one of those things at a time. Thinking about creating any one of these states should make you realize that it seems a lot more manageable than trying to just throw together a script that does all that at once. This is why this is so commonly used, it's a simple way of breaking down code into manageable chunks.
Example of a simple state machine
So when do we change from one state to another? Each state has conditions that when fulfilled prompts a change to a different state. In the example you could change from Attacking to Fleeing if the agents hp drops below 10% then start to flee, and if you lose track of where the player is you switch to Searching. The simplest way of doing a transition is to pass in the state manager into as a parameter in the update function and then just do the tests and make the transition.
Implementation
Please note that the following source code is not the best way of implementing a FSM, the code is designed to be as compact and as intuitive as possible.
StateManager.h
The state manager is the class that collects all the different states and keeps track which one is active. All states components of this class.
#ifndef STATEMANAGER_H#define STATEMANAGER_H
enum{
STATE_SEARCHING,
STATE_ATTACKING,
STATE_FLEEING,
STATE_COUNT
};
class State;
class StateManager
{
private:
State* states[STATE_COUNT];
int current_state;
public:
StateManager();
~StateManager();
void ChangeState(int state_id);
void Update();
};
#endif // STATEMANAGER_H
StateManager.cpp
#include "State.h"
#include "StateBehaviours.h"
StateManager::StateManager()
{
states[STATE_ATTACKING] = new StateAttacking();
states[STATE_SEARCHING] = new StateSearching();
states[STATE_FLEEING] = new StateFleeing();
}
StateManager::~StateManager() {}
void StateManager::ChangeState(int state_id)
{ if(state_id < STATE_COUNT) current_state = state_id; }
void StateManager::Update()
{ states[current_state]->Update(this); }
State.h
This is the abstract base class for all states, it defines the protocol by which the state manager communicates to each state. Add things here if you want the state manager to be able to do more with each state.
#ifndef STATE_H#define STATE_H
class StateManager;
class State
{
public:
virtual ~State() {};
virtual void Update(StateManager* state_manager) = 0;
};
#endif // STATE_H
StateBehaviors.h
Instead of creating 3 different headers for the 3 different classes just for the sake of compacting the code down a bit I put them all in one file. We make sure that all states here implement the update function defined in "State".
#ifndef STATEBEHAVIORS_H#define STATEBEHAVIORS_H
#include "State.h"
class StateAttacking : public State
{
public:
void Update(StateManager* state_manager);
};
class StateSearching : public State
{
public:
void Update(StateManager* state_manager);
};
class StateFleeing : public State
{
public:
void Update(StateManager* state_manager);
};
#endif // STATEBEHAVIORS_H
StateBehaviors.cpp
This is where you add all your behavior, we have minimal compile dependencies so any change here will only affect this .cpp file. How you get access to the player class you'll have to figure out on your own, there are many ways of doing it and it's up to you to figure out which ones work best for your specific problem.
#include "StateBehaviours.h"#include "StateManager.h"
// ATTACKING --------------------------------------------
void StateAttacking::Update(StateManager* state_manager) {
if(hp < 10)
state_manager->ChangeState(STATE_FLEEING);
}
// SEARCHING --------------------------------------------
void StateSearching::Update(StateManager* state_manager) {}
// FLEEING ----------------------------------------------
void StateFleeing::Update(StateManager* state_manager) {}
No comments:
Post a Comment