def draw(self): if self.follow: cam = self.camera.pos cam.center = self.player.rect.center cam.bottom = min(cam.bottom, self.level_rect.bottom) # scrolling background self.screen.fill(BACKGROUND_COLOR) bg_rect = self.camera.apply(self.bg) bg_rect.x %= WIDTH bg_rect.y = max(bg_rect.y, HEIGHT - bg_rect.h) bg_rect.y = min(bg_rect.y, 0) self.screen.blit(self.bg.image, bg_rect) self.screen.blit(self.bg.image, bg_rect.move(bg_rect.width, 0)) self.screen.blit(self.bg.image, bg_rect.move(-bg_rect.width, 0)) # draw sprites for sprite in self.fixed_sprites + self.fading: self.screen.blit(sprite.image, self.camera.apply(sprite)) for sprite in self.destroyers: self.screen.blit(sprite.image, self.camera.apply(sprite)) self.player.draw(self.screen, self.camera, self.debug) for zombie in self.zombies.sprites() + self.fading_zombies: zombie.draw(self.screen, self.camera, self.debug) if self.minimap: mm = pg.Rect(100, 60, WIDTH - 200, HEIGHT - 120) map_cam = Camera(*mm) map_cam.move( -220, -200) # TODO: dynamic offset & scale factor to fit whole level pg.draw.rect(self.screen, pg.Color('gray'), mm, 0) for sprite in self.fixed_sprites: self.screen.blit(sprite.map_image, map_cam.apply_rect(sprite.map_rect)) # draw current screen cur_screen = self.camera.pos.copy() cur_screen.w *= 0.5 cur_screen.h *= 0.5 cur_screen.x *= 0.5 cur_screen.y *= 0.5 pg.draw.rect(self.screen, pg.Color('red'), map_cam.apply_rect(cur_screen), 1)
class Game: all_sprites: Group def __init__(self): # Initialize pygame pygame.init() # Create variable to satisfy game loop self.running = True # Initialize game-pad / joystick[0] self.control = Control(self) # Set window title and icon pygame.display.set_caption('Zorlda') self.icon = pygame.image.load(WINDOW_ICON) pygame.display.set_icon(self.icon) # Set display size to match assets, create display, and scale # 'SCALED' flag seems to be an integer scalar that is in PyGame 2.0.0+ # without, game runs at original pixel resolution (currently at 320x180) self.screen = pygame.display.set_mode((ART_SCALE_X, ART_SCALE_Y), SCALED) # ~Sprite Groups~ # ALL sprites # group with all sprites for camera updates self.all_sprites = pygame.sprite.Group() # Walls group self.walls = pygame.sprite.Group() # Mobs group self.mobs = pygame.sprite.Group() # Animated sprites group self.anim_spr = pygame.sprite.Group() # EXPERIMENTAL Set up active sprite group, which sorts layering by Y axis value # self.sprite_draw_group = YSortedGroup() # Beeper group self.beepers = pygame.sprite.Group() # ~Levels/Maps # Load the map file self.map = TiledMap(MAP_FILE) self.map_img = self.map.make_map() self.map_rect = self.map_img.get_rect() # Load objects from map self.load_map_obj() # -Background objects - transparent layer on top of other sprites bushes, fountains, etc. self.bg_objects = BGObjects(self, 0, 0, BG_OBJECTS_IMG) # ~Clocks # Pygame clock self.clock = pygame.time.Clock() # # Delta time for movement # self.dt = 0 # Animation clock self.anim_clock = Animation() # ~Camera # IT'S MAGIC self.camera = Camera(self.map.width, self.map.height) # Testing self.show_fps = False self.draw_debug = False def load_map_obj(self): for tile_object in self.map.tmxdata.objects: if tile_object.name == 'player': # Place Player self.player = Player(self, tile_object.x, tile_object.y, PLAYER_SPRITESHEET) if tile_object.name == 'karel': # place Karels Karel(self, tile_object.x, tile_object.y, KAREL_SPRITESHEET) if tile_object.name == 'wall': # place wall, etc. bounding boxes Obstacle(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height) def input_processing(self): self.control.get_input() def update(self): # Update ALL SPRITES # Sprite groups can ONLY call the update function and then use the functions *in there* XD self.all_sprites.update() # a Karel hits the Player(!) hits = pygame.sprite.spritecollide(self.player, self.mobs, False, collided=collide_hit_rect) for hit in hits: self.player.health -= 1 # hit.vel = vec(0, 0) if self.player.health <= 0: self.running = False if hits: # KNOCK-BACK # TODO: make this more responsive # ...camera moves too much to be comfortable at high velocities # solution might be to stop the mob and player's movement for a sec, freeze mob longer than player self.player.pos += hit.vel * 8 hit.pos = hit.pos - (hit.vel * 12) # a Karel hits a beeper hits = pygame.sprite.groupcollide(self.mobs, self.beepers, False, True) for hit in hits: hit.health -= 1 # Update camera self.camera.update(self.player.hit_rect) def render(self): # Render Map layers self.screen.blit(self.map_img, self.camera.apply_rect(self.map_rect)) # Render all sprites!! for sprite in self.all_sprites: self.screen.blit(sprite.image, self.camera.apply(sprite)) if self.draw_debug: pygame.draw.rect(self.screen, CYAN, self.camera.apply_rect(sprite.hit_rect), 1) # Testing functions # Show FPS in window title if self.show_fps: pygame.display.set_caption('Zorlda - FPS:{:.2f}'.format( self.clock.get_fps())) else: pygame.display.set_caption('Zorlda') # Show bounding boxes if self.draw_debug: for wall in self.walls: pygame.draw.rect(self.screen, CYAN, self.camera.apply_rect(wall.rect), 1) # HUD functions draw_player_health(self.screen, 10, 10, self.player.health) # Update display pygame.display.update() def run(self): while self.running: self.input_processing() self.update() self.render() # Frame-rate (essentially divides 1000 milliseconds(via PyGame clock) by FRAME_RATE) self.clock.tick(FRAME_RATE)
class Game: def __init__(self): pg.init() self.screen = pg.display.set_mode((WIDTH, HEIGHT)) pg.display.set_caption(TITLE) self.clock = pg.time.Clock() self.load_data() def load_data(self): game_folder = path.dirname(__file__) img_folder = path.join(game_folder, 'img') map_folder = path.join(game_folder, 'img') self.map = TiledMap(path.join(map_folder, 'map_city.tmx')) self.map_img = self.map.make_map() self.map_rect = self.map_img.get_rect() def new(self): # initialize all variables and do all the setup for a new game self.all_sprites = pg.sprite.Group() self.obstacles = pg.sprite.Group() self.stairsRD = pg.sprite.Group() self.stairsLD = pg.sprite.Group() for tile_object in self.map.tmxdata.objects: if tile_object.name == 'player': self.player_img = [self.map.tmxdata.get_tile_image_by_gid(gid) for gid, duration in tile_object.properties['frames']] self.player = Player(self, tile_object.x, tile_object.y) if tile_object.name == 'obstacle': Obstacle(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height) if tile_object.name == 'stair-right-down': StairRD(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height) if tile_object.name == 'stair-left-down': StairLD(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height) self.camera = Camera(self.map.width, self.map.height) self.draw_debug = False def run(self): # game loop - set self.playing = False to end the game self.playing = True while self.playing: self.dt = self.clock.tick(FPS) / 1000 self.events() self.update() self.draw() def quit(self): pg.quit() sys.exit() def update(self): # update portion of the game loop self.all_sprites.update() self.camera.update(self.player) def draw_grid(self): for x in range(0, WIDTH, TILESIZE): pg.draw.line(self.screen, LIGHTGREY, (x, 0), (x, HEIGHT)) for y in range(0, HEIGHT, TILESIZE): pg.draw.line(self.screen, LIGHTGREY, (0, y), (WIDTH, y)) def draw(self): pg.display.set_caption("{:.2f}".format(self.clock.get_fps())) # self.screen.fill(BGCOLOR) # self.draw_grid() self.screen.blit(self.map_img, self.camera.apply_rect(self.map_rect)) for sprite in self.all_sprites: self.screen.blit(sprite.image, self.camera.apply(sprite)) pg.display.flip() def events(self): # catch all events here for event in pg.event.get(): if event.type == pg.QUIT: self.quit() if event.type == pg.KEYDOWN: if event.key == pg.K_ESCAPE: self.quit() def show_start_screen(self): pass def show_go_screen(self): pass
class Game: def __init__(self): """Initialize everything needed to run the game.""" pg.init() self.screen = pg.display.set_mode((WIDTH, HEIGHT)) pg.display.set_caption(TITLE) self.clock = pg.time.Clock() pg.key.set_repeat(500, 100) def new(self): """ Initialize everything needed for a new game.""" self.background = self.create_layer(Tile) # Holds all layers. self.layers = [self.background] self.player_sprite = pg.sprite.Group() self.player = Player(self, 0, 0) self.camera = Camera(self.background.width, self.background.height) def create_layer(self, object_type): """Create a layer and populate it with the appropriate object.""" layer = Grid(object_type) layer.init_chunks() layer.init_objects() return layer def run(self): """Runs pygame.""" while True: self.dt = self.clock.tick(FPS) / 1000 self.events() self.update() self.draw() def events(self): """Catch all events here.""" for event in pg.event.get(): if event.type == pg.QUIT: pg.quit() sys.exit() if event.type == pg.KEYDOWN: if event.key == pg.K_ESCAPE: pg.quit() sys.exit() # Moves the player and camera. if event.key == pg.K_LEFT: self.player.move(dx=-1) if event.key == pg.K_RIGHT: self.player.move(dx=1) if event.key == pg.K_UP: self.player.move(dy=-1) if event.key == pg.K_DOWN: self.player.move(dy=1) def update(self): """Everything that needs to be updated is done here.""" # Update player sprite. self.player_sprite.update() self.camera.update(self.player) # Uses player position to know which chunks to render around it. for layer in self.layers: layer.render = layer.render_chunks(*self.player.get()) # Set new limits for the camera. self.camera._update_max_size(self.background) def draw(self): """Draws images and displays it on the screen.""" # Covers previous screen. Essentially wipes it. self.screen.fill(BGCOLOR) # Get fps of the game. pg.display.set_caption("{:.2f}".format(self.clock.get_fps())) for layer in self.layers: for chunk in layer.render: for tile in chunk: self.screen.blit(tile[0], self.camera.apply_rect(tile[1])) for sprite in self.player_sprite: self.screen.blit(sprite.image, self.camera.apply(sprite)) pg.display.flip()
class Game(object): def __init__(self): self.screen = pg.display.set_mode(SIZE) self.clock = pg.time.Clock() self.running = True self.paused = False self.debug = False self.shift = False self.follow = False self.minimap = False self.FPS = 60 self.bg = Background(SIZE) self.camera = Camera(w=WIDTH, h=HEIGHT) self.blocks = [] self.fading = [] self.fading_zombies = [] self.screenshot_counter = 0 self.destroyers = pg.sprite.Group() self.player = Ninja(position=(200, 100), screen=self.bg.rect) zombie1 = Zombie((100, 500), self.bg.rect, False) zombie2 = Zombie((700, 50), self.bg.rect, True) self.zombies = pg.sprite.Group(zombie1, zombie2) self.reset() self.run() def reset(self): for char in self.zombies.sprites() + [self.player]: char.reset() # TODO: readd zombies level, deco, self.objects, level_rect, cam_origin = load_level() self.fixed_sprites = level + deco + self.objects del self.blocks[:] self.blocks.extend([block.rect for block in level]) if cam_origin: self.camera.reset() self.camera.move(*cam_origin) self.camera.pos.bottom = min(self.camera.pos.bottom, level_rect.bottom) else: self.camera.pos.center = level_rect.center self.camera.pos.bottom = level_rect.bottom self.level_rect = level_rect def toggle_debug(self): self.debug = not self.debug def toggle_paused(self): if self.minimap: return self.paused = not self.paused def toggle_follow(self): self.follow = not self.follow def toggle_slomo(self): # slow motion self.FPS = 30 if self.FPS == 60 else 60 def toggle_minimap(self): self.minimap = not self.minimap self.paused = self.minimap def ranged_attack(self): if self.paused: return kunai = self.player.throw() if kunai: self.destroyers.add(kunai) def right(self): if self.shift: self.camera.move(100, 0) elif not self.paused: self.player.move_right() def left(self): if self.shift: self.camera.move(-100, 0) elif not self.paused: self.player.move_left() def up(self): if self.shift: self.camera.move(0, -100) elif not self.paused: self.player.move_up() def down(self): if self.shift: self.camera.move(0, 100) elif not self.paused: self.player.move_down() def screenshot(self): filename = "screenshot{c}.jpg".format(c=self.screenshot_counter) self.screenshot_counter += 1 pg.image.save(self.screen, filename) def draw(self): if self.follow: cam = self.camera.pos cam.center = self.player.rect.center cam.bottom = min(cam.bottom, self.level_rect.bottom) # scrolling background self.screen.fill(BACKGROUND_COLOR) bg_rect = self.camera.apply(self.bg) bg_rect.x %= WIDTH bg_rect.y = max(bg_rect.y, HEIGHT - bg_rect.h) bg_rect.y = min(bg_rect.y, 0) self.screen.blit(self.bg.image, bg_rect) self.screen.blit(self.bg.image, bg_rect.move(bg_rect.width, 0)) self.screen.blit(self.bg.image, bg_rect.move(-bg_rect.width, 0)) # draw sprites for sprite in self.fixed_sprites + self.fading: self.screen.blit(sprite.image, self.camera.apply(sprite)) for sprite in self.destroyers: self.screen.blit(sprite.image, self.camera.apply(sprite)) self.player.draw(self.screen, self.camera, self.debug) for zombie in self.zombies.sprites() + self.fading_zombies: zombie.draw(self.screen, self.camera, self.debug) if self.minimap: mm = pg.Rect(100, 60, WIDTH - 200, HEIGHT - 120) map_cam = Camera(*mm) map_cam.move( -220, -200) # TODO: dynamic offset & scale factor to fit whole level pg.draw.rect(self.screen, pg.Color('gray'), mm, 0) for sprite in self.fixed_sprites: self.screen.blit(sprite.map_image, map_cam.apply_rect(sprite.map_rect)) # draw current screen cur_screen = self.camera.pos.copy() cur_screen.w *= 0.5 cur_screen.h *= 0.5 cur_screen.x *= 0.5 cur_screen.y *= 0.5 pg.draw.rect(self.screen, pg.Color('red'), map_cam.apply_rect(cur_screen), 1) def run(self): while self.running: dt = self.clock.tick( self.FPS) / 1000 # Amount of seconds between each loop. def nop(): return lambda: None def stop(): if self.paused: return self.player.stop() keydown_handlers = defaultdict( nop, { pg.K_ESCAPE: sys.exit, pg.K_F1: self.toggle_debug, pg.K_F2: self.toggle_slomo, pg.K_F3: self.toggle_follow, pg.K_F12: self.screenshot, pg.K_RIGHT: self.right, pg.K_LEFT: self.left, pg.K_UP: self.up, pg.K_DOWN: self.down, pg.K_SPACE: lambda: None if self.paused else self.player.jump(), pg.K_LCTRL: lambda: None if self.paused else self.player.attack(), pg.K_x: lambda: None if self.paused else self.player.die(), pg.K_c: lambda: None if self.paused else self.player.glide(), pg.K_v: self.ranged_attack, pg.K_r: self.reset, pg.K_p: self.toggle_paused, pg.K_m: self.toggle_minimap, pg.K_b: lambda: [zombie.die() for zombie in self.zombies], pg.K_a: lambda: [zombie.attack() for zombie in self.zombies], }) keyup_handlers = defaultdict( nop, { pg.K_RIGHT: stop, pg.K_LEFT: stop, pg.K_UP: stop, pg.K_DOWN: stop, # pg.K_DOWN or pg.K_UP: self.player.velocity.y = 0 }) for event in pg.event.get(): mods = pg.key.get_mods() self.shift = mods & pg.KMOD_SHIFT if event.type == pg.QUIT: self.running = False elif event.type == pg.KEYDOWN: keydown_handlers[event.key]() elif event.type == pg.KEYUP: keyup_handlers[event.key]() if not self.paused: # update character geometry self.player.fall() for zombie in self.zombies: zombie.fall() self.player.update( self.blocks + [o.rect for o in self.objects + self.zombies.sprites()], self.camera) for zombie in self.zombies: zombie.update( self.blocks + [o.rect for o in self.objects + [self.player]]) for zombie in self.fading_zombies: zombie.update([]) # ranged attacks for destr in self.destroyers: if destr.update(self.blocks, self.camera): self.destroyers.remove(destr) idx = destr.rect.collidelist(self.objects) if idx != -1: self.destroyers.remove(destr) obj = self.objects[idx] self.fading.append(obj) self.objects.remove(obj) self.fixed_sprites.remove(obj) for zombie in self.zombies: if destr.rect.colliderect(zombie.rect): if zombie.suffer(50): self.zombies.remove(zombie) self.fading_zombies.append(zombie) self.destroyers.remove(destr) # melee attack death_box = self.player.get_attack_box() if death_box: idx = death_box.collidelist(self.objects) if idx != -1: obj = self.objects[idx] self.fading.append(obj) self.objects.remove(obj) self.fixed_sprites.remove(obj) idx = death_box.collidelist([z.rect for z in self.zombies]) for zombie in self.zombies: if death_box.colliderect(zombie.rect): if zombie.suffer(2): self.fading_zombies.append(zombie) self.zombies.remove(zombie) # fade-outs self.fading = [obj for obj in self.fading if not obj.fade()] self.fading_zombies = [ z for z in self.fading_zombies if not z.fade() ] self.draw() if self.debug: # show animation progression anim, idx = self.player.get_anim() x = y = 20 scale_factor = -0.5 for i, a in enumerate(anim): rect = a.get_rect() rect.inflate_ip(rect.w * scale_factor, rect.h * scale_factor) image = pg.transform.scale(a, rect[-2:]) rect.move_ip(x, y) x += rect.width self.screen.blit(image, rect) if i == idx + 1: pg.draw.rect(self.screen, RED, rect, 1) pg.draw.rect(self.screen, RED, self.camera.apply(self.player), 1) for sprite in self.fixed_sprites + self.destroyers.sprites(): pg.draw.rect(self.screen, RED, self.camera.apply(sprite), 1) if self.player.current_animation == 'Attack': r = self.player.rect.h // 2 top = self.player.rect.top x = self.player.rect.right if self.player.facing_right else self.player.rect.left y = top + r pg.draw.circle(self.screen, RED, self.camera.apply_coords((x, y)), r, 1) death_box = self.player.get_attack_box() if death_box: pg.draw.rect(self.screen, RED, self.camera.apply_rect(death_box), 1) pg.display.update()