This tutorial is for all of those LUA game makers who would like to clean up their code and stop using complex if-loops to control different parts of their game.
Think of a state like a mode, as you go throughout the game, you switch modes; for example, you start at the menu, then go to the game, you might pause, and then you lose, and want to go back to the menu, or maybe start over again. That'd be quite confusing to keep track of, wouldn't it? State system, please enter into the spotlight :Punk: .
Note that since we're in Lua, this isn't going to be as "clean" as it would be in either C or C++, but it still helps quite a bit :).
The first things you're going to need are variables to keep track of your current state and your last state.
Moving on from this, there are 4 main things you want to do in each state. These are:Code:-- statesystem.lua oldState = "none" currentState = "none"
- Setup your state
- Draw your state
- Get your state's input
- Shutdown your state
Now that we have this information, we can create a file for our new state with each of these functions. In my game, I have a separate Lua file for each state, with one of each of these functions. For example, GameSetup(), GameDraw(), GameDoInput(), GameShutdown(); MenuSetup(), MenuDraw(), MenuDoInput(), MenuShutdown().
Now it would only make sense to have some functions to take care of this in your main loop, wouldn't it? Enter two of our three state system functions:
In this example we'll be pretending we have 2 different states, the menu state, and the game state.
As you can see, these functions simply check the current state and call the appropriate functions for drawing and input.Code:-- statesystem.lua oldState = "none" currentState = "none" function StateDrawCurrentState() if currentState == "Game" then GameDraw() elseif currentState == "Menu" then MenuDraw() end end function StateGetCurrentStateInput() if currentState == "Game" then GameDoInput() elseif currentState == "Menu" then MenuDoInput() end end
The last state system function, and the most useful, is the function used to switch states. Please direct your attention below:
Simple enough, isn't it? What this function does is first check to make sure we're not trying to switch to a state that's already being run, for obvious reasons :P. Afterwards, we set the currentState to the old state, call the current state's shutdown function (this can be used for clearing the screen or other various purposes), then make our current state the state we specified. Finally, we call the setup function for the new state.Code:-- statesystem.lua oldState = "none" currentState = "none" function stateDrawCurrentState() if currentState == "Game" then GameDraw() elseif currentState == "Menu" then MenuDraw() end end function stateGetCurrentStateInput() if currentState == "Game" then GameDoInput() elseif currentState == "Menu" then MenuDoInput() end end function switchState(state) if currentState ~= state then oldState = currentState if oldState == "Game" then GameShutdown() elseif oldState == "Menu" then MenuShutdown() end currentState = state if currentState == "Game" then GameSetup() elseif currentState == "Menu" then MenuSetup() end end end
You can of course, enhance this to have a list of all of your states, and check to see if the state being passed in is valid, to prevent something nasty happening if you pass in a state that doesn't exist. What you will also want to do is have a variable in each of your states that checks to see if your state has already been initialized before initializing it again and possibly wiping out some of your current game data (for example, if you went from game to pause to game again, you could run the risk of losing all of your current data from pause back to game).
Moving on, now that we have our state systems all setup, we can go to our main loop.
Here we first include our states and our state system, then switch to our initial state, and let our extremely simple loop do the rest :). Makes it a heck of a lot easier to read with a simple main script and all of your code modularized, doesn't it? Finally, for example I'll show what my menu state file looks like:Code:-- script.lua cexit = "false" dofile("gamestate.lua") dofile("menustate.lua") dofile("statesystem.lua") switchState("Menu") while cexit ~= "true" do stateDrawCurrentState() stateGetCurrentStateInput() screen.waitVblankStart() end
You'll notice that each of the four state functions are here, and everything is taken care of in its own loop. Nifty, eh?Code:-- menustate.lua menuAlreadyInit = "false" function MenuSetup() if menuAlreadyInit == "false" then MenuImage = Image.load("./Data/Textures/UI/Menu/menu.png") end doFlip = "false" MenuPosition = 0 end function MenuDraw() screen:blit(0,0,MenuImage,false) if doFlip == "false" then screen.flip() doFlip = "true" end end function MenuDoInput() pad = Controls.read() if pad:circle() then screen:save("./Screenshots/screenshot.jpeg") end if pad:triangle() then cexit = "true" end if pad:cross() then switchState("Game") end end function MenuShutdown() screen:clear() end
Anyways, I hope you all get some use out of this. I know it has certainly helped me in several of my projects, in several languages :). Questions, comments, flames, or burritos? Leave them below and let me know what you think :dj:.