import pygame from settings import * from entity import Entity from support import * class Enemy(Entity): def __init__(self,monster_name,pos,groups,obstacle_sprites,damage_player,trigger_death_particles,add_exp): # general setup # 调用父类的初始化方法,将实例添加到指定的分组中 super().__init__(groups) # 设置角色的精灵类型为 'enemy' self.sprite_type = 'enemy' # graphics setup # 导入指定怪物名称的图像资源 self.import_graphics(monster_name) # 设置角色的状态为 'idle'(空闲状态) self.status = 'idle' # 获取角色当前状态的图像 self.image = self.animations[self.status][self.frame_index] # movement # 设置角色的位置 self.rect = self.image.get_rect(topleft = pos) # 设置角色的碰撞框,稍微向上收缩一些 self.hitbox = self.rect.inflate(0,-10) # 设置角色所在的障碍物精灵组 self.obstacle_sprites = obstacle_sprites # stats 将怪物的各种值赋予 self.monster_name = monster_name # 从怪物数据字典中获取指定怪物的信息 monster_info = monster_data[self.monster_name] # 怪物的生命值 self.health = monster_info['health'] # 击败怪物后获得的经验值 self.exp = monster_info['exp'] # 怪物的移动速度 self.speed = monster_info['speed'] # 怪物的攻击伤害 self.attack_damage = monster_info['damage'] # 怪物的抵抗力 self.resistance = monster_info['resistance'] # 怪物的攻击范围 self.attack_radius = monster_info['attack_radius'] # 怪物的发现玩家范围 self.notice_radius = monster_info['notice_radius'] # 怪物的攻击类型 self.attack_type = monster_info['attack_type'] # player interaction 设置角色与玩家交互相关的属性和方法 # 角色是否能够进行攻击 self.can_attack = True # 记录上次攻击的时间 self.attack_time = None # 攻击的冷却时间(毫秒) self.attack_cooldown = 400 # 对玩家造成伤害的方法 self.damage_player = damage_player # 触发角色死亡特效的方法 self.trigger_death_particles = trigger_death_particles # 增加玩家经验值的方法 self.add_exp = add_exp # invincibility timer 设置角色的无敌时间相关属性 # 角色是否处于易受伤状态 self.vulnerable = True # 记录角色受到攻击的时间点 self.hit_time = None # 角色的无敌持续时间(毫秒) self.invincibility_duration = 300 # sounds 设置角色相关的音效 # 死亡音效 self.death_sound = pygame.mixer.Sound('../audio/death.wav') # 受伤音效 self.hit_sound = pygame.mixer.Sound('../audio/hit.wav') # 攻击音效 self.attack_sound = pygame.mixer.Sound(monster_info['attack_sound']) # 设置死亡音效的音量 self.death_sound.set_volume(0.6) # 设置受伤音效的音量 self.hit_sound.set_volume(0.6) # 设置攻击音效的音量 self.attack_sound.set_volume(0.6) def import_graphics(self,name): # 初始化动画字典,包含三种动画状态:idle(空闲)、move(移动)、attack(攻击) self.animations = {'idle':[],'move':[],'attack':[]} # 指定敌人角色图形资源的主路径,根据角色名称构建路径 main_path = f'../graphics/monsters/{name}/' # 调用 import_folder 函数导入指定路径下的图像资源,并将结果存储到动画字典中对应状态的键中 for animation in self.animations.keys(): self.animations[animation] = import_folder(main_path + animation) def get_player_distance_direction(self,player): # 获取敌人角色和玩家角色的中心点坐标作为向量 enemy_vec = pygame.math.Vector2(self.rect.center) player_vec = pygame.math.Vector2(player.rect.center) # 计算敌人角色与玩家角色之间的距离 distance = (player_vec - enemy_vec).magnitude() # 如果距离大于 0,则计算方向向量 if distance > 0: # 计算方向向量并归一化 direction = (player_vec - enemy_vec).normalize() # 距离为 0 或负数时,方向向量为零向量 else: direction = pygame.math.Vector2() # 返回距离和方向向量的元组 return (distance,direction) def get_status(self, player): # 获取敌人角色与玩家角色的距离 distance = self.get_player_distance_direction(player)[0] # 根据距离和设定的攻击范围、发现范围确定敌人角色的状态 if distance <= self.attack_radius and self.can_attack: # 如果距离小于等于攻击范围并且可以进行攻击 if self.status != 'attack': # 重置动画帧索引 self.frame_index = 0 # 设置状态为攻击 self.status = 'attack' # 如果距离小于等于发现范围 elif distance <= self.notice_radius: # 设置状态为移动 self.status = 'move' # 距离大于发现范围 else: # 设置状态为空闲(静止) self.status = 'idle' def actions(self,player): # 如果状态为攻击 if self.status == 'attack': # 记录攻击时间 self.attack_time = pygame.time.get_ticks() # 对玩家造成伤害 self.damage_player(self.attack_damage,self.attack_type) # 播放攻击音效 self.attack_sound.play() # 如果状态为移动 elif self.status == 'move': # 获取朝向玩家的方向向量 self.direction = self.get_player_distance_direction(player)[1] # 其他情况(状态为空闲或其他) else: # 将方向向量设为零向量,即停止移动 self.direction = pygame.math.Vector2() def animate(self): # 获取当前状态对应的动画帧列表 animation = self.animations[self.status] # 更新帧索引,控制动画播放速度 self.frame_index += self.animation_speed # 如果帧索引超过动画帧列表的长度 if self.frame_index >= len(animation): # 如果是攻击状态,设置为不可攻击状态 if self.status == 'attack': self.can_attack = False # 重置帧索引,从头开始播放动画 self.frame_index = 0 # 更新角色的图像为当前帧的图像 self.image = animation[int(self.frame_index)] # 更新角色的矩形位置 self.rect = self.image.get_rect(center = self.hitbox.center) # 根据角色的易受伤状态设置图像的透明度 if not self.vulnerable: # 调用自定义的方法获取波形值,用于设置透明度 alpha = self.wave_value() # 设置图像的透明度 self.image.set_alpha(alpha) # 如果角色不是易受伤状态,则设置完全不透明(透明度为 255) else: self.image.set_alpha(255) # 处理敌人角色的攻击冷却和无敌状态的持续时间。 def cooldowns(self): # 获取当前时间(以毫秒为单位) current_time = pygame.time.get_ticks() # 处理攻击冷却时间 # 如果当前不能攻击(即处于攻击冷却中) if not self.can_attack: # 如果距离上次攻击时间已经超过攻击冷却时间 if current_time - self.attack_time >= self.attack_cooldown: # 设置为可以再次攻击 self.can_attack = True # 处理无敌状态持续时间 # 如果当前不可受伤(即处于无敌状态) if not self.vulnerable: # 如果距离上次受伤时间已经超过无敌状态持续时间 if current_time - self.hit_time >= self.invincibility_duration: # 设置为可受伤状态(结束无敌状态) self.vulnerable = True # 处理敌人角色受到玩家攻击造成的伤害。 def get_damage(self,player,attack_type): if self.vulnerable: # 播放受击音效 self.hit_sound.play() # 设置受击后的朝向玩家的方向向量 self.direction = self.get_player_distance_direction(player)[1] # 根据攻击类型应用不同的伤害值 if attack_type == 'weapon': # 玩家使用武器攻击造成伤害 self.health -= player.get_full_weapon_damage() # 玩家使用魔法攻击造成伤害 else: self.health -= player.get_full_magic_damage() # 记录受击时间 self.hit_time = pygame.time.get_ticks() # 设置为不可受伤状态,进入无敌状态 self.vulnerable = False #检查角色是否死亡 def check_death(self): # 如果敌人角色的生命值小于等于 0,表示角色已经死亡 if self.health <= 0: # 从精灵组中移除角色,通常用于删除角色或停止其更新和渲染 self.kill() # 触发死亡粒子效果,通常用于播放死亡动画或粒子效果 self.trigger_death_particles(self.rect.center,self.monster_name) # 增加经验值 self.add_exp(self.exp) # 播放死亡音效,提供游戏中的音效反馈 self.death_sound.play() #被攻击后的反应 def hit_reaction(self): # 如果敌人角色不可受伤 if not self.vulnerable: # 则将角色的移动方向向量乘以负阻力,模拟受击后的击退效果。 self.direction *= -self.resistance #更新状态 def update(self): # 处理受击反应 self.hit_reaction() # 移动角色,传入速度参数 self.move(self.speed) # 更新角色动画 self.animate() # 处理攻击冷却和无敌状态持续时间 self.cooldowns() # 检查角色是否死亡并执行死亡逻辑 self.check_death() def enemy_update(self,player): # 获取敌人角色的状态 self.get_status(player) # 执行敌人角色的行为 self.actions(player)