class Game(object): # Game parameters BG_TILE_IMG = 'images/brick_tile.png' SCREEN_WIDTH, SCREEN_HEIGHT = 580, 500 GRID_SIZE = 20 FIELD_SIZE = 400, 400 CREEP_FILENAMES = [ ('images/bluecreep_0.png', 'images/bluecreep_45.png'), ('images/greencreep_0.png', 'images/greencreep_45.png'), ('images/yellowcreep_0.png', 'images/yellowcreep_45.png'), ('images/pinkcreep_0.png', 'images/pinkcreep_45.png'), ] MAX_N_CREEPS = 50 def __init__(self): pygame.init() self.screen = pygame.display.set_mode( (self.SCREEN_WIDTH, self.SCREEN_HEIGHT), 0, 32) self.tile_img = pygame.image.load(self.BG_TILE_IMG).convert_alpha() self.tile_img_rect = self.tile_img.get_rect() self.field_border_width = 4 field_outer_width = self.FIELD_SIZE[0] + 2 * self.field_border_width field_outer_height = self.FIELD_SIZE[1] + 2 * self.field_border_width self.field_rect_outer = Rect(20, 60, field_outer_width, field_outer_height) self.field_bgcolor = Color(109, 41, 1, 100) self.field_border_color = Color(0, 0, 0) self.field_box = Box(self.screen, rect=self.field_rect_outer, bgcolor=self.field_bgcolor, border_width=self.field_border_width, border_color=self.field_border_color) self.tboard_text = ['The amazing Creeps!'] self.tboard_rect = Rect(20, 20, field_outer_width, 30) self.tboard_bgcolor = Color(50, 20, 0) self.tboard = MessageBoard(self.screen, rect=self.tboard_rect, bgcolor=self.tboard_bgcolor, border_width=4, border_color=Color('black'), text=self.tboard_text, font=('tahoma', 18), font_color=Color('yellow')) self.mboard_text = [] self.mboard_rect = Rect(440, 60, 120, 60) self.mboard_bgcolor = Color(50, 20, 0) self.mboard = MessageBoard(self.screen, rect=self.mboard_rect, bgcolor=self.mboard_bgcolor, border_width=4, border_color=Color('black'), text=self.mboard_text, font=('verdana', 16), font_color=Color('white')) self.clock = pygame.time.Clock() self.paused = False self.creep_images= [ ( pygame.image.load(f1).convert_alpha(), pygame.image.load(f2).convert_alpha()) for (f1, f2) in self.CREEP_FILENAMES] explosion_img = pygame.image.load('images/explosion1.png').convert_alpha() self.explosion_images = [ explosion_img, pygame.transform.rotate(explosion_img, 90)] self.field_rect = self.get_field_rect() self.entrance_rect = Rect( self.field_rect.left, self.field_rect.top, self.GRID_SIZE * 2, self.GRID_SIZE * 2) self.exit_rect = Rect( self.field_rect.right - self.GRID_SIZE * 2, self.field_rect.bottom - self.GRID_SIZE * 2, self.GRID_SIZE * 2, self.GRID_SIZE * 2) # Create the creeps group and the first creep self.creeps = pygame.sprite.Group() self.spawn_new_creep() self.creep_spawn_timer = Timer(500, self.spawn_new_creep) self.create_walls() # Create the grid-path representation of the field # self.grid_nrows = self.FIELD_SIZE[1] / self.GRID_SIZE self.grid_ncols = self.FIELD_SIZE[0] / self.GRID_SIZE self.goal_coord = (self.grid_nrows - 1, self.grid_ncols - 1) self.gridpath = GridPath( nrows=self.grid_nrows, ncols=self.grid_ncols, goal=self.goal_coord) for wall in self.walls: self.gridpath.set_blocked(wall) self.options = dict( draw_grid=False) def create_walls(self): walls_list = [] for r in range(0, 15): walls_list.append((r, 6)) if r != 7: walls_list.append((r, 3)) walls_list.append((r, 4)) if r > 4: walls_list.append((r, 1)) for r in range(9, 20): walls_list.append((r, 10)) for c in range(14, 18): walls_list.append((15, c)) self.walls = dict().fromkeys(walls_list, True) def next_on_path(self, coord): """ Given a coord, returns the next coord on the path to the goal. None is returned if no path exists from the coord. """ return self.gridpath.get_next(coord) def xy2coord(self, pos): """ Convert a (x, y) pair to a (nrow, ncol) coordinate """ x, y = (pos[0] - self.field_rect.left, pos[1] - self.field_rect.top) return (int(y) / self.GRID_SIZE, int(x) / self.GRID_SIZE) def coord2xy_mid(self, coord): """ Convert a (nrow, ncol) coordinate to a (x, y) pair, where x,y is the middle of the square at the coord """ nrow, ncol = coord return ( self.field_rect.left + ncol * self.GRID_SIZE + self.GRID_SIZE / 2, self.field_rect.top + nrow * self.GRID_SIZE + self.GRID_SIZE / 2) def is_goal_coord(self, coord): return coord == self.goal_coord _spawned_creep_count = 0 def spawn_new_creep(self): if self._spawned_creep_count >= self.MAX_N_CREEPS: return self.creeps.add( Creep( screen=self.screen, game=self, creep_images=choice(self.creep_images), explosion_images=self.explosion_images, field=self.field_rect, init_position=( self.field_rect.left + self.GRID_SIZE / 2, self.field_rect.top + self.GRID_SIZE / 2), init_direction=(1, 1), speed=0.05)) self._spawned_creep_count += 1 def get_field_rect(self): """ Return the internal field rect - the rect of the game field exluding its border. """ return self.field_box.get_internal_rect() def draw_background(self): img_rect = self.tile_img.get_rect() nrows = int(self.screen.get_height() / img_rect.height) + 1 ncols = int(self.screen.get_width() / img_rect.width) + 1 for y in range(nrows): for x in range(ncols): img_rect.topleft = (x * img_rect.width, y * img_rect.height) self.screen.blit(self.tile_img, img_rect) def draw_portals(self): entrance_sf = pygame.Surface((self.entrance_rect.w, self.entrance_rect.h)) entrance_sf.fill(Color(80, 200, 80)) entrance_sf.set_alpha(150) self.screen.blit(entrance_sf, self.entrance_rect) exit_sf = pygame.Surface((self.exit_rect.w, self.exit_rect.h)) exit_sf.fill(Color(200, 80, 80)) exit_sf.set_alpha(150) self.screen.blit(exit_sf, self.exit_rect) def draw_grid(self): for y in range(self.grid_nrows + 1): pygame.draw.line( self.screen, Color(50, 50, 50), (self.field_rect.left, self.field_rect.top + y * self.GRID_SIZE - 1), (self.field_rect.right - 1, self.field_rect.top + y * self.GRID_SIZE - 1)) for x in range(self.grid_ncols + 1): pygame.draw.line( self.screen, Color(50, 50, 50), (self.field_rect.left + x * self.GRID_SIZE - 1, self.field_rect.top), (self.field_rect.left + x * self.GRID_SIZE - 1, self.field_rect.bottom - 1)) def draw_walls(self): wallcolor = Color(140, 140, 140) for wall in self.walls: nrow, ncol = wall pos_x = self.field_rect.left + ncol * self.GRID_SIZE + self.GRID_SIZE / 2 pos_y = self.field_rect.top + nrow * self.GRID_SIZE + self.GRID_SIZE / 2 radius = 3 pygame.draw.polygon(self.screen, wallcolor, [ (pos_x - radius, pos_y), (pos_x, pos_y + radius), (pos_x + radius, pos_y), (pos_x, pos_y - radius)]) if (nrow + 1, ncol) in self.walls: pygame.draw.line(self.screen, wallcolor, (pos_x, pos_y), (pos_x, pos_y + self.GRID_SIZE), 3) if (nrow, ncol + 1) in self.walls: pygame.draw.line(self.screen, wallcolor, (pos_x, pos_y), (pos_x + self.GRID_SIZE, pos_y), 3) def draw(self): self.draw_background() self.field_box.draw() if self.options['draw_grid']: self.draw_grid() self.draw_walls() self.tboard.draw() self.mboard.text = self.mboard_text self.mboard.draw() for creep in self.creeps: creep.draw() self.draw_portals() def run(self): # The main game loop # while True: # Limit frame speed to 30 FPS # time_passed = self.clock.tick(30) #~ time_passed = self.clock.tick() #~ print time_passed # If too long has passed between two frames, don't # update (the game must have been suspended for some # reason, and we don't want it to "jump forward" # suddenly) # if time_passed > 100: continue for event in pygame.event.get(): if event.type == pygame.QUIT: self.quit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: self.paused = not self.paused elif event.key == pygame.K_g: if pygame.key.get_mods() & pygame.KMOD_CTRL: self.options['draw_grid'] = not self.options['draw_grid'] elif ( event.type == pygame.MOUSEBUTTONDOWN and event.button == 1): for creep in self.creeps: creep.mouse_click_event(event.pos) if not self.paused: msg1 = 'Creeps: %d' % len(self.creeps) msg2 = '' self.mboard_text = [msg1, msg2] self.creep_spawn_timer.update(time_passed) # Update and all creeps for creep in self.creeps: creep.update(time_passed) self.draw() pygame.display.flip() def quit(self): sys.exit()
class Game(object): print "Setting global Game params." # Game parameters BG_TILE_IMG = 'images/wood2.png' BUTTON_BGIMG = 'images/x.png' SCREEN_WIDTH, SCREEN_HEIGHT = 580, 500 GRID_SIZE = 20 FIELD_SIZE = 400, 400 #need to implement resource loading here #global game constants make cheating easy! def __init__(self): pygame.init() print "Pygame started." #set up screen and background self.screen = pygame.display.set_mode( (self.SCREEN_WIDTH, self.SCREEN_HEIGHT), 0, 32) self.tile_img = pygame.image.load(self.BG_TILE_IMG).convert_alpha() self.tile_img_rect = self.tile_img.get_rect() #Drawing a handy MessageBoard widget #Can use these for any text. print "Configuring tboard MessageBoard params." self.tboard_text = ['This is a test.'] self.tboard_x = 120 self.tboard_y = 120 self.tboard_width = 125 self.tboard_height = 30 self.tboard_rect = Rect(self.tboard_x, self.tboard_y, self.tboard_width, self.tboard_height) self.tboard_bgcolor = Color(50, 20, 0) self.tboard = MessageBoard(self.screen, rect=self.tboard_rect, bgcolor=self.tboard_bgcolor, border_width=4, border_color=Color('black'), text=self.tboard_text, padding=5, font=('comic sans', 18), font_color=Color('yellow')) print "Moving on to buttons..." self.button_bgimgs = ['images/x.png'] #self.button_width = self.button_bgimgs[0].get_width() #self.button_height = self.button_bgimgs[0].get_height() #hopefully this will draw the button -15 pixels from the right end, +15 from the top #(hopefully giving us a nice X) # should be replaced in the future with a method that returns the coords for an x button # in whatever corner we want. #self.button_rect = Rect(self.tboard_width, self.tboard_y-15, self.button_width, self.button_height) self.button = Button(self.screen, pos=vec2d(self.tboard_width, self.tboard_y-15), btntype='Close', imgnames=self.button_bgimgs, attached=self.tboard) print "Created close button." #setting up our toggle button self.togglebtn_bgimgs = ['images/toggle1.png', 'images/toggle2.png'] self.togglebtn = Button(self.screen, pos=vec2d(250, 250), btntype='Toggle', imgnames=self.togglebtn_bgimgs, attached="") print "Created toggle button." self.buttons = [self.button, self.togglebtn] self.clock = pygame.time.Clock() self.paused = False #spawning entities #Setting up gamefield #need a method for dynamically figuring out how many rows/columns we need based on #the spacing we want and field size. Using some constants for now. self.grid_nrows = 30 self.grid_ncols = 30 self.field_rect = Rect(0, 0, self.SCREEN_WIDTH, self.SCREEN_HEIGHT) self.options = dict(debug=True, draw_grid=False) print "Done setting game options, exiting Game init." def xy2coord(self, pos): """ Convert a (x, y) pair to a (nrow, ncol) coordinate """ x, y = (pos[0] - self.field_rect.left, pos[1] - self.field_rect.top) return (int(y) / self.GRID_SIZE, int(x) / self.GRID_SIZE) def coord2xy_mid(self, coord): """ Convert a (nrow, ncol) coordinate to a (x, y) pair, where x,y is the middle of the square at the coord """ nrow, ncol = coord return ( self.field_rect.left + ncol * self.GRID_SIZE + self.GRID_SIZE / 2, self.field_rect.top + nrow * self.GRID_SIZE + self.GRID_SIZE / 2) def get_field_rect(self): """ Return the internal field rect - the rect of the game field exluding its border. """ return self.field_box.get_internal_rect() def draw_background(self): img_rect = self.tile_img.get_rect() nrows = int(self.screen.get_height() / img_rect.height) + 1 ncols = int(self.screen.get_width() / img_rect.width) + 1 for y in range(nrows): for x in range(ncols): img_rect.topleft = (x * img_rect.width, y * img_rect.height) self.screen.blit(self.tile_img, img_rect) def draw_grid(self): for y in range(self.grid_nrows + 1): pygame.draw.line( self.screen, Color(50, 50, 50), (self.field_rect.left, self.field_rect.top + y * self.GRID_SIZE - 1), (self.field_rect.right - 1, self.field_rect.top + y * self.GRID_SIZE - 1)) for x in range(self.grid_ncols + 1): pygame.draw.line( self.screen, Color(50, 50, 50), (self.field_rect.left + x * self.GRID_SIZE - 1, self.field_rect.top), (self.field_rect.left + x * self.GRID_SIZE - 1, self.field_rect.bottom - 1)) def draw(self): #draw background image self.draw_background() #draw game field crap here #decide if we should draw grid. if self.options['draw_grid']: self.draw_grid() #Only stuff being drawn right now. #Message board with x button self.tboard.draw() if self.button.is_visible(): self.button.draw() if self.togglebtn.is_visible(): self.togglebtn.draw() #this way we can draw dynamic MessageBoards. #self.mboard.text = self.mboard_text <-- copy in latest text #self.mboard.draw() #other entity draw calls def run(self): print "Beginning run sequence." # The main game loop # while True: # Limit frame speed to 30 FPS # time_passed = self.clock.tick(30) #~ time_passed = self.clock.tick() #~ print time_passed # If too long has passed between two frames, don't # update (the game must have been suspended for some # reason, and we don't want it to "jump forward" # suddenly) # if time_passed > 100: continue #Event loop. In-game control is routed through here #Will probably need something more robust soon. for event in pygame.event.get(): if event.type == pygame.QUIT: self.quit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: self.paused = not self.paused elif event.key == pygame.K_g: #toggle draw grid self.options['draw_grid'] = not self.options['draw_grid'] elif (event.type == pygame.MOUSEBUTTONDOWN and event.button == 1): for button in self.buttons: button.mouse_click_event(event.pos) pass #entity events here. #update hud, counters, score, anything like that here if not self.paused: msg1 = '' msg2 = '' #update stats counters. Not doing anything yet self.mboard_text = [msg1, msg2] #update entities with time passed for internal calculations self.draw() #actually flip Surface buffer pygame.display.flip() def quit(self): sys.exit()