from ..components import info import pygame from .. import tools, setup from .. import constants as C from .. components import player,stuff,brick,box,enemy import os import json class Level: def start(self,game_info): self.game_info = game_info self.finished = False self.next = 'game_over' self.info = info.Info('level',self.game_info) self.load_map_data() self.setup_background() self.setup_start_positions() self.setup_player() self.setup_ground_items() self.setup_bricks_and_boxes() self.setup_enemies() self.setup_checkpoints() def load_map_data(self): file_name = 'level_1.json' file_path = os.path.join('source/data/maps',file_name) with open(file_path) as f: self.map_data = json.load(f) def setup_background(self): self.image_name = self.map_data['image_name'] self.background = setup.GRAPHICS[self.image_name] rect = self.background.get_rect() self.background = pygame.transform.scale(self.background,(int(rect.width * C.BG_MULTI), int(rect.height * C.BG_MULTI))) self.background_rect = self.background.get_rect() self.game_window = setup.SCREEN.get_rect() self.game_ground = pygame.Surface((self.background_rect.width,self.background_rect.height)) def setup_start_positions(self): self.positions = [] for data in self.map_data['maps']: self.positions.append((data['start_x'],data['end_x'],data['player_x'],data['player_y'])) self.start_x,self.end_x,self.player_x,self.player_y = self.positions[0] def setup_player(self): self.player = player.Player('mario') self.player.rect.x = self.game_window.x + self.player_x self.player.rect.bottom = self.player_y def setup_ground_items(self): self.ground_items_group = pygame.sprite.Group() # 组存放多个精灵类,方便处理 for name in ['ground','pipe','step']: for item in self.map_data[name]: self.ground_items_group.add(stuff.Item(item['x'],item['y'],item['width'],item['height'],name)) def setup_bricks_and_boxes(self): self.brick_group = pygame.sprite.Group() self.box_group = pygame.sprite.Group() self.coin_group = pygame.sprite.Group() self.powerup_group = pygame.sprite.Group() if'brick' in self.map_data: for brick_data in self.map_data['brick']: # 遍历得到x,y坐标以及type x,y = brick_data['x'],brick_data['y'] brick_type = brick_data['type'] if brick_type == 0: if 'brick_num' in brick_data: #TODO Batch bricks pass else: self.brick_group.add(brick.Brick(x, y, brick_type,None)) elif brick_type == 1: self.brick_group.add(brick.Brick(x, y, brick_type, self.coin_group)) else: self.brick_group.add(brick.Brick(x, y, brick_type, self.powerup_group)) if 'box' in self.map_data: for box_data in self.map_data['box']: # 遍历得到x,y坐标以及type x, y = box_data['x'], box_data['y'] box_type = box_data['type'] if box_type == 1: self.box_group.add(box.Box(x, y, box_type, self.coin_group)) else: self.box_group.add(box.Box(x, y, box_type,self.powerup_group)) def setup_enemies(self): self.dying_group = pygame.sprite.Group() self.shell_group = pygame.sprite.Group() self.enemy_group = pygame.sprite.Group() # 初始化 self.enemy_group_dict = {} for enemy_group_data in self.map_data['enemy']: # 字典存放 group = pygame.sprite.Group() for enemy_group_id,enemy_list in enemy_group_data.items(): for enemy_data in enemy_list: group.add(enemy.create_enemy(enemy_data)) # 调用 ce self.enemy_group_dict[enemy_group_id] = group def setup_checkpoints(self): self.checkpoint_group = pygame.sprite.Group() for item in self.map_data['checkpoint']: x,y,w,h = item['x'],item['y'],item['width'],item['height'] checkpoint_type = item['type'] enemy_groupid = item.get('enemy_groupid') self.checkpoint_group.add(stuff.Checkpoint(x,y,w,h,checkpoint_type,enemy_groupid)) def update(self, surface, keys): self.current_time = pygame.time.get_ticks() self.player.update(keys) if self.player.dead: if self.current_time - self.player.death_timer > 3000: self.finished = True self.update_game_info() else: self.update_player_position() self.check_checkpoints() self.check_if_go_die() self.update_game_window() self.info.update() self.brick_group.update() self.box_group.update() self.enemy_group.update(self) self.dying_group.update(self) self.shell_group.update(self) self.coin_group.update() self.box_group.update() self.draw(surface) def update_player_position(self): # x判定 self.player.rect.x += self.player.x_vel if self.player.rect.x < self.start_x: self.player.rect.x = self.start_x elif self.player.rect.right > self.end_x: self.player.rect.right = self.end_x self.check_x_collisions() # y判定 if not self.player.dead: self.player.rect.y += self.player.y_vel self.check_y_collisions() def check_x_collisions(self): # x方向 check_group = pygame.sprite.Group(self.ground_items_group,self.brick_group,self.box_group) collided_sprite = pygame.sprite.spritecollideany(self.player,check_group) # 检测碰撞 if collided_sprite: self.adjust_player_x(collided_sprite) enemy = pygame.sprite.spritecollideany(self.player, self.enemy_group) if enemy: self.player.go_die() shell = pygame.sprite.spritecollideany(self.player, self.shell_group) # 碰撞检测 if shell: if shell.state == 'slide': self.player.go_die() else: if self.player.rect.x < shell.rect.x: # 龟壳弹开方向以及速度 shell.x_vel = 10 shell.rect.x += 40 shell.direction = 1 else: shell.x_vel = -10 shell.rect.x -= 40 shell.direction = 0 shell.state = 'slide' def check_y_collisions(self): # y方向 ground_item = pygame.sprite.spritecollideany(self.player,self.ground_items_group) brick = pygame.sprite.spritecollideany(self.player, self.brick_group) box = pygame.sprite.spritecollideany(self.player, self.box_group) enemy = pygame.sprite.spritecollideany(self.player, self.enemy_group) if brick and box: # 判断离哪个砖块进哪个顶起 to_brick = abs(self.player.rect.centerx - brick.rect.centerx) to_box = abs(self.player.rect.centerx - box.rect.centerx) if to_brick > to_box: brick = None else: box = None if ground_item: self.adjust_player_y(ground_item) elif brick: self.adjust_player_y(brick) elif box: self.adjust_player_y(box) elif enemy: self.enemy_group.remove(enemy) if enemy.name == 'koopa': self.shell_group.add(enemy) # 龟壳状态进龟壳组 else: self.dying_group.add(enemy) # 由敌人组 → 死亡组 if self.player.y_vel < 0: how = 'bumped' else: how = 'trampled' self.player.state = 'jump' self.player.rect.bottom = enemy.rect.top self.player.y_vel = self.player.jump_vel * 0.8 # 力道 enemy.go_die(how) self.check_will_fall(self.player) # 坠落检测 def adjust_player_x(self,sprite): if self.player.rect.x < sprite.rect.x: self.player.rect.right = sprite.rect.left # 重置位置为最左边 else: self.player.rect.left = sprite.rect.right self.player.x_vel = 0 def adjust_player_y(self,sprite): if self.player.rect.bottom < sprite.rect.bottom: self.player.y_vel = 0 self.player.rect.bottom = sprite.rect.top self.player.state = 'walk' else: self.player.y_vel = 7 self.player.rect.top = sprite.rect.bottom self.player.state = 'fall' if sprite.name == 'box': if sprite.state == 'rest': sprite.go_bumped() if sprite.name == 'brick': if sprite.state == 'rest': sprite.go_bumped() def check_will_fall(self,sprite): sprite.rect.y += 1 # 下落一个像素 check_group = pygame.sprite.Group(self.ground_items_group,self.brick_group,self.box_group) # 检测是否碰撞 collided_sprite = pygame.sprite.spritecollideany(sprite,check_group) if not collided_sprite and sprite.state != 'jump': # 如果没有或者处于跳起则不坠落 sprite.state = 'fall' sprite.rect.y -= 1 def update_game_window(self): third = self.game_window.x + self.game_window.width/3 if self.player.x_vel > 0 and self.player.rect.centerx > third and self.game_window.right < self.end_x: # 判断移动 self.game_window.x += self.player.x_vel # 用窗口更新代替主角移动 self.start_x = self.game_window.x def draw(self, surface): self.game_ground.blit(self.background,self.game_window,self.game_window) self.game_ground.blit(self.player.image,self.player.rect) # 将背景与人物化境 self.brick_group.draw(self.game_ground) self.box_group.draw(self.game_ground) self.enemy_group.draw(self.game_ground) self.dying_group.draw(self.game_ground) self.shell_group.draw(self.game_ground) self.coin_group.draw(self.game_ground) self.powerup_group.draw(self.game_ground) surface.blit(self.game_ground,(0,0), self.game_window) # 渲染 self.info.draw(surface) def check_checkpoints(self): checkpoint = pygame.sprite.spritecollideany(self.player,self.checkpoint_group) if checkpoint: if checkpoint.checkpoint_type == 0: # checkpoint for enemy appearance # 如果检查点类型为0 释放对应敌人 self.enemy_group.add(self.enemy_group_dict[str(checkpoint.enemy_groupid)]) # 一旦某个点被触发就把对应敌人放入此组 checkpoint.kill() # 被触碰后消失 def check_if_go_die(self): if self.player.rect.y > C.SCREEN_H: self.player.go_die() # 判断是否掉落屏幕外 def update_game_info(self): if self.player.dead: self.game_info['lives'] -= 1 if self.game_info['lives'] == 0: self.next = 'game_over' else: self.next = 'load_screen'