Last active
December 16, 2015 05:29
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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