打boss动画

This commit is contained in:
Guanforever 2025-06-30 19:27:31 +08:00
parent b3e6a3965b
commit 3c71ebb33a
5 changed files with 186 additions and 31 deletions

BIN
cast.wav Normal file

Binary file not shown.

BIN
groan.wav Normal file

Binary file not shown.

BIN
hit.wav Normal file

Binary file not shown.

211
wan.py
View File

@ -1,100 +1,233 @@
import pygame
import sys
import time
import random
import math
# 配置
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600
SCREEN_WIDTH, SCREEN_HEIGHT = 900, 600
BOSS_IMG = 'boss.png' # 替换为你的boss图片路径
SKILL_IMG = 'skill.png' # 替换为你的技能图片路径
FPS = 60
HIT_SOUND = 'hit.wav' # 技能击中boss
CAST_SOUND = 'cast.wav' # 技能释放
WIN_SOUND = 'win.wav' # 胜利
GROAN_SOUND = 'groan.wav' # 怪物呻吟
# 技能颜色和名称
SKILL_COLORS = [(255, 80, 80), (80, 180, 255), (120, 255, 120), (255, 220, 80), (180, 80, 255)]
SKILL_NAMES = ["火球", "冰锥", "风刃", "雷击", "暗影"]
class Particle:
def __init__(self, x, y, color, shadow=False, glow=False):
self.x = x + random.randint(-10, 10)
self.y = y + random.randint(-10, 10)
self.radius = random.randint(2, 5)
self.color = color
self.life = random.randint(15, 30)
self.vx = random.uniform(-1.2, 1.2)
self.vy = random.uniform(-1.2, 1.2)
self.shadow = shadow
self.glow = glow
def update(self):
self.x += self.vx
self.y += self.vy
self.life -= 1
self.radius = max(0, self.radius - 0.08)
def draw(self, screen):
if self.life > 0 and self.radius > 0:
if self.shadow:
s = pygame.Surface((int(self.radius*4), int(self.radius*2)), pygame.SRCALPHA)
pygame.draw.ellipse(s, (30,30,30,80), (0,0,int(self.radius*4),int(self.radius*2)))
screen.blit(s, (int(self.x-self.radius*2), int(self.y+self.radius)))
if self.glow:
s = pygame.Surface((int(self.radius*4), int(self.radius*4)), pygame.SRCALPHA)
pygame.draw.circle(s, (*self.color, 60), (int(self.radius*2), int(self.radius*2)), int(self.radius*2))
screen.blit(s, (int(self.x-self.radius), int(self.y-self.radius)))
pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), int(self.radius))
def draw_gradient_bg(screen):
for i in range(SCREEN_HEIGHT):
color = (
220 - i // 8,
220 - i // 16,
255 - i // 12
)
pygame.draw.line(screen, color, (0, i), (SCREEN_WIDTH, i))
# 震动参数
def shake_pos(x, y, frame):
if frame % 2 == 0:
return x + 5, y
return x + 8, y + random.randint(-3, 3)
else:
return x - 5, y
return x - 8, y + random.randint(-3, 3)
def draw_health_bar(screen, x, y, w, h, hp, max_hp):
pygame.draw.rect(screen, (180, 0, 0), (x, y, w, h))
pygame.draw.rect(screen, (0, 220, 0), (x, y, int(w * hp / max_hp), h))
pygame.draw.rect(screen, (0, 0, 0), (x, y, w, h), 2)
pygame.draw.rect(screen, (180, 0, 0), (x, y, w, h), border_radius=8)
pygame.draw.rect(screen, (0, 220, 0), (x, y, int(w * hp / max_hp), h), border_radius=8)
pygame.draw.rect(screen, (0, 0, 0), (x, y, w, h), 2, border_radius=8)
def draw_skill_cooldown(screen, x, y, w, h, cooldown, max_cd):
pygame.draw.rect(screen, (80, 80, 80), (x, y, w, h), border_radius=6)
if cooldown > 0:
pygame.draw.rect(screen, (120, 120, 255), (x, y, int(w * cooldown / max_cd), h), border_radius=6)
class Skill:
def __init__(self, idx, dmg, img):
self.idx = idx
self.dmg = dmg
self.img = img
self.x = 0
self.y = SCREEN_HEIGHT // 2
self.x = 60
self.y = SCREEN_HEIGHT // 2 + random.randint(-60, 60)
self.active = True
self.color = SKILL_COLORS[idx % len(SKILL_COLORS)]
self.angle = random.uniform(-0.2, 0.2)
self.trail = []
self.shadow = True
self.glow = True
self.speed = 7 + self.idx * 0.7 # 更慢
self.cast_sound_played = False
def update(self):
self.x += 20
self.trail.append((self.x, self.y))
if len(self.trail) > 12:
self.trail.pop(0)
self.x += self.speed
self.y += math.sin(self.x / 40) * 3 + self.angle * 8
if self.x > SCREEN_WIDTH:
self.active = False
def draw(self, screen):
# 阴影
s = pygame.Surface((self.img.get_width(), self.img.get_height()), pygame.SRCALPHA)
pygame.draw.ellipse(s, (30,30,30,80), (0, self.img.get_height()//2, self.img.get_width(), self.img.get_height()//2))
screen.blit(s, (self.x, self.y+self.img.get_height()//2))
# 光晕
s2 = pygame.Surface((self.img.get_width()*2, self.img.get_height()*2), pygame.SRCALPHA)
pygame.draw.circle(s2, (*self.color, 60), (self.img.get_width(), self.img.get_height()), self.img.get_width())
screen.blit(s2, (self.x-self.img.get_width()//2, self.y-self.img.get_height()//2))
# 轨迹
for i, (tx, ty) in enumerate(self.trail):
alpha = int(255 * (i + 1) / len(self.trail))
s = pygame.Surface((self.img.get_width(), self.img.get_height()), pygame.SRCALPHA)
s.fill((*self.color, alpha // 2))
screen.blit(s, (tx, ty))
screen.blit(self.img, (self.x, self.y))
class Boss:
def __init__(self, img, hp):
def __init__(self, img, hp, groan_sound=None):
self.img = img
self.hp = hp
self.max_hp = hp
self.x = SCREEN_WIDTH - img.get_width() - 50
self.x = SCREEN_WIDTH - img.get_width() - 80
self.y = SCREEN_HEIGHT // 2 - img.get_height() // 2
self.shake_frame = 0
self.shake = False
self.dead = False
self.death_anim = 0
self.particles = []
self.groan_sound = groan_sound
self.groan_cooldown = 0
def hit(self, dmg):
if self.hp > 0:
self.hp = max(0, self.hp - dmg)
self.shake = True
self.shake_frame = 10
self.shake_frame = 12
# 受击粒子
for _ in range(16):
self.particles.append(Particle(self.x + self.img.get_width() // 2, self.y + self.img.get_height() // 2, (255, 80, 80), shadow=True, glow=True))
if self.groan_sound and self.groan_cooldown == 0:
self.groan_sound.play()
self.groan_cooldown = 30
if self.hp == 0:
self.dead = True
self.death_anim = 60
def update(self):
if self.shake:
self.shake_frame -= 1
if self.shake_frame <= 0:
self.shake = False
if self.groan_cooldown > 0:
self.groan_cooldown -= 1
# 死亡动画
if self.dead and self.death_anim > 0:
self.death_anim -= 1
for _ in range(8):
self.particles.append(Particle(self.x + self.img.get_width() // 2, self.y + self.img.get_height() // 2, (80, 80, 80), shadow=True, glow=True))
# 更新粒子
for p in self.particles:
p.update()
self.particles = [p for p in self.particles if p.life > 0]
def draw(self, screen):
# 投影
s = pygame.Surface((self.img.get_width()*2, self.img.get_height()), pygame.SRCALPHA)
pygame.draw.ellipse(s, (30,30,30,90), (0, self.img.get_height()//2, self.img.get_width()*2, self.img.get_height()//2))
screen.blit(s, (self.x-self.img.get_width()//2, self.y+self.img.get_height()-10))
# 光晕
s2 = pygame.Surface((self.img.get_width()*2, self.img.get_height()*2), pygame.SRCALPHA)
pygame.draw.circle(s2, (200,200,255,60), (self.img.get_width(), self.img.get_height()), self.img.get_width())
screen.blit(s2, (self.x-self.img.get_width()//2, self.y-self.img.get_height()//2))
if self.shake:
x, y = shake_pos(self.x, self.y, self.shake_frame)
else:
x, y = self.x, self.y
if not self.dead or self.death_anim > 0:
screen.blit(self.img, (x, y))
draw_health_bar(screen, x, y - 30, 200, 20, self.hp, self.max_hp)
draw_health_bar(screen, x, y - 40, 240, 24, self.hp, self.max_hp)
for p in self.particles:
p.draw(screen)
def main(boss_hp, skill_seq):
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('自动打Boss')
pygame.display.set_caption('自动打Boss - 3D炫酷升级版')
clock = pygame.time.Clock()
boss_img = pygame.image.load(BOSS_IMG).convert_alpha()
skill_img = pygame.image.load(SKILL_IMG).convert_alpha()
boss = Boss(boss_img, boss_hp)
# 音效
try:
hit_sound = pygame.mixer.Sound(HIT_SOUND)
cast_sound = pygame.mixer.Sound(CAST_SOUND)
win_sound = pygame.mixer.Sound(WIN_SOUND)
groan_sound = pygame.mixer.Sound(GROAN_SOUND)
except Exception:
hit_sound = cast_sound = win_sound = groan_sound = None
boss = Boss(boss_img, boss_hp, groan_sound=groan_sound)
skills = []
skill_idx = 0
running = True
skill_cooldown = 0
font = pygame.font.SysFont(None, 36)
max_cd = 40 # 冷却更长
font = pygame.font.SysFont('SimHei', 36)
big_font = pygame.font.SysFont('SimHei', 60)
particles = []
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((220, 220, 255))
draw_gradient_bg(screen)
# 随机背景粒子
if random.random() < 0.18:
particles.append(Particle(random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT), (200, 200, 255), shadow=True, glow=True))
for p in particles:
p.update()
p.draw(screen)
particles = [p for p in particles if p.life > 0]
boss.update()
boss.draw(screen)
# 技能释放
if skill_idx < len(skill_seq) and skill_cooldown == 0:
if skill_idx < len(skill_seq) and skill_cooldown == 0 and not boss.dead:
s = skill_seq[skill_idx]
skills.append(Skill(s['first'], s['second'], skill_img))
skill_cooldown = 30 # 帧数间隔
if cast_sound:
cast_sound.play()
skill_cooldown = max_cd
skill_idx += 1
if skill_cooldown > 0:
skill_cooldown -= 1
@ -105,25 +238,46 @@ def main(boss_hp, skill_seq):
skill.update()
skill.draw(screen)
# 判断是否击中boss
if skill.x + skill.img.get_width() > boss.x and boss.hp > 0:
if skill.x + skill.img.get_width() > boss.x and boss.hp > 0 and not boss.dead:
boss.hit(skill.dmg)
# 爆炸粒子
for _ in range(24):
particles.append(Particle(boss.x+boss.img.get_width()//2, boss.y+boss.img.get_height()//2, random.choice(SKILL_COLORS), shadow=True, glow=True))
if hit_sound:
hit_sound.play()
skill.active = False
# 移除无效技能
skills = [s for s in skills if s.active]
# 技能冷却条和技能名
if skill_idx < len(skill_seq):
s = skill_seq[skill_idx]
skill_name = SKILL_NAMES[s['first'] % len(SKILL_NAMES)]
name_text = font.render(f"下一个技能:{skill_name}", True, SKILL_COLORS[s['first'] % len(SKILL_COLORS)])
screen.blit(name_text, (60, SCREEN_HEIGHT - 80))
draw_skill_cooldown(screen, 60, SCREEN_HEIGHT - 40, 200, 18, skill_cooldown, max_cd)
# 显示血量数字
hp_text = font.render(f'Boss HP: {boss.hp}/{boss.max_hp}', True, (0,0,0))
screen.blit(hp_text, (50, 30))
screen.blit(hp_text, (60, 30))
# Boss死亡动画
if boss.dead and boss.death_anim == 0:
win_text = big_font.render("胜利!", True, (255, 120, 80))
screen.blit(win_text, (SCREEN_WIDTH // 2 - 80, SCREEN_HEIGHT // 2 - 60))
if win_sound:
win_sound.play()
pygame.display.flip()
clock.tick(FPS)
if boss.hp <= 0 and not skills:
time.sleep(1)
if boss.hp <= 0 and not skills and boss.death_anim == 0:
time.sleep(1.5)
running = False
pygame.quit()
if __name__ == '__main__':
# 示例boss血量1000技能序列
if __name__ == "__main__":
# 示例参数boss血量1000技能序列
boss_hp = 1000
skill_seq = [
{'first': 0, 'second': 120},
@ -133,3 +287,4 @@ if __name__ == '__main__':
{'first': 4, 'second': 250},
]
main(boss_hp, skill_seq)

BIN
win.wav Normal file

Binary file not shown.