Skip to content

Game States Engine

Alekmaul edited this page Jul 4, 2026 · 1 revision

The game states engine is a small, generic finite-state-machine helper bundled with pvsneslib. It lets you split a game into independent "states" (intro, title screen, menu, gameplay, game over, …), each with its own init and update function, and it takes care of calling the right functions at the right time, frame after frame.

what is a game state machine?

It is especially useful for organizing games into clear logical sections such as menus, gameplay, pause screens, and game over states.

State Purpose
Intro Show intro screen
Menu Show menu screen, wait for START
Playing Run game, handle input, render
Paused Freeze game, show overlay message
Game Over Display score, wait for restart

Each state has its own code, logic, etc...

Concepts

A game state is identified by a small integer (an enum value, e.g. INTRO, MENU).

Each state provides exactly two functions:

  • gstaini_<STATE_ID>(void) — called once, when the engine switches into that state.
  • gstaupd_<STATE_ID>(void) — called once per frame, for as long as the state is active.

At any point, your code can call gstaSetState(NEXT_STATE) to tell the engine "stop the current state and switch to NEXT_STATE". The switch actually happens on the next pass of the engine loop: the current state's init/update cycle is abandoned, gstaini_<NEXT_STATE> runs once, and then gstaupd_<NEXT_STATE> starts running every frame.

  • gstaProcess() never returns — it is your game loop. Once you call it (after setting the first state), it loops forever, calling init/update for whichever state is current.

Declaring your states in an include file on game side

Your game must provide a .h file which:

  1. Lists every state in a GSTATESDEF macro, using _GSTATEDEF.
  2. Turns that list into a GSTATES enum.
#ifndef _STA_MAIN_H
#define _STA_MAIN_H

#define GSTATESDEF \
 _GSTATEDEF(INTRO) \
 _GSTATEDEF(TITLE) \
GSTATE_DEF_END

typedef enum {
    GSTATESDEF
    N_GSTATES
} GSTATES;

#endif

Each _GSTATEDEF(NAME) expands to NAME,, so GSTATESDEF produces a comma-separated list of enum values (INTRO, TITLE,).

N_GSTATES is automatically added at the end and gives you the total number of declared states — handy for sanity checks, but not required by the engine.

GSTATE_DEF_END is defined by the engine as nothing; it just closes the macro cleanly (no trailing backslash issues).

To add a new state, add one _GSTATEDEF(YOURSTATE) line here.

Writing a state

Each state lives in its own .c file and implements two functions named after the state:

void gstaini_<STATE_ID>(void);   // called once, when entering the state
void gstaupd_<STATE_ID>(void);   // called every frame, while the state is active

Each state can change VRAM, musics and can have a different VBlank function with nmiSet().

Example: the INTRO state (stateintro.c)

Base on the example you can find in snes-examples/systems/states.

#include <snes.h>
#include "../res/libfontintro.inc"
#include "states.h"

extern u16 pad0;

void gstaini_INTRO(void) {
    consoleInitText(0, 16 * 2, &libfontintro_til, &libfontintro_pal);
    consoleDrawText(7, 10, "Hello Intro State!");
}

void gstaupd_INTRO(void) {
    pad0 = padsCurrent(0);

    if (pad0 & KEY_A) {
        gstaSetState(TITLE);   // leave INTRO, go to TITLE
    }
}

gstaini_INTRO sets up the font and prints a message. It runs exactly once.

gstaupd_INTRO runs every frame: it reads the pad, and as soon as the player presses A it calls gstaSetState(TITLE). From that point on the engine will stop calling gstaupd_INTRO and instead call gstaini_TITLE once, then gstaupd_TITLE every frame.

Rules for game state functions

Names must follow the gstaini_<ID> / gstaupd_<ID> pattern, where <ID> is exactly the identifier used in _GSTATEDEF/_GSTATEINIT (e.g. INTRO, TITLE).

Both functions take no parameters and return void.

Nothing stops a state's update function from calling gstaSetState() for another state (this is how transitions happen), or from never calling it at all (a state that never ends, e.g. a "game over — reset console" screen).

Wiring states into the engine

In your main file, you declare which states exist for this game and boot the engine.

Base on the example you can find in snes-examples/systems/states.

#include <snes.h>
#include "states.h"

// Forward-declare init/update pairs for each state
DECLARE_GSTATE(INTRO);
DECLARE_GSTATE(TITLE);

// need to undef as it is also defined (empty) in the library
#undef GSTATESINIT

// need to declare local states
#define GSTATESINIT \
 _GSTATEINIT(INTRO) \
 _GSTATEINIT(TITLE) \
GSTATE_DEF_END

u16 pad0;   // shared across all states

int main(void) {
    spcBoot();

    consoleSetTextMapPtr(0x6800);
    consoleSetTextGfxPtr(0x3000);
    consoleSetTextOffset(0x0100);

    bgSetGfxPtr(0, 0x2000);
    bgSetMapPtr(0, 0x6800, SC_32x32);

    setMode(BG_MODE1, 0);
    bgSetDisable(1);
    bgSetDisable(2);

    gstaInitEngine();      // register all states declared in STATESINIT

    setScreenOn();

    gstaSetState(INTRO);   // pick the first state to run

    gstaProcess();          // enter the state loop — never returns

    return 0;
}

Step by step:

  1. DECLARE_GSTATE(NAME) — a convenience macro (see gamestates.h engine header) that forward-declares gstaini_NAME and gstaupd_NAME as extern, so states.c can reference functions defined in stateintro.c / statetitle.c.
  2. GSTATESINIT — redefined locally to list, via _GSTATEINIT(NAME), every state that should be registered with the engine. This must mirror the list of states in your .h file (GSTATESDEF).
  3. gstaInitEngine() — expands to GSTATESINIT, i.e. it calls gstaInitFunctions() once per state to register each state's init/update function pointers with the engine.
  4. gstaSetState(INTRO) — selects which state runs first.
  5. gstaProcess() — starts the infinite engine loop. This call does not return; it is the game loop from this point on.

That's all for the game states engine, you can now play with it for your own game!

Clone this wiki locally