import sys import pygame import random from modules.TankGame import TankGame from modules.sprites import * from modules.views.AbstractView import AbstractView from modules.sprites.tanks import DIRECTION from pygame.sprite import spritecollide, groupcollide, collide_rect class GameLevelView(AbstractView): def _init_resources(self): config = self.config self.__sounds = TankGame().sounds self.__other_images = config.OTHER_IMAGE_PATHS self.__home_images = config.HOME_IMAGE_PATHS self.__background_img = pygame.image.load(self.__other_images.get('background')) self.__font = pygame.font.Font(config.FONTPATH, config.HEIGHT // 35) self.__border_len = config.BORDER_LEN self.__grid_size = config.GRID_SIZE self.__screen_width, self.__screen_height = config.WIDTH, config.HEIGHT self.__panel_width = config.PANEL_WIDTH self.__tank_factory = TankFactory(self.config) self.__scene_factory = SceneFactory(self.config) self.__scene_elements = None def _init_text(self): color_white = (255, 255, 255) self.__fix_text_tips = { 1: {'text': 'Operate-P1'}, 2: {'text': 'K_w: Up'}, 3: {'text': 'K_s: Down'}, 4: {'text': 'K_a: Left'}, 5: {'text': 'K_d: Right'}, 6: {'text': 'K_SPACE: Shoot'}, 8: {'text': 'Operate-P2:'}, 9: {'text': 'K_UP: Up'}, 10: {'text': 'K_DOWN: Down'}, 11: {'text': 'K_LEFT: Left'}, 12: {'text': 'K_RIGHT: Right'}, 13: {'text': 'K_KP0: Shoot'}, 15: {'text': 'State-P1:'}, 19: {'text': 'State-P2:'}, } for pos, tip in self.__fix_text_tips.items(): tip['render'] = self.__font.render(tip['text'], True, color_white) tip['rect'] = tip['render'].get_rect() tip['rect'].left, tip['rect'].top = self.__screen_width + 5, self.__screen_height * pos / 30 pass '''开始游戏''' def _init_game_window(self): TankGame().init_game_window( (self.config.WIDTH + self.config.PANEL_WIDTH, self.config.HEIGHT) ) def __play_sound(self, sound): self.__sounds[sound].play() def __dispatch_player_operation(self): key_pressed = pygame.key.get_pressed() key_maps = { 'dir': { self.__tank_player1: { pygame.K_w: DIRECTION.UP, pygame.K_s: DIRECTION.DOWN, pygame.K_a: DIRECTION.LEFT, pygame.K_d: DIRECTION.RIGHT, }, self.__tank_player2: { pygame.K_UP: DIRECTION.UP, pygame.K_DOWN: DIRECTION.DOWN, pygame.K_LEFT: DIRECTION.LEFT, pygame.K_RIGHT: DIRECTION.RIGHT, }, }, 'fire': { self.__tank_player1: pygame.K_SPACE, self.__tank_player2: pygame.K_KP0, }, } # 玩家一, WSAD移动, 空格键射击 player_tank_list = [] if self.__tank_player1.health >= 0: player_tank_list.append(self.__tank_player1) if TankGame().multiplayer_mode and (self.__tank_player1.health >= 0): player_tank_list.append(self.__tank_player2) for tank in player_tank_list: for key, dir in key_maps['dir'][tank].items(): if key_pressed[key]: self.__entities.remove(tank) tank.move(dir, self.__scene_elements, self.__entities.player_tanks, self.__entities.enemy_tanks, self.__home) tank.roll() self.__entities.add(tank) break if key_pressed[key_maps['fire'][tank]]: bullet = tank.shoot() if bullet: self.__play_sound('fire') if tank._level < 2 else self.__play_sound('Gunfire') self.__entities.add(bullet) def __dispatch_food_effect(self, food: Foods, player_tank: PlayerTank): self.__play_sound('add') if food.type == Foods.BOOM: for _ in self.__entities.enemy_tanks: self.__play_sound('bang') self.__total_enemy_num -= len(self.__entities.enemy_tanks) self.__entities.clear_enemy_tanks() elif food.type == Foods.CLOCK: for enemy_tank in self.__entities.enemy_tanks: enemy_tank.set_still() elif food.type == Foods.GUN: player_tank.improve_level() elif food.type == Foods.IRON: for x, y in self.__home.walls_position: self.__scene_elements.add( self.__scene_factory.create_element((x, y), SceneFactory.IRON) ) elif food.type == Foods.PROTECT: player_tank.protected = True elif food.type == Foods.STAR: player_tank.improve_level() player_tank.improve_level() elif food.type == Foods.TANK: player_tank.add_health() self.__entities.foods.remove(food) def __dispatch_collisions(self): collision_results = { 'group': {}, 'sprite': {}, 'foreach_sprite': {}, } for (collision, args) in self.__collisions['group'].items(): collision_results['group'][collision] = groupcollide(*args) for (collision, args) in self.__collisions['sprite'].items(): collision_results['sprite'][collision] = spritecollide(*args) for (collision, args) in self.__collisions['foreach_sprite'].items(): arg_list = list(args) sprite_list = arg_list[0] for sprite in sprite_list: arg_list[0] = sprite args = tuple(arg_list) collision_results['foreach_sprite'][sprite] = spritecollide(*args) for bullet in self.__entities.player_bullets: collision_result = spritecollide(bullet, self.__scene_elements.iron_group, bullet.enhanced, None) if collision_result: bullet.kill() for player_tank in self.__entities.player_tanks: for food in self.__entities.foods: collision_result = collide_rect(player_tank, food) if collision_result: self.__dispatch_food_effect(food, player_tank) # --我方子弹撞敌方坦克 for tank in self.__entities.enemy_tanks: if collision_results['foreach_sprite'][tank]: if tank.food: self.__entities.add(tank.food) tank.clear_food() if tank.decrease_level(): self.__play_sound('bang') self.__total_enemy_num -= 1 # --敌方子弹撞我方坦克 for tank in self.__entities.player_tanks: if collision_results['foreach_sprite'][tank]: if tank.protected: self.__play_sound('blast') else: if tank.decrease_level(): self.__play_sound('bang') if tank.health < 0: self.__entities.remove(tank) if collision_results['sprite']['PlayerBulletWithHome'] or collision_results['sprite']['EnemyBulletWithHome']: self.__is_win_flag = False self.__has_next_loop = False self.__play_sound('bang') self.__home.destroyed = True if collision_results['group']['PlayerTankWithTree']: self.__play_sound('hit') def _draw_interface(self): screen = TankGame().screen screen.fill((0, 0, 0)) screen.blit(self.__background_img, (0, 0)) self.__scene_elements.draw(screen, 1) self.__entities.draw(screen, 1) self.__scene_elements.draw(screen, 2) self.__home.draw(screen) self.__entities.draw(screen, 2) self.__draw_game_panel() pygame.display.flip() def _main_loop(self): clock = pygame.time.Clock() # cheat for test # self.__tank_player1.improve_level() # self.__tank_player1.improve_level() # self.__tank_player1.protected = True while self.__has_next_loop: # 用户事件捕捉 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() # --敌方坦克生成 elif event.type == self.__generate_enemies_event: if self.max_enemy_num > len(self.__entities.enemy_tanks): for position in self.__enemy_spawn_point: if len(self.__entities.enemy_tanks) == self.__total_enemy_num: break enemy_tank = self.__tank_factory.create_tank(position, TankFactory.ENEMY_TANK) if spritecollide(enemy_tank, self.__entities.enemy_tanks, False, None) or\ spritecollide(enemy_tank, self.__entities.player_tanks, False, None): del enemy_tank else: self.__entities.add(enemy_tank) # --用户按键 self.__dispatch_player_operation() # 碰撞检测 self.__dispatch_collisions() self.__entities.update(self.__scene_elements, self.__home) self._draw_interface() # 我方坦克都挂了 if len(self.__entities.player_tanks) == 0: self.__is_win_flag = False self.__has_next_loop = False # 敌方坦克都挂了 if self.__total_enemy_num <= 0: self.__is_win_flag = True self.__has_next_loop = False clock.tick(60) def __init_collision_config(self): self.__collisions = { 'group': { 'PlayerBulletWithBrick': (self.__entities.player_bullets, self.__scene_elements.brick_group, True, True), 'EnemyBulletWithBrick': (self.__entities.enemy_bullets, self.__scene_elements.brick_group, True, True), 'EnemyBulletWithIron': (self.__entities.enemy_bullets, self.__scene_elements.iron_group, True, False), 'BulletWithBullet': (self.__entities.player_bullets, self.__entities.enemy_bullets, True, True), 'PlayerTankWithTree': (self.__entities.player_tanks, self.__scene_elements.tree_group, False, False), }, 'sprite': { 'EnemyBulletWithHome': (self.__home, self.__entities.enemy_bullets, True, None), 'PlayerBulletWithHome': (self.__home, self.__entities.player_bullets, True, None), }, 'foreach_sprite': { 'PlayerTankWithEnemyBullet': (self.__entities.player_tanks, self.__entities.enemy_bullets, True, None), 'EnemyTankWithPlayerBullet': (self.__entities.enemy_tanks, self.__entities.player_bullets, True, None), } } def __init_tanks(self): self.__tank_player1 = self.__tank_factory.create_tank( self.__player_spawn_point[0], TankFactory.PLAYER1_TANK ) self.__entities.add(self.__tank_player1) self.__tank_player2 = None if TankGame().multiplayer_mode: self.__tank_player2 = self.__tank_factory.create_tank( self.__player_spawn_point[1], TankFactory.PLAYER2_TANK ) self.__entities.add(self.__tank_player2) # 敌方坦克 for position in self.__enemy_spawn_point: self.__entities.add( self.__tank_factory.create_tank(position, TankFactory.ENEMY_TANK) ) def __init_user_event(self): self.__generate_enemies_event = pygame.constants.USEREVENT pygame.time.set_timer(self.__generate_enemies_event, 20000) def __init_entities(self): self.__entities = EntityGroup() def show(self): self._init_game_window() self._init_text() self.__load_level_file() self.__init_entities() self.__init_user_event() self.__init_tanks() self.__init_collision_config() self.__play_sound('start') self.__is_win_flag = False self.__has_next_loop = True self._main_loop() TankGame().is_win = self.__is_win_flag '''显示游戏面板''' def __draw_game_panel(self): color_white = (255, 255, 255) dynamic_text_tips = { 16: {'text': 'Health: %s' % self.__tank_player1.health}, 17: {'text': 'Level: %s' % self.__tank_player1._level}, 23: {'text': 'Game Level: %s' % (TankGame().level + 1)}, 24: {'text': 'Remain Enemy: %s' % self.__total_enemy_num} } if self.__tank_player2: dynamic_text_tips[20] = {'text': 'Health: %s' % self.__tank_player2.health} dynamic_text_tips[21] = {'text': 'Level: %s' % self.__tank_player2._level} else: dynamic_text_tips[20] = {'text': 'Health: %s' % None} dynamic_text_tips[21] = {'text': 'Level: %s' % None} for pos, tip in dynamic_text_tips.items(): tip['render'] = self.__font.render(tip['text'], True, color_white) tip['rect'] = tip['render'].get_rect() tip['rect'].left, tip['rect'].top = self.__screen_width + 5, self.__screen_height * pos / 30 screen = TankGame().screen for pos, tip in self.__fix_text_tips.items(): screen.blit(tip['render'], tip['rect']) for pos, tip in dynamic_text_tips.items(): screen.blit(tip['render'], tip['rect']) def __load_level_file(self): self.__scene_elements = groups.SceneElementsGroup() elems_map = { 'B': SceneFactory.BRICK, 'I': SceneFactory.IRON, 'C': SceneFactory.ICE, 'T': SceneFactory.TREE, 'R': SceneFactory.RIVER_1 } home_walls_position = [] home_position = () f = open(TankGame().level_file, errors='ignore') num_row = -1 for line in f.readlines(): line = line.strip('\n') # 注释 if line.startswith('#') or (not line): continue # 敌方坦克总数量 elif line.startswith('%TOTALENEMYNUM'): self.__total_enemy_num = 15 + TankGame().level #int(line.split(':')[-1]) # 场上敌方坦克最大数量 elif line.startswith('%MAXENEMYNUM'): self.max_enemy_num = 15 + TankGame().level #int(line.split(':')[-1]) # 大本营位置 elif line.startswith('%HOMEPOS'): home_position = line.split(':')[-1] home_position = [ int(home_position.split(',')[0]), int(home_position.split(',')[1]) ] home_position = ( self.__border_len + home_position[0] * self.__grid_size, self.__border_len + home_position[1] * self.__grid_size ) # 大本营周围位置 elif line.startswith('%HOMEAROUNDPOS'): home_walls_position = line.split(':')[-1] home_walls_position = [ [ int(pos.split(',')[0]), int(pos.split(',')[1]) ] for pos in home_walls_position.split(' ') ] home_walls_position = [ ( self.__border_len + pos[0] * self.__grid_size, self.__border_len + pos[1] * self.__grid_size ) for pos in home_walls_position ] # 我方坦克初始位置 elif line.startswith('%PLAYERTANKPOS'): self.__player_spawn_point = line.split(':')[-1] self.__player_spawn_point = [ [ int(pos.split(',')[0]), int(pos.split(',')[1]) ] for pos in self.__player_spawn_point.split(' ') ] self.__player_spawn_point = [ ( self.__border_len + pos[0] * self.__grid_size, self.__border_len + pos[1] * self.__grid_size ) for pos in self.__player_spawn_point ] # 敌方坦克初始位置 elif line.startswith('%ENEMYTANKPOS'): self.__enemy_spawn_point = line.split(':')[-1] self.__enemy_spawn_point = [ [ int(pos.split(',')[0]), int(pos.split(',')[1]) ] for pos in self.__enemy_spawn_point.split(' ') ] self.__enemy_spawn_point = [ ( self.__border_len + pos[0] * self.__grid_size, self.__border_len + pos[1] * self.__grid_size ) for pos in self.__enemy_spawn_point ] # 地图元素 else: num_row += 1 for num_col, elem in enumerate(line.split(' ')): position = self.__border_len + num_col * self.__grid_size, self.__border_len + num_row * self.__grid_size scene_element = None if elem in elems_map: scene_element = self.__scene_factory.create_element(position, elems_map[elem]) elif elem == 'R': scene_element = self.__scene_factory.create_element( position, random.choice([SceneFactory.RIVER_1, SceneFactory.RIVER_2]) ) if scene_element is not None: self.__scene_elements.add(scene_element) self.__home = Home(position=home_position, imagefile=self.__home_images, walls_position=home_walls_position)