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)

