I'm re-acquainting myself with PyGame prior to the next 48-hour game programming comp (yes, I'm probably going to be hosting it). I decided to start with something simple that I'd never actually gotten around to writing: a scrollable map that you navigate by clicking around it. So, here's:
- playmap.py — the source that does the work,
- tiles.png — map tile image source (with variants for most tiles),
- map.txt — map layout text file, and
- grid.png — 64x64 grid image, handy for using as a backdrop layer when drawing the map tiles.
The core of the code is the Map class:
# define where to get the tile images from in the tiles source image tile_coords = { 'a': (0,0), 'b': [(64,0), (64, 128)], 'c': (128,0), 'd': [(0,64), (128, 64)], 'e': (0,128), 'f': (128,128), '.': None, } class Map: def __init__(self, map, tiles): self.tiles = pygame.image.load(tiles) l = [line.strip() for line in open(map).readlines()] self.map = [[None]*len(l[0]) for j in range(len(l))] for i in range(len(l[0])): for j in range(len(l)): tile = l[j][i] tile = tile_coords[tile] if tile is None: continue elif isinstance(tile, type([])): tile = random.choice(tile) cx, cy = tile if random.choice((0,1)): cx += 192 if random.choice((0,1)): cy += 192 self.map[j][i] = (cx, cy) def draw(self, view, viewpos): '''Draw the map to the "view" with the top-left of "view" being at "viewpos" in the map. ''' sx, sy = view.get_size() bx = viewpos[0]/64 by = viewpos[1]/64 for x in range(0, sx+64, 64): i = x/64 + bx for y in range(0, sy+64, 64): j = y/64 + by try: tile = self.map[j][i] except IndexError: # too close to the edge continue if tile is None: continue cx, cy = tile view.blit(self.tiles, (x, y), (cx, cy, 64, 64)) def limit(self, view, pos): '''Limit the "viewpos" variable such that it defines a valid top-left rectangle of "view"'s size over the map. ''' x, y = pos # easy x = max(x, 0) y = max(y, 0) # figure number of tiles in a view, hence max x and y viewpos sx, sy = view.get_size() nx, ny = sx/64, sy/64 mx = (len(self.map[0]) - nx) * 64 my = (len(self.map) - ny) * 64 print y, my return (min(x, mx), min(y, my))
To do:
- re-write the limit() method to use pygame's built-in Rect objects to do the clamping,
- make it smoother (currently moving around is done in 64 pixel jumps, and constant movement would be nice), and
- make it do less work (currently it re-draws the whole map each time - we should be able to blit() some of the existing map when we re-draw)