When I started to create my game, I knew I wanted a few basic things. I wanted the game engine to be completely separate from whatever UI I wrapped around it. I wanted to be able to switch the game between a graphical or text based UI. I also decided that I wanted the UI to drive the engine, passing in Actions for actors who required input.

I decided to have the UI drive the engine based on Bob Nystrom’s post A Turn-Based Game Loop. If you’re interested in roguelike and turn based game design, I cannot recommend his blog, or his book Game Programming Patterns highly enough.

So after I’d finished up the design of my game engine, I knew I needed a UI wrapped around it. I’d written the engine with the idea that the UI would drive the engine, passing in actions for actors who required input, and drawing the state after each action. With this in mind, my engine was designed to announce every action taken during the game. Ignoring user input for the moment, I ended up with a first round where my UI looked like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Engine(object):
    def __init__(self):
        self.state = State()    # Ignore the internals of how I manage state here
        self.listeners = set()

    def announce(self, action):
        for listener in self.listeners:
            listener(self.state, action)

    def execute(self, action):
        pass                    # Ignore how I modify state here

    def progress():
        actor = self.get_next_actor()   # Get an actor
        action = actor.get_action()     # Get the actors action
        self.execute(action)            # Update the state
        self.announce(action)           # Announce the action to anyone interested


class UI(object):
    def __init__(self, engine):
        self.engine = engine
        self.engine.listeners.add(self.handle_events)

    def render(self, state, action):
        self.screen.clear()             # This is a vast oversimplification of how I draw things, But
        for element in state:           # that's fine for now.  I just wanted to show that at the start,
            self.screen.draw(element)   # I'd clear the screen, and then add each element from the state


    def run_game(self):
        while self.engine.is_finished == False:
            self.engine.progress()

So if we look at the flow of control in the above example, we can see that the UI in it’s __init__ method adds it’s handle_events method as a listener for Engine state changes. Then run_game tells the engine to progress. Each time the engine progresses, it get’s an actor, get’s the actor’s action, updates the state, and then announces that action to the UI. The UI object’s render method then takes control back and renders the new state, before returning to the engine, which finishes up and returns to run_game. Run game repeats the process until the engine reports that the game is over, and then the UI exits.

The nice thing about this is that nothing in the engine cares about the UI. In fact, you can run the engine entirely without a UI, taking the final loop in run_game and running it manually. This brings with it the nice benefit that you can implement multiple UIs for the game and easily interchange them. To start of with, I wrote a terminal based UI using curses. This was nice because it was relatively easy to setup, and quickly got me into a code -> run -> test -> code loop. I could test things quickly, and it allowed me to make quick progress on the game engine. However, terminal graphics and interfaces don’t suit the game as well as mouse/touch based graphics, so implementing the engine in this way allows me to switch to implementing the UI in pyGame or similar without having to refactor the engine.