Skip to content

Instantly share code, notes, and snippets.

@stuckie
Last active October 19, 2015 21:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stuckie/f83dcfe8b1a7905d88e4 to your computer and use it in GitHub Desktop.
Save stuckie/f83dcfe8b1a7905d88e4 to your computer and use it in GitHub Desktop.
Simple C State Machine
/*
Stuckie's Silly Little State Example .. let's call it SLate for a giggle.
Coded in C for the hell of it, and it's more interesting than in C++ which gives us the use of Classes and virtual functions.
*/
#include <stdio.h>
#include <stdlib.h>
/* Some globals for this little example. */
int gLoopCounter = 0;
int gUpdateState = 1;
/*
All important typedef for our state function.
We'll return an int ( negative numbers designate error values as standard C practise ) and expect a float ( deltaTime. )
You could state this as a generic function pointer as explained here: http://www.safercode.com/blog/2008/11/25/generic-function-pointers-in-c-and-void.html
However, you'll have lost a lot of the "WTF IS GOING ON!?"-ness of the system, therefore we're going for the good 'ol strict typedef function pointer style.
*/
typedef int(*StateFunction)(float);
/* State itself is quite simple, and mimics what you'd essentially do in C++... */
typedef struct State
{
char* name; /* every state gets a unique name to identify it. Could choose an int or something else instead if wanted. */
/* Technically you could have as many, or as few, state phases as you want. We'll have onEnter, update and onExit as defaults. */
StateFunction onEnter;
StateFunction onUpdate;
StateFunction onExit;
} State;
/* We want to effectively only have one state active at any one point, so lets have a global here. */
State* gCurrentState;
/* May be daft to have a "createState" function when really you just need to do it yourself and plunk a few variables in, but it keeps things neat and tidy! */
State* CreateState(char* name, StateFunction onEnter, StateFunction onUpdate, StateFunction onExit)
{
State* newState = malloc(sizeof(State));
newState->name = name; /* obviously you want to behave yourself and do some checking here. */
newState->onEnter = onEnter;
newState->onUpdate = onUpdate;
newState->onExit = onExit;
/*
You may want to do something else here..
I suggest storing all these states in an array or something - hence the name id - so you can pull them back out and switch between them at will.
Whatever you do, this is where you'd do it.
*/
return newState;
}
/* Again, you could just modify and call stuff directly, but we want to keep some sort of sanity in the system to figure out what's going on! */
void ChangeState(State* state)
{
int error = 0; /* Obviously, you'll want to be checking this... */
if (gCurrentState == state) /* do nothing if we're trying to set the same state again. */
return;
if (gCurrentState) {
error = gCurrentState->onExit(0.0F); /* You don't generally give a hoot about deltaTime when exiting a state, same with entering. */
printf("Killing Old State: %s\n", gCurrentState->name);
free(gCurrentState); /* be good and clean up after yourself! */
gCurrentState = 0;
}
if (state) {
gCurrentState = state;
error = gCurrentState->onEnter(0.0F);
}
}
/* With our "State System" out the way - apart from updating, but we'll get to that - we'll define some dummy functions to call. */
int outroOnEnter(float deltaTime)
{
printf("Outro onEnter\n");
return 0;
}
int outroOnUpdate(float deltaTime)
{
printf("We should've effectively cleaned up now and ready to quit, so let's set the global update state to something to force a quit\n");
gUpdateState = 0;
return 0;
}
int outroOnExit(float deltaTime)
{
printf("Outro onExit\n");
return 0;
}
int gameOnEnter(float deltaTime)
{
printf("Game OnEnter\n");
return 0;
}
int gameOnUpdate(float deltaTime)
{
printf("I'S DANCIN!!!\n");
++gLoopCounter;
return 0;
}
int gameOnExit(float deltaTime)
{
printf("Game onExit\n");
return 0;
}
int introOnEnter(float deltaTime)
{
printf("Intro OnEnter\n");
return 0;
}
int introOnUpdate(float deltaTime)
{
printf("Intro Updating Just Once\n");
ChangeState(CreateState("Game", gameOnEnter, gameOnUpdate, gameOnExit));
return 0;
}
int introOnExit(float deltaTime)
{
printf("Intro onExit\n");
return 0;
}
int main(void)
{
State* introState = CreateState("Intro", introOnEnter, introOnUpdate, introOnExit);
State* outroState = CreateState("Outro", outroOnEnter, outroOnUpdate, outroOnExit);
ChangeState(introState);
while (gUpdateState > 0) {
float deltaTime = 0.1333F; /* Calculate this properly! */
gCurrentState->onUpdate(deltaTime);
if (gLoopCounter == 10) /* We've finished our game loop and signified to ourselves to quit */
ChangeState(outroState);
}
if (gCurrentState) {
gCurrentState->onExit(0.0F);
free(gCurrentState);
gCurrentState = 0;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment