Game State Management in LUA

Does anyone have any advice on game states with LUA? I’m completely new to this non-OOP language and I’m looking for general advice. I’ve found several libraries but at this point I’m not knowledgeable enough to understand what I need in order to make an informed decision on any existing libraries or starting points.

My project scope is simple. It’s really only going to have a couple main states, such as Playing, Paused, Game Over.

I’m not currently aware of anything, though I am hoping our developers will fill in the blanks on this in the near future. (I have a custom library I developed for this, but it’s rough enough that I’m not sure sharing it would be of much use.)

1 Like

Thanks for explaining!

I use classic.lua to get OOP-like behaviour in Lua. I still use it in our game but it requires some awkward importing with Playdate SDK. The official SDK OOP is almost as good, and I created a thread about adding Mixin support if you miss that.

And I’ve been trying out https://github.com/kyleconroy/lua-state-machine/blob/master/README.md but I’ve been putting off making a decision.

1 Like

From your description it seems your states will be simple so my advice would be to keep it simple too in the implementation.

Simply have a variable that contain the current state
gameState = "gameplay"

and in your update function simply branch out depending on the variable

if gameState=="gameplay" then
	gameplayUpdate()
elseif gameState=="pause" then
	pauseUpdate()
elseif gameState=="gameover" then
	gameoverUpdate()
end

It’s not fancy but you shouldn’t be if you don’t need to.
This is what I used for a long time and it works fine. When I needed more controls I used a mode manager which handle my states/screen with a stack system.

4 Likes

I use Playdate’s object system and whenever I need a state machine, I write something like this in the init function:

-- Game States
self.states = {
    gameplay = self.gameplayState,
    pause = self.pauseState,
    gameover = self.gameoverState
}
-- Starting Game State
self.state = self.states.gameplay

Then my update looks like:

function Game:update()
	-- Perform State
	self.state(self)
end

and then I go and make function Game:gameplayState(), function Game:pauseState(), etc. in the object to hold each states’ code. If I want to change states, I just write something like self.state = self.states.pause.

8 Likes

[ i'm aware this is an ancient tome of a thread. however, i found it in my search for the truth so maybe others will find my two cents helpful, even if OP has long-solved their original question ]

I manage states very simply, but it works.

This is my state structure, with some added convenience.

In reality my states are just 1, 2, 3, 4, ...
But by defining the global ints like EXPLORE = 1, I can easily reference the states elsewhere in code without having to recall the assigned number.

EXPLORE = 1
DIALOGUE = 2
COMBAT = 3
PAUSE = 4
QUIT = 5

gameState = {
  EXPLORE,
  DIALOGUE,
  COMBAT,
  PAUSE,
  QUIT
}

function gameStateToString(worldState)
  if worldState == EXPLORE then return "explore"
  elseif worldState == DIALOGUE then return "dialogue"
  elseif worldState == COMBAT then return "combat"
  elseif worldState == PAUSE then return "pause"
  elseif worldState == QUIT then return "quit"
  else return "unknown" end
end

Example of use from my main update loop

-- input handler switching
if reportState then print("gameState = "..gameStateToString(worldState)); reportState=false; end
  
if worldState == gameState[EXPLORE] then inputExplore()
elseif worldState == gameState[DIALOGUE] then inputDialogue()
else print("Unkown game state.") end

ps - didn't want the state printing every frame so elsewhere in my code, where states are changed, i run reportState = true . state is reported once per change, it's nice feedback to have.

Oh, fun. I made my own state class too. Bear in mind I'm not that familir with lua so this may be all off.

A game state looks like:

stateSomeGameState = {
  init = function()
  end,
  shutdown = function()
  end,
  sequence = function()
  end,
  draw = function()
  end,
  input = {
    AButtonDown = function()
    end
  },
  nextState = nil
}

And then I have a state management class that keeps a table of all currently active states. When a state is added, its input table is pushed to playdate's input handler stack and its sequence is invoked as a coroutine, allowing me to add states that just Do A Thing and then exit, like a cut scene for instance. The states are drawn in the order they were added. It's not truly a stack in the same way as the input is - it probably should be though because when a state's sequence ends, its shutdown is called and then its input is popped from the stack. That assumes the states live and die in stack order, so that needs fixing. Its nextState is then added to the state machine and off we trot.

I also have a global bool that's set to false every time the draw routine is run (once per frame), and states can just set that to true if they want to be redrawn that frame. That way if nothing changes on the screen then states can elect not to redraw. That could probably be improved to only cover parts of the screen...

It's not working out too badly for me so far.

It's already biting me in the rear end!

im a little unclear on how coroutines work. are you using them to run states in parallel? in that case what's an example of two states you'd want to run as coroutines?

For instance, my save game routine runs as a separate state, and counts up to 900 before autosaving. That way I don't pollute my main game state with save game code, and I can just add the save game state into my running states when the game begins and remove it when it ends.

I could also have a pause menu that I simply add to the running states when the running game state handles a button press. Its init() function would pause the game, its shutdown() would unpause it again, and it would have its own input handler that would handle all inputs (to prevent the game state from receiving input while the menu was open). The sequence coroutine might not even need to be implemented for a menu, but could be used to handle animations and such.