Python: Runtime State Management

(1/1)

Richard Marks:
How many times have you started to write a game and after writing several hundred lines of code realize that you really should have done something to manage your code better? This little technique will help you to organize your project's code better. Now first I will say that I do not claim this to be the best approach, but I know it works, and I use it cause I like the way it works. If you use it, then give me credit.Thanks. Lets get started with the c0d3. :)

Code:

class StateMachine(object):
    def __init__(self):
        self.__states = []
    def PushState(self, newstate):
        self.__states.append(newstate)
    def PushStateMulti(self, statelist):
        for state in statelist: self.__states.append(state)
    def PopState(self):
        self.__states.pop()
    def PopStateAll(self):
        while len(self.__states): self.__states.pop()
    def Process(self):
        if len(self.__states): return True
        else: return None
    def Execute(self):
        return self.__states[-1]


Now this class may seem a little bit of overkill, since its nothing more than a wrapper around basic the functionality of Python LIST objects, but it lets you concentrate on what you are doing when writing your game code. You will see a difference in how much easier your code will be to maintain by using classes such as this one.

Next I will show  some sample usage of the StateMachine class.

Code:

class Game(object):
    def __init__(self):
        self.__statemgr = StateMachine()
        self.__gs = GameStates()
        self.__statemgr.PushState(self.__gs.ShutdownState)
        self.__statemgr.PushState(self.__gs.TitleState)
        self.__statemgr.PushState(self.__gs.InitializationState)
        data = None
        while self.__statemgr.Process() is not None:
            state = self.__statemgr.Execute()
            state(self, self.__statemgr, data)


This might need some explaining. To start with, we create an instance of our StateMachine class. Next we create an instance of another class that I will show in a few moments called GameStates which holds the code for each state that the game can be in. We then push the ShutdownState onto our state machine, followed by the TitleState and finally the InitializationState. Why do we push them in this order? Because we will be processing the last state that was pushed onto the state machine. data is set to None for now, since our game doesn't really do anything at this point other than switch states. Next the main loop of our game is simple. We query our state machine to see if there are still states to be processed, and we loop until this query returns None. Inside our loop we get a reference to the state to be executed using the Execute method from our StateMachine class, and finally we call the state function.

We pass self to the state function for the parent parameter that you will later. Following the parent, comes the reference of our state machine instance, and finally the data. Now, we know that we assume that all states will accept our three parameters, and also that they will be used properly. Next let me show you the GameStates class.

Code:

class GameStates:
    def InitializationState(self, parent, statemgr, data):
        print 'Initializing...'
        statemgr.PopState()
 
    def ShutdownState(self, parent, statemgr, data):
        print 'Shutting Down...'
        statemgr.PopState()

    def TitleState(self, parent, statemgr, data):
        print 'Title Screen'
        userinput = raw_input('Play? Y or N:')
        if userinput in ['N','n']: statemgr.PopState()
        elif userinput in ['Y','y']:
            statemgr.PushState(self.WorldMapState)
 
    def WorldMapState(self, parent, statemgr, data):
        print 'Entering World...'
        print 'I am bored there is nothing to do here.'
        print 'Leaving World...'
        statemgr.PopState()


That is about all there is to this little article. I'll answer any questions you may have. Just drop me a PM on the forums.

Navigation

[0] Message Index