I released an OOP class sytem for leadwerks last time this time I'm going to post my StateManager class which I use to control all the gamestates for the game I am working on.
If you don't know what a Statemanager is or what my interpretation of one is I'll run through it quickly.
Generally speaking it's just a stack full of 'states' each state has a list of functions with identical names but different internal functionality, the game will call these functions but ONLY for the active state, say your game has a Draw function but you want a splash screen with a Draw function, you'll separate these into different States for the stack, now when your splash state is active only the splash's Draw function will be called. State stacks almost always run [L]ast n [F]irst [O]ut, so the last state you push onto the stack will be the one the game starts using, so to have a splash screen -> menu -> game order in your stack you would push the game onto the stack first, the menu second and the splash last so that the splash is the first thing the end user sees.
Enough ramblings let me post some code.
requires the class scripts.
class "CStateManager"; function CStateManager:Init() self.States = {} end function CStateManager:Push(state, init) if( ctype(state) == "CState" ) then self.States[#self.States+1] = state; if( init ) then if( self.States[#self.States-1] and self.States[#self.States-1].isinit ) then self.States[#self.States-1]:Shutdown(); self.States[#self.States-1].isinit = false; end self.States[#self.States]:Init(App.context); self.States[#self.States].isinit = true; end else print("StateManager: CStateManager.Push expected CState got: "..ctype(state)); end end function CStateManager:InitCurrentState() if( self.States[1] and not self.States[#self.States].isinit ) then self.States[#self.States]:Init(App.context); self.States[#self.States].isinit = true; end end function CStateManager:Pop() if( self.States[1] ) then if( self.States[#self.States].isinit ) then self.States[#self.States].isinit = false; self.States[#self.States]:Shutdown(); end local oldState = self.States[#self.States]; self.States[#self.States] = nil; self:InitCurrentState(); return oldState; end print("StateManager: Called CStateManager.Pop with empty stack"); end function CStateManager:GetAll() return self.States end function CStateManager:GetActive() if( self.States[1] and self.States[#self.States].isinit ) then return self.States[#self.States]; end print("StateManager: Called CStateManager.GetActive with no running states"); end function CStateManager:Pause(state) if( ctype(state) == "CState" ) then state.paused = true; end end function CStateManager:Resume(state) if( ctype(state) == "CState" ) then state.paused = false; end end function CStateManager:IsPaused(state) if( ctype(state) == "CState" ) then return state.paused; end end function CStateManager:Call(func, ...) if( self.States[1] and self.States[#self.States].isinit and not self.States[#self.States].paused ) then if( self.States[#self.States][func] ) then self.States[#self.States][func](self.States[#self.States], ...); end end end
state.lua
-- Tiny file this one. really just a declaration and a nilfix file. class "CState"; function CState:Init() end function CState:Shutdown() end
Example useage.
-- Our splash screen. SplashScreen = new "CState" function SplashScreen:Init() self.Something = "HELLO"; end function SplashScreen:Think() self.Something = self.Something.."O"; end function SplashScreen:Draw() App.context:DrawText(self.Something, 100, 100); end -- Now something else. Random = new "CState" function Random:Draw() App.context:DrawText("Second State", 100, 200); end
-- Now in our main file to initialize out statemanager and load our states.
function App:Start() StateManager = new "CStateManager"; StateManager:Push(Random); -- Remember this goes before the splash so we see it AFTER the splash StateManager:Push(SplashScreen, true); the true means we want to initialize this, as it's the last state being pushed we may aswell tell the statemanager we are ready to begin. end function App:Loop() StateManager:Call("Think") -- Can name your functions anything, Init and Shutdown are always the same though. StateManager:Call("Draw", "some", "arguments", "here", "if", "you", "want"); end
To remove the current state from the stack and initialize the next, we use StateManager:Pop();
I hope people get some use out of this, and I hope I've explained it nice enough.
2 Comments
Recommended Comments