A state machine (or finite state machine, FSM) is a programming design pattern used to model the behavior of a system by breaking it into a set of states, and defining how it transitions from one state to another based on inputs or events.
in this program we want to implement a simple state machine like this, something very common in a game:

SDL_Init(SDL_INIT_VIDEO);
TTF_Init();
SDL_Window *win = SDL_CreateWindow("State Machine SDL3", 800, 600, 0);
SDL_Renderer *renderer = SDL_CreateRenderer(win, NULL);
TTF_Font *font = TTF_OpenFont("FreeSans.ttf", 24);
if (!font) {
SDL_Log("Could not load font: %s", SDL_GetError());
return 1;
}
To implement the state machine we first need to define the states in an enum:
typedef enum {
STATE_MENU,
STATE_PLAYING,
STATE_PAUSED,
STATE_EXIT
} GameState;
And then we define the function for each states:
For the menu state:
void handle_menu(SDL_Event *e, GameState *state) {
if (e->type == SDL_EVENT_KEY_DOWN && e->key.key == SDLK_RETURN) {
*state = STATE_PLAYING;
}
}
For the playing state:
void handle_playing(SDL_Event *e, GameState *state) {
if (e->type == SDL_EVENT_KEY_DOWN) {
if (e->key.key == SDLK_P) {
*state = STATE_PAUSED;
} else if (e->key.key == SDLK_ESCAPE) {
*state = STATE_EXIT;
}
}
}
For the pause state:
void handle_paused(SDL_Event *e, GameState *state) {
if (e->type == SDL_EVENT_KEY_DOWN && e->key.key == SDLK_R) {
*state = STATE_PLAYING;
}
}
We then need a function to render the text of the states :
void render_text(SDL_Renderer *renderer, TTF_Font *font, std::string text, float x, float y) {
SDL_Color white = {255, 255, 255, 255};
SDL_Surface *surface = TTF_RenderText_Blended(font, text.c_str(), text.size(), white);
if (!surface) return;
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FRect dst = {x, y, (float)surface->w, (float)surface->h};
SDL_RenderTexture(renderer, texture, NULL, &dst);
SDL_DestroyTexture(texture);
SDL_DestroySurface(surface);
}
The event loop is pretty simple we need to call the state handling functions accrding to the state:
SDL_Event e;
while (SDL_PollEvent(&e)) {
if (e.type == SDL_EVENT_QUIT) {
state = STATE_EXIT;
}
switch (state) {
case STATE_MENU: handle_menu(&e, &state); break;
case STATE_PLAYING: handle_playing(&e, &state); break;
case STATE_PAUSED: handle_paused(&e, &state); break;
default: break;
}
}
The rendering part of the program is tto call the render_text function with the right text to display:
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
switch (state) {
case STATE_MENU:
render_text(renderer, font, "Press Enter to Start", 50, 50);
break;
case STATE_PLAYING:
render_text(renderer, font, "Playing... Press P to Pause, Esc to Quit", 50, 50);
break;
case STATE_PAUSED:
render_text(renderer, font, "Paused... Press R to Resume, Esc to Quit", 50, 50);
break;
default: break;
}
We need to desallocate the objects:
TTF_CloseFont(font);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(win);
TTF_Quit();
SDL_Quit();
Need another OS ? => Windows, Mac, Linux