Mercurial > pavulon
changeset 22:78492768ce73
fix the depth bug, add comments
| author | sheep@ghostwheel |
|---|---|
| date | Sun, 25 Oct 2009 13:21:29 +0100 |
| parents | 7bd74f38be48 |
| children | 71ba52633428 |
| files | MgOpenModataBold.ttf cursor.png dialog.png qq.py |
| diffstat | 4 files changed, 49 insertions(+), 37 deletions(-) [+] |
line wrap: on
line diff
--- a/qq.py Sun Oct 25 13:10:22 2009 +0100 +++ b/qq.py Sun Oct 25 13:21:29 2009 +0100 @@ -1,12 +1,27 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +""" +This is sample of how you can implement a tile-based game, not unlike +the RPG games known from consoles, in pygame. It's not a playable game, +but it can be turned into one. Care has been taken to comment it clearly, +so that you can use it easily as a starting point for your game. + +The program reads a level definition from a "level.map" file, and uses the +graphics referenced for that file to display a tiled map on the screen and +let you move an animated player character around it. + +Note that a lot of additional work is needed to turn it into an actual game. + +@copyright: 2008, 2009 Radomir Dopieralski <qq@sheep.art.pl> +@license: BSD, see COPYING for details + +""" + import ConfigParser import pygame import pygame.locals as pg -# from pygame.locals import * - # Motion offsets for particular directions # N E S W @@ -24,16 +39,19 @@ self.height = height or width def __getitem__(self, filename): + """Return a table of tiles, load it from disk if needed.""" + key = (filename, self.width, self.height) try: return self.cache[key] except KeyError: - tile_table = self.load_tile_table(filename, self.width, self.height) + tile_table = self._load_tile_table(filename, self.width, + self.height) self.cache[key] = tile_table return tile_table - def load_tile_table(self, filename, width, height): - """Load the image and split into tiles.""" + def _load_tile_table(self, filename, width, height): + """Load an image and split it into tiles.""" image = pygame.image.load(filename).convert() image_width, image_height = image.get_size() @@ -51,11 +69,9 @@ """A sprite group that sorts them by depth.""" def sprites(self): - """The list of sprites in the group.""" + """The list of sprites in the group, sorted by depth.""" - sprites = self.spritedict.keys() - sprites.sort(lambda a, b: a.depth - b.depth) - return sprites + return sorted(self.spritedict.keys(), key=lambda sprite: sprite.depth) class Shadow(pygame.sprite.Sprite): @@ -87,7 +103,6 @@ self.rect = self.image.get_rect() self.animation = self.stand_animation() self.pos = pos - self.depth = 0 def _get_pos(self): """Check the current position of the sprite on the map.""" @@ -95,7 +110,7 @@ return (self.rect.midbottom[0]-12)/24, (self.rect.midbottom[1]-16)/16 def _set_pos(self, pos): - """Set the position of the sprite on the map.""" + """Set the position and depth of the sprite on the map.""" self.rect.midbottom = pos[0]*24+12, pos[1]*16+16 self.depth = self.rect.midbottom[1] @@ -134,8 +149,6 @@ self.direction = 2 self.animation = None self.image = self.frames[self.direction][0] - # Move it on top of all other sprites on the same tile - self.rect.move_ip((0, 1)) def walk_animation(self): """Animation for the player walking.""" @@ -179,21 +192,14 @@ self.map = parser.get("level", "map").split("\n") for section in parser.sections(): if len(section) == 1: - # value = parser.get(section, "name") desc = dict(parser.items(section)) self.key[section] = desc self.width = len(self.map[0]) self.height = len(self.map) - y = 0 - for line in self.map: - x = 0 - for c in line: - if not self.is_wall(x, y): - # floor - if 'sprite' in self.key[c]: - self.items[(x, y)] = self.key[c] - x += 1 - y += 1 + for y, line in enumerate(self.map): + for x, c in enumerate(line): + if not self.is_wall(x, y) and 'sprite' in self.key[c]: + self.items[(x, y)] = self.key[c] def render(self): """Draw the level on the surface.""" @@ -203,11 +209,10 @@ tiles = TileCache(tile_width, tile_height)[self.tileset] image = pygame.Surface((self.width*tile_width, self.height*tile_height)) overlays = {} - map_y = 0 - for line in self.map: - map_x = 0 - for c in line: + for map_y, line in enumerate(self.map): + for map_x, c in enumerate(line): if wall(map_x, map_y): + # Draw different tiles depending on neighbourhood if not wall(map_x, map_y+1): if wall(map_x+1, map_y) and wall(map_x-1, map_y): tile = 1, 2 @@ -226,6 +231,7 @@ tile = 2, 1 else: tile = 3, 1 + # Add overlays if the wall may be obscuring something if not wall(map_x, map_y-1): if wall(map_x+1, map_y) and wall(map_x-1, map_y): over = 1, 0 @@ -241,11 +247,10 @@ tile = self.key[c]['tile'].split(',') tile = int(tile[0]), int(tile[1]) except (ValueError, KeyError): + # Default to ground tile tile = 0, 3 tile_image = tiles[tile[0]][tile[1]] image.blit(tile_image, (map_x*tile_width, map_y*tile_height)) - map_x += 1 - map_y += 1 return image, overlays def get_tile(self, x, y): @@ -264,7 +269,7 @@ """Tell if the specified flag is set for position on the map.""" value = self.get_tile(x, y).get(name) - return value in ('true', 'yes', 'True', 'Yes', '1', 'on', 'On') + return value in (True, 1, 'true', 'yes', 'True', 'Yes', '1', 'on', 'On') def is_wall(self, x, y): """Is there a wall?""" @@ -303,6 +308,7 @@ self.sprites = SortedUpdates() self.overlays = pygame.sprite.RenderUpdates() self.level = level + # Populate the game with the level's objects for pos, tile in level.items.iteritems(): if tile.get("player") in ('true', '1', 'yes', 'on'): sprite = Player(pos) @@ -311,9 +317,10 @@ sprite = Sprite(pos, TileCache()[tile["sprite"]]) self.sprites.add(sprite) self.shadows.add(Shadow(sprite)) + # Render the level map self.background, overlays = self.level.render() - for pos, image in overlays.iteritems(): - x, y = pos + # Add the overlays for the level map + for (x, y), image in overlays.iteritems(): overlay = pygame.sprite.Sprite(self.overlays) overlay.image = image overlay.rect = image.get_rect().move(x*24, y*16-16) @@ -324,7 +331,7 @@ keys = pygame.key.get_pressed() def pressed(key): - """Check if the key is pressed.""" + """Check if the specified key is pressed.""" return self.pressed_key == key or keys[key] @@ -350,13 +357,16 @@ """Run the main loop.""" clock = pygame.time.Clock() + # Draw the whole screen initially self.screen.blit(self.background, (0, 0)) self.overlays.draw(self.screen) pygame.display.flip() + # The main game loop while not self.game_over: - # Don't clear shadows and overlays. + # Don't clear shadows and overlays, only sprites. self.sprites.clear(self.screen, self.background) self.sprites.update() + # If the player's animation is finished, check for keypresses if self.player.animation is None: self.control() self.player.update() @@ -366,10 +376,13 @@ self.shadows.draw(self.screen) dirty = self.sprites.draw(self.screen) # Don't add ovelays to dirty rectangles, only the places where - # sprites are need to be updated. + # sprites are need to be updated, and those are already dirty. self.overlays.draw(self.screen) + # Update the dirty areas of the screen pygame.display.update(dirty) + # Wait for one tick of the game clock clock.tick(15) + # Process pygame events for event in pygame.event.get(): if event.type == pg.QUIT: self.game_over = True @@ -380,5 +393,4 @@ if __name__ == "__main__": pygame.init() pygame.display.set_mode((424, 320)) - # font = pygame.font.Font("MgOpenModataBold.ttf", 11) Game().main()
