import pygame from settings import * from tile import Tile from player import Player from support import * from random import choice, randint from weapon import Weapon from ui import UI from enemy import Enemy from particles import AnimationPlayer from magic import MagicPlayer from upgrade import Upgrade class Level: def __init__(self): # 获取游戏窗口的显示表面(surface) self.display_surface = pygame.display.get_surface() # 标记游戏是否处于暂停状态 self.game_paused = False # 可见精灵组,用于管理在摄像机视野内可见的精灵 self.visible_sprites = YSortCameraGroup() # 障碍物精灵组,用于管理所有的障碍物精灵 self.obstacle_sprites = pygame.sprite.Group() # 当前攻击对象 self.current_attack = None # 攻击精灵组,用于管理所有的攻击精灵 self.attack_sprites = pygame.sprite.Group() # 可被攻击的精灵组,用于管理所有可以被攻击的精灵 self.attackable_sprites = pygame.sprite.Group() # 创建地图 self.create_map() # UI 对象,用于管理游戏界面的显示和交互 self.ui = UI() # 升级对象,与玩家角色相关联,用于管理升级和技能 self.upgrade = Upgrade(self.player) # 动画播放器对象,用于管理游戏中的动画效果 self.animation_player = AnimationPlayer() # 魔法播放器对象,与动画播放器相关联,用于管理游戏中的魔法效果 self.magic_player = MagicPlayer(self.animation_player) def create_map(self): # 定义不同类型的地图布局和图形资源路径 layouts = { 'boundary': import_csv_layout('../map/map_FloorBlocks.csv'), 'grass': import_csv_layout('../map/map_Grass.csv'), 'object': import_csv_layout('../map/map_Objects.csv'), 'entities': import_csv_layout('../map/map_Entities.csv') } graphics = { 'grass': import_folder('../graphics/Grass'), 'objects': import_folder('../graphics/objects') } # 遍历不同的地图布局和样式 for style,layout in layouts.items(): # 遍历地图布局的每一行 for row_index,row in enumerate(layout): # 遍历每行中的每个元素(列 for col_index, col in enumerate(row): # 如果元素不为'-1',表示有地图元素需要创建 if col != '-1': # 计算元素在屏幕上的位置(像素坐标) x = col_index * TILESIZE y = row_index * TILESIZE # 边界类型的地图元素 if style == 'boundary': # 创建障碍物精灵对象,设置为'invisible'类型(不可见) Tile((x,y),[self.obstacle_sprites],'invisible') # 草地类型的地图元素 if style == 'grass': # 从草地图形资源中随机选择一个图像 random_grass_image = choice(graphics['grass']) # 创建草地精灵对象,加入可见精灵组、障碍物精灵组和可攻击精灵组 Tile( (x,y), [self.visible_sprites,self.obstacle_sprites,self.attackable_sprites], 'grass', random_grass_image) # 物体类型的地图元素 if style == 'object': # 根据列数获取物体图形资源,创建物体精灵对象 surf = graphics['objects'][int(col)] Tile((x,y),[self.visible_sprites,self.obstacle_sprites],'object',surf) # 实体类型的地图元素(角色和敌人) if style == 'entities': # 玩家角色 if col == '394': # 创建玩家对象,并加入可见精灵组 self.player = Player( (x,y), [self.visible_sprites], self.obstacle_sprites, self.create_attack, self.destroy_attack, self.create_magic) # 敌人角色 else: # 根据不同的角色编号选择对应的怪物名称 if col == '390': monster_name = 'bamboo' elif col == '391': monster_name = 'spirit' elif col == '392': monster_name ='raccoon' else: monster_name = 'squid' # 创建敌人对象,并加入可见精灵组和可攻击精灵组 Enemy( monster_name, (x,y), [self.visible_sprites,self.attackable_sprites], self.obstacle_sprites, self.damage_player, self.trigger_death_particles, self.add_exp) # 创建武器对象,将其设为当前攻击对象 def create_attack(self): # 该方法用于创建武器对象,并将其设为当前攻击对象。 self.current_attack = Weapon(self.player,[self.visible_sprites,self.attack_sprites]) def create_magic(self,style,strength,cost): if style == 'heal': # 创建治疗魔法效果 self.magic_player.heal(self.player,strength,cost,[self.visible_sprites]) if style == 'flame': # 创建火焰魔法效果 self.magic_player.flame(self.player,cost,[self.visible_sprites,self.attack_sprites]) def destroy_attack(self): # 检查当前是否存在攻击对象 if self.current_attack: # 销毁当前攻击对象 self.current_attack.kill() # 将当前攻击对象设置为 None self.current_attack = None def player_attack_logic(self): # 检查是否存在攻击精灵 if self.attack_sprites: # 遍历每个攻击精灵 for attack_sprite in self.attack_sprites: # 检测当前攻击精灵与可攻击精灵组中的精灵是否发生碰撞 collision_sprites = pygame.sprite.spritecollide(attack_sprite,self.attackable_sprites,False) # 如果发生碰撞,遍历碰撞到的每个精灵 if collision_sprites: for target_sprite in collision_sprites: # 如果碰撞到的是草地精灵 if target_sprite.sprite_type == 'grass': # 获取草地精灵的中心位置和偏移量 pos = target_sprite.rect.center offset = pygame.math.Vector2(0,75) # 随机生成一定数量的草叶粒子效果 for leaf in range(randint(3,6)): self.animation_player.create_grass_particles(pos - offset,[self.visible_sprites]) # 销毁草地精灵 target_sprite.kill() # 如果碰撞到的是其他类型的精灵,执行造成伤害的操作 else: target_sprite.get_damage(self.player,attack_sprite.sprite_type) def damage_player(self,amount,attack_type): # 如果玩家处于可受伤状态 if self.player.vulnerable: # 减少玩家的健康值 self.player.health -= amount # 设置玩家为无敌状态 self.player.vulnerable = False # 记录玩家受伤的时间 self.player.hurt_time = pygame.time.get_ticks() # 创建攻击类型的粒子效果 self.animation_player.create_particles(attack_type,self.player.rect.center,[self.visible_sprites]) def trigger_death_particles(self,pos,particle_type): # 创建指定类型的死亡粒子效果 self.animation_player.create_particles(particle_type,pos,self.visible_sprites) def add_exp(self,amount): # 增加玩家的经验值 self.player.exp += amount def toggle_menu(self): # 切换游戏菜单状态(暂停或继续) self.game_paused = not self.game_paused def run(self): # 绘制可见精灵和玩家 self.visible_sprites.custom_draw(self.player) # 显示用户界面(UI) self.ui.display(self.player) # 如果游戏暂停,显示升级界面 if self.game_paused: self.upgrade.display() # 如果游戏未暂停,更新可见精灵组 else: self.visible_sprites.update() # 更新敌人精灵状态 self.visible_sprites.enemy_update(self.player) # 处理玩家攻击逻辑 self.player_attack_logic() class YSortCameraGroup(pygame.sprite.Group): def __init__(self): # 调用父类的初始化方法,初始化精灵组 super().__init__() # 获取当前 Pygame 显示的表面(屏幕) self.display_surface = pygame.display.get_surface() # 计算屏幕宽度的一半 self.half_width = self.display_surface.get_size()[0] // 2 # 计算屏幕高度的一半 self.half_height = self.display_surface.get_size()[1] // 2 # 创建一个空的 Vector2 对象,用于表示摄像机的偏移量 self.offset = pygame.math.Vector2() # 加载地板图像并转换为适合显示的格式 self.floor_surf = pygame.image.load('../graphics/tilemap/ground.png').convert() # 获取地板图像的矩形区域(左上角位置为 (0, 0)) self.floor_rect = self.floor_surf.get_rect(topleft = (0,0)) def custom_draw(self,player): # 获取摄像机偏移量 self.offset.x = player.rect.centerx - self.half_width self.offset.y = player.rect.centery - self.half_height # 绘制地板 floor_offset_pos = self.floor_rect.topleft - self.offset self.display_surface.blit(self.floor_surf,floor_offset_pos) # 按精灵的中心 Y 坐标排序并绘制精灵 for sprite in sorted(self.sprites(),key = lambda sprite: sprite.rect.centery): # 计算精灵在屏幕上的绘制位置 offset_pos = sprite.rect.topleft - self.offset # 在屏幕上绘制精灵图像 self.display_surface.blit(sprite.image,offset_pos) def enemy_update(self,player): # 从精灵组中筛选出所有的敌人精灵 enemy_sprites = [sprite for sprite in self.sprites() if hasattr(sprite,'sprite_type') and sprite.sprite_type == 'enemy'] # 遍历所有的敌人精灵 for enemy in enemy_sprites: # 调用每个敌人精灵的特定更新方法,传递玩家角色作为参数 enemy.enemy_update(player)