Skip to content

Instantly share code, notes, and snippets.

@steve-sarcinella
Last active December 16, 2015 05:29
Show Gist options
  • Save steve-sarcinella/5384560 to your computer and use it in GitHub Desktop.
Save steve-sarcinella/5384560 to your computer and use it in GitHub Desktop.
This is a Finite State Machine that I'm pretty happy with and use on some of my projects. Mainly used for games but can easily be adapted. It's use is explained below. I don't mind if anyone wants to use this, just remember to type it yourself to learn :D
#ifndef _FSM_H_
#define _FSM_H_
//This is game specific but it's use is just to get the frametime.
#include "time.h"
#define IGNORE_DUPLICATE_STATES 1
namespace GameObjects
{
/*
With this class I wanted to create a Finite State Manager that was tied to a specific object.
Previous to this I was templatizing the state manager but using specialization for each objects different states
I wanted to have each objects functions and transitions contained within their own class definitions.
So I made a generic FSM class that takes in the pointers to those functions and upates and transitions accordingly.
TO MAKE USE OF THIS:
In the class you want an FSM
#include "FSM.H"
Declare/Init an FSM. i.e.
StateManager<MyClass>* mStateManager = new StateManager<MyClass>(this);
Add States:
mStateManager->AddState("StateName", &UpdateFunction1, &EnterFunction1, &ExitFunction1);
Define States (Since they are memeber functions they must follow the typedef defined in the FSM)
Update function takes a float deltaTime as an arg, the enter/exit functions take nothing.
All three return void
void UpdateFunction1(float aDeltaTime)
{
Changing state example
if(mHealth < 3)
{
mStateManager->ChangeState("New State Name");
}//end if
}//end update function
void EnterFunction1()
{
}//end exit function
void ExitFunction1()
{
}//end exit function
*/
template<typename T>
class StateManager
{
//Typedef of member function pointers
typedef void (T::*updateFunctionPointer)(float);
typedef void (T::*transitionFunctionPointer)(void);
public:
//Internal struct for state data to help clean up some of the code
//Each struct contains an enter/exit/update function pointer from a specific object
struct StateData
{
updateFunctionPointer mUpdatePtr;
transitionFunctionPointer mEnterPtr;
transitionFunctionPointer mExitPtr;
std::string mStateName;
};//End state data struct
StateManager(T* aObj);
~StateManager();
//This function takes a state name,
//and update function pointer from the obj's class, an enter function pointer and an exit function pointer
void AddState(std::string aState, updateFunctionPointer aUpdatePtr, transitionFunctionPointer aEnterPtr, transitionFunctionPointer aExitPtr);
bool RemoveState(std::string);
void ClearStates();
void Update(float aDeltaTime);
void ChangeState(std::string aNewStateName);
void NotifyObservers();
void DelayChangeState(std::string, double aTimeAmount);
int HaveState(std::string aStateName); // returns the index of the found state, -1 if not found
//For outer access to the current state, could be locked down with a Get Function
StateData mCurrentState;
private:
std::string mNextStateName;
double mNextStateTimeRemaining;
bool mHaveNextState;
std::vector<StateData> mStates;
T* mObj;
};//end state manager class
template<typename T>
StateManager<T>::StateManager(T* aObj)
{
if(aObj)
{
mObj = aObj;
}//end if
mNextStateName = "";
mNextStateTimeRemaining = 0;
mHaveNextState = false;
}//end template state constructor
template<typename T>
StateManager<T>::~StateManager(){}//end template state destructor
template<typename T>
void StateManager<T>::AddState(std::string aState, updateFunctionPointer aUpdatePtr, transitionFunctionPointer aEnterPtr, transitionFunctionPointer aExitPtr)
{
if(aState.length() > 0 && aUpdatePtr && aEnterPtr && aExitPtr)
{
bool bDuplicateFound = false;
if(!IGNORE_DUPLICATE_STATES)
{
for(int i = 0; i < mStates.size(); ++i)
{
if(mStates.at(i).mStateName == aState)
{
bDuplicateFound = true;
break;
}//end if
}//end for
}//end duplicate check
if(!bDuplicateFound)
{
StateData temp;
temp.mStateName = aState;
temp.mUpdatePtr = aUpdatePtr;
temp.mEnterPtr = aEnterPtr;
temp.mExitPtr = aExitPtr;
mStates.push_back(temp);
if(mCurrentState.mStateName == "")
{
mCurrentState = temp;
(*mObj.*(mCurrentState.mEnterPtr))();
}//end if
}
}//end data check
}//end template add state
template<typename T>
bool StateManager<T>::RemoveState(std::string aStateName)
{
for(int i = 0; i < mStates.size(); ++i)
{
if(mStates.at(i).mStateName == aStateName)
{
mStates.erase(mStates.begin() + i, mStates.begin() + i + 1);
break;
}//end if
}//end for
}//end template remove state
template<typename T>
void StateManager<T>::ClearStates()
{
mStates.erase(mStates.begin(), mStates.end());
}//end template clear states
template<typename T>
void StateManager<T>::Update(float aDeltaTime)
{
if(mHaveNextState)
{
mNextStateTimeRemaining -= aDeltaTime;
if(mNextStateTimeRemaining <= 0)
{
ChangeState(mNextStateName);
mNextStateName = "";
mNextStateTimeRemaining = 0;
mHaveNextState = false;
}//change to the next state
}//end have next state check
(*mObj.*(mCurrentState.mUpdatePtr))(aDeltaTime);
}//end template update
template<typename T>
void StateManager<T>::ChangeState(std::string aNewStateName)
{
//if -1, no state was found
int aNewStateIndex = HaveState(aNewStateName);
if(aNewStateIndex >= 0)
{
//Run on state exit
(*mObj.*(mCurrentState.mExitPtr))();
//Change the current state
mCurrentState = mStates.at(aNewStateIndex);
//run on state enter
(*mObj.*(mCurrentState.mEnterPtr))();
//Notify Observers
NotifyObservers();
}//end if
}//end template change state
template<typename T>
void StateManager<T>::NotifyObservers()
{
mObj->NotifyObservers(mCurrentState.mStateName);
std::cout << mCurrentState.mStateName << std::endl;
}//end template notify observers
template<typename T>
void StateManager<T>::DelayChangeState(std::string aStateName, double aTimeAmount)
{
int nextStateIndex = HaveState(aStateName);
if(nextStateIndex >= 0)
{
mNextStateName = mStates.at(nextStateIndex).mStateName;
mNextStateTimeRemaining = aTimeAmount;
mHaveNextState = true;
}//end if
}//end delay change state
template<typename T>
int StateManager<T>::HaveState(std::string aStateName)
{
for(int i = 0; i < mStates.size(); ++i)
{
if(mStates.at(i).mStateName == aStateName)
{
return i;
}//end if
}//end for
return -1;
}//end have state check
}//end FSM namespace
#endif //_FSM_H_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment