打boss动画
This commit is contained in:
parent
b3e6a3965b
commit
3c71ebb33a
217
wan.py
217
wan.py
@ -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):
|
||||
self.hp = max(0, self.hp - dmg)
|
||||
self.shake = True
|
||||
self.shake_frame = 10
|
||||
if self.hp > 0:
|
||||
self.hp = max(0, self.hp - dmg)
|
||||
self.shake = True
|
||||
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
|
||||
screen.blit(self.img, (x, y))
|
||||
draw_health_bar(screen, x, y - 30, 200, 20, self.hp, self.max_hp)
|
||||
|
||||
|
||||
if not self.dead or self.death_anim > 0:
|
||||
screen.blit(self.img, (x, y))
|
||||
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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user