First of all, let's get rid of these ugly if-blocks:

for e in pygame.event.get(): if e.type == QUIT: raise SystemExit, "QUIT" if e.type == KEYDOWN and e.key == K_ESCAPE: raise SystemExit, "ESCAPE" if e.type == KEYDOWN and e.key == K_UP: up = True if e.type == KEYDOWN and e.key == K_LEFT: left = True if e.type == KEYDOWN and e.key == K_RIGHT: right = True if e.type == KEYUP and e.key == K_UP: up = False if e.type == KEYUP and e.key == K_LEFT: left = False if e.type == KEYUP and e.key == K_RIGHT: right = False

We can rewrite them as:

for e in pygame.event.get(): if e.type == QUIT: raise SystemExit, "QUIT" if e.type == KEYDOWN and e.key == K_ESCAPE: raise SystemExit, "ESCAPE" pressed = pygame.key.get_pressed() up, left, right = [pressed[key] for key in (K_UP, K_LEFT, K_RIGHT)]

This will come in handy later.

Back to topic: What we want is a bunch of different Scenes. Each Scene has to be responsible for its own rendering of the screen and event-handling.

Let's try to extract the existing code into a game scene, so that it will be possible to add other scenes later on. We start by creating an empty Scene class that will be the base class of our scenes:

class Scene(object): def __init__(self): pass def render(self, screen): raise NotImplementedError def update(self): raise NotImplementedError def handle_events(self, events): raise NotImplementedError

Our plan is to overwrite each method in each sub-class, so we raise NotImplementedError s in the base class so we easily discover if we forget to do so (we could also use ABCs, but let's keep it simple).

Now let's put everything related to the running-game state (which is basically everything) into a new GameScene class.

class GameScene(Scene): def __init__(self): super(GameScene, self).__init__() level = 0 self.bg = Surface((32,32)) self.bg.convert() self.bg.fill(Color("#0094FF")) up = left = right = False self.entities = pygame.sprite.Group() self.player = Player(32, 32) self.enemy = Enemy(32,32) self.platforms = [] x = 0 y = 0 if level == 0: level = [ " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " E ", " PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPPP PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPP P", " PPPP P", " PPPP PPPPPPP", " PPPPPPPPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPPPPP", "PPPPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPPP PPPP PPPPPPP", "PPP PPPP", "PPP PPPP", "PPP PPPP", "PPP PPPPPPPPPPPPPPPPPP", "PPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",] #background = pygame.image.load("Untitled.png") total_level_width = len(level[0]) * 32 total_level_height = len(level) * 32 # build the level for row in level: for col in row: if col == "P": p = Platform(x, y) self.platforms.append(p) self.entities.add(p) if col == "E": e = ExitBlock(x, y) self.platforms.append(e) self.entities.add(e) x += 32 y += 32 x = 0 self.camera = Camera(complex_camera, total_level_width, total_level_height) self.entities.add(self.player) self.entities.add(self.enemy) def render(self, screen): for y in range(20): for x in range(25): screen.blit(self.bg, (x * 32, y * 32)) for e in self.entities: screen.blit(e.image, self.camera.apply(e)) def update(self): pressed = pygame.key.get_pressed() up, left, right = [pressed[key] for key in (K_UP, K_LEFT, K_RIGHT)] self.player.update(up, left, right, self.platforms) self.enemy.update(self.platforms) self.camera.update(self.player) def handle_events(self, events): for e in events: if e.type == KEYDOWN and e.key == K_ESCAPE: pass #somehow go back to menu

Not perfect yet, but a good start. Everything related to the actual gameplay is extracted to its own class. Some variables have to be instance variable, so they have to be accessed via self .

Now we need to alter the main -function to actually use this class:

def main(): pygame.init() screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH) pygame.display.set_caption("ABCDEFGHIJKLMNOPQRSTUVWXYZ") timer = pygame.time.Clock() running = True scene = GameScene() while running: timer.tick(60) if pygame.event.get(QUIT): running = False return scene.handle_events(pygame.event.get()) scene.update() scene.render(screen) pygame.display.flip()

Note that I changed two little things: I use pygame.event.get(QUIT) to only get a possible QUIT -event first, since this is the only event that we are interesset in in our main-loop. All other events are passed directly to the current scene: scene.handle_events(pygame.event.get()) .

At this point, we could think about extracting some classes to thier own files, but let's going on instead.

Let's create a title menu:

class TitleScene(object): def __init__(self): super(TitleScene, self).__init__() self.font = pygame.font.SysFont('Arial', 56) self.sfont = pygame.font.SysFont('Arial', 32) def render(self, screen): # beware: ugly! screen.fill((0, 200, 0)) text1 = self.font.render('Crazy Game', True, (255, 255, 255)) text2 = self.sfont.render('> press space to start <', True, (255, 255, 255)) screen.blit(text1, (200, 50)) screen.blit(text2, (200, 350)) def update(self): pass def handle_events(self, events): for e in events: if e.type == KEYDOWN and e.key == K_SPACE: self.manager.go_to(GameScene(0))

This just displays a green background and some text. If the player presses SPACE , we want to start the first level. Note this line:

self.manager.go_to(GameScene(0))

Here I pass the argument 0 to the GameScene class, so let's alter it so it accepts this parameter:

class GameScene(Scene): def __init__(self, level): ...

the line level = 0 can be removed, as you already guessed.

So what it's self.manager ? It's just a little helper class that manages the scenes for us.

class SceneMananger(object): def __init__(self): self.go_to(TitleScene()) def go_to(self, scene): self.scene = scene self.scene.manager = self

It starts with the title scene and sets each scenes manager field to itself to allow the changing of the current scene. There are a lot of possibilities of how to implement such a scene manager, and this is the simpliest approach. A disadvantage is that each scene has to know which scene comes after it, but that should not bother us right now.

Let's use our new SceneMananger :

def main(): pygame.init() screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH) pygame.display.set_caption("ABCDEFGHIJKLMNOPQRSTUVWXYZ") timer = pygame.time.Clock() running = True manager = SceneMananger() while running: timer.tick(60) if pygame.event.get(QUIT): running = False return manager.scene.handle_events(pygame.event.get()) manager.scene.update() manager.scene.render(screen) pygame.display.flip()

straightforward. Let's quickly add a second level and a winning/losing screen and we are done.

class CustomScene(object): def __init__(self, text): self.text = text super(CustomScene, self).__init__() self.font = pygame.font.SysFont('Arial', 56) def render(self, screen): # ugly! screen.fill((0, 200, 0)) text1 = self.font.render(self.text, True, (255, 255, 255)) screen.blit(text1, (200, 50)) def update(self): pass def handle_events(self, events): for e in events: if e.type == KEYDOWN: self.manager.go_to(TitleScene())

Below is the complete code. Note the changes to the Player class: instead of calling the main function again, it calls methods on the scene to indicate the player reached the exit or died.

Also, I changed the placement of the player and enemies. Now, you specify where the entity will appear in the level. E.g. Player(5, 40) will create the player at column 5, row 40 in the level. As a bonus, the enemies to proper collision detection.

I extracted the description of the levels to a dictionary named levels , so it will be easy to alter and add levels as needed (later, you probably want one file per level, so this is a good start). It could be extended to hold the player starting position, but you could also create a special tile, like * for starting position and E for enemy in the level description.