python---新年烟花

目录

效果演示

2026新年快乐烟花演示

代码示例

bash 复制代码
# V1.0 初始版本,实现放烟花
# V1.1 优化代码结构,增加粒子类,优化粒子效果
# V1.2 优化上一版本中卡顿问题
# V1.3 增加音效
import pygame
import random
import math
import sys
import colorsys
from typing import List, Tuple
import time
import os

# 初始化pygame和音频
pygame.init()
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)

# 设置窗口尺寸
WIDTH, HEIGHT = 1200, 800
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("新年快乐!2026绚烂烟花秀 - 带音效版")

# 颜色定义
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

# 预生成颜色表,避免重复计算
COLOR_CACHE = {}
def get_color(hue: float, saturation: float = 0.9, value: float = 0.9) -> Tuple[int, int, int]:
    """缓存的颜色生成函数"""
    key = (int(hue * 1000), int(saturation * 100), int(value * 100))
    if key not in COLOR_CACHE:
        rgb = colorsys.hsv_to_rgb(hue, saturation, value)
        COLOR_CACHE[key] = (int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255))
    return COLOR_CACHE[key]

# 预生成颜色列表
COLORS = [get_color(i / 20) for i in range(20)]  # 减少到20种颜色

# 特殊效果颜色(预定义,避免运行时生成)
SPECIAL_COLORS = [
    (255, 215, 0),   # 金色
    (192, 192, 192), # 银色
    (255, 20, 147),  # 深粉色
    (0, 255, 255),   # 青色
    (255, 105, 180), # 热粉色
]

# 数学函数预计算和缓存
SIN_CACHE = {}
COS_CACHE = {}

def fast_sin(angle: float) -> float:
    """缓存的sin函数"""
    key = int(angle * 1000)
    if key not in SIN_CACHE:
        SIN_CACHE[key] = math.sin(angle)
    return SIN_CACHE[key]

def fast_cos(angle: float) -> float:
    """缓存的cos函数"""
    key = int(angle * 1000)
    if key not in COS_CACHE:
        COS_CACHE[key] = math.cos(angle)
    return COS_CACHE[key]

# 预计算的PI值
PI2 = math.pi * 2

# 声音管理器类
class SoundManager:
    def __init__(self):
        self.sounds = {}
        self.volume = 0.7
        
        # 尝试加载声音文件
        try:
            # 这里我们可以使用pygame内置生成声音,或者加载文件
            self.generate_sounds()
        except Exception as e:
            print(f"声音初始化失败: {e}")
    
    def generate_sounds(self):
        """生成烟花声音效果"""
        # 1. 发射声音(上升音调)
        self.sounds['launch'] = []
        for i in range(3):
            sound = self.create_launch_sound(pitch_factor=0.8 + i * 0.1)
            self.sounds['launch'].append(sound)
        
        # 2. 爆炸声音(不同大小)
        self.sounds['explosion_small'] = []
        self.sounds['explosion_medium'] = []
        self.sounds['explosion_large'] = []
        
        for i in range(4):
            # 小爆炸
            sound = self.create_explosion_sound(duration=0.5, pitch=0.7 + i * 0.1)
            self.sounds['explosion_small'].append(sound)
            
            # 中爆炸
            sound = self.create_explosion_sound(duration=0.8, pitch=0.5 + i * 0.1)
            self.sounds['explosion_medium'].append(sound)
            
            # 大爆炸
            sound = self.create_explosion_sound(duration=1.2, pitch=0.3 + i * 0.1)
            self.sounds['explosion_large'].append(sound)
        
        # 3. 火花声音(细小爆裂声)
        self.sounds['sparkle'] = []
        for i in range(5):
            sound = self.create_sparkle_sound()
            self.sounds['sparkle'].append(sound)
        
        # 4. 特殊效果声音
        self.sounds['special'] = []
        for i in range(3):
            sound = self.create_special_sound()
            self.sounds['special'].append(sound)
    
    def create_launch_sound(self, pitch_factor=1.0):
        """创建发射声音"""
        duration = 0.3  # 秒
        sample_rate = 22050
        samples = int(duration * sample_rate)
        
        sound_array = []
        for i in range(samples):
            # 上升音调的正弦波
            t = i / sample_rate
            freq = 200 + 400 * t * pitch_factor  # 频率从200Hz上升到600Hz
            value = math.sin(2 * math.pi * freq * t) * (1 - t/duration)  # 音量衰减
            sound_array.append([int(value * 32767 * 0.3), int(value * 32767 * 0.3)])
        
        sound = pygame.sndarray.make_sound(pygame.sndarray.array(sound_array))
        sound.set_volume(self.volume * 0.5)
        return sound
    
    def create_explosion_sound(self, duration=1.0, pitch=1.0):
        """创建爆炸声音"""
        sample_rate = 22050
        samples = int(duration * sample_rate)
        
        sound_array = []
        for i in range(samples):
            t = i / sample_rate
            
            # 基础频率
            base_freq = 80 * pitch
            
            # 多个频率叠加,创造丰富的爆炸声
            value = 0
            for freq_mul in [1.0, 1.5, 2.0, 3.0, 4.5]:
                freq = base_freq * freq_mul
                value += (math.sin(2 * math.pi * freq * t) / freq_mul) * math.exp(-t * 3)
            
            # 添加一些噪声
            noise = (random.random() - 0.5) * 0.2
            
            # 音量包络:快速上升,缓慢衰减
            envelope = min(t * 10, 1) * math.exp(-t * 2)
            
            sample_value = int((value * 0.7 + noise * 0.3) * envelope * 32767)
            sound_array.append([sample_value, sample_value])
        
        sound = pygame.sndarray.make_sound(pygame.sndarray.array(sound_array))
        sound.set_volume(self.volume)
        return sound
    
    def create_sparkle_sound(self):
        """创建火花声音"""
        duration = 0.1
        sample_rate = 22050
        samples = int(duration * sample_rate)
        
        sound_array = []
        for i in range(samples):
            t = i / sample_rate
            freq = 2000 + random.random() * 2000
            value = math.sin(2 * math.pi * freq * t) * math.exp(-t * 20)
            sample_value = int(value * 32767 * 0.2)
            sound_array.append([sample_value, sample_value])
        
        sound = pygame.sndarray.make_sound(pygame.sndarray.array(sound_array))
        sound.set_volume(self.volume * 0.3)
        return sound
    
    def create_special_sound(self):
        """创建特殊效果声音"""
        duration = 1.5
        sample_rate = 22050
        samples = int(duration * sample_rate)
        
        sound_array = []
        for i in range(samples):
            t = i / sample_rate
            
            # 创造"嘶嘶"上升效果
            freq = 100 + 500 * t
            value1 = math.sin(2 * math.pi * freq * t) * 0.5
            
            # 添加高频成分
            value2 = math.sin(2 * math.pi * freq * 3 * t) * 0.3 * math.exp(-t * 2)
            
            # 最终混合
            envelope = math.exp(-t * 1.5)
            sample_value = int((value1 + value2) * envelope * 32767 * 0.4)
            sound_array.append([sample_value, sample_value])
        
        sound = pygame.sndarray.make_sound(pygame.sndarray.array(sound_array))
        sound.set_volume(self.volume * 0.4)
        return sound
    
    def play_launch(self):
        """播放发射声音"""
        if self.sounds.get('launch'):
            sound = random.choice(self.sounds['launch'])
            sound.play()
    
    def play_explosion(self, size_type="medium"):
        """播放爆炸声音"""
        if self.sounds.get(f'explosion_{size_type}'):
            sound = random.choice(self.sounds[f'explosion_{size_type}'])
            sound.play()
    
    def play_sparkle(self):
        """播放火花声音"""
        if self.sounds.get('sparkle'):
            if random.random() < 0.3:  # 30%概率播放,避免太频繁
                sound = random.choice(self.sounds['sparkle'])
                sound.play()
    
    def play_special(self):
        """播放特殊效果声音"""
        if self.sounds.get('special'):
            sound = random.choice(self.sounds['special'])
            sound.play()
    
    def set_volume(self, volume):
        """设置音量"""
        self.volume = max(0.0, min(1.0, volume))

# 创建声音管理器实例
sound_manager = SoundManager()

# 优化版粒子类(添加声音支持)
class Particle:
    __slots__ = ('x', 'y', 'color', 'particle_type', 'size', 'speed', 'life', 
                 'decay', 'gravity', 'angle', 'vx', 'vy', 'alpha', 'rotation',
                 'rotation_speed', 'pulse_speed', 'pulse_phase', 'original_size',
                 'color_change', 'target_color', 'current_color', 'drag_factor')
    
    def __init__(self, x: float, y: float, color=None, particle_type="normal"):
        self.x = x
        self.y = y
        self.particle_type = particle_type
        
        # 颜色选择优化
        if color is None:
            if particle_type == "special":
                self.color = random.choice(SPECIAL_COLORS)
            elif particle_type == "sparkle":
                self.color = WHITE
            else:
                self.color = random.choice(COLORS)
        else:
            self.color = color
        
        # 根据粒子类型设置参数(减少随机范围)
        if particle_type == "normal":
            self.size = 1.5 + random.random() * 2.0
            self.speed = 2 + random.random() * 6
            self.life = 40 + random.randint(0, 60)
            self.decay = 1 + random.random() * 2
            self.gravity = 0.1
        elif particle_type == "fast":
            self.size = 1.0 + random.random() * 1.5
            self.speed = 6 + random.random() * 6
            self.life = 20 + random.randint(0, 30)
            self.decay = 3 + random.random() * 2
            self.gravity = 0.05
        elif particle_type == "sparkle":
            self.size = 0.5 + random.random() * 1.0
            self.speed = 4 + random.random() * 4
            self.life = 30 + random.randint(0, 30)
            self.decay = 4 + random.random() * 2
            self.gravity = 0.02
        else:  # special
            self.size = 3.0 + random.random() * 2.0
            self.speed = 3 + random.random() * 4
            self.life = 60 + random.randint(0, 60)
            self.decay = 1 + random.random() * 1
            self.gravity = 0.08
        
        # 使用预计算的sin/cos
        self.angle = random.random() * PI2
        cos_a = fast_cos(self.angle)
        sin_a = fast_sin(self.angle)
        self.vx = cos_a * self.speed
        self.vy = sin_a * self.speed
        
        self.alpha = 255
        self.rotation = random.random() * 360
        self.rotation_speed = -5 + random.random() * 10
        
        # 减少复杂效果以提升性能
        self.pulse_speed = 0.1  # 固定值减少计算
        self.pulse_phase = random.random() * PI2
        self.original_size = self.size
        
        # 减少颜色变化效果
        self.color_change = False  # 禁用颜色变化提升性能
        
        # 预计算空气阻力因子
        self.drag_factor = 0.97 + (random.random() * 0.04 - 0.02)
    
    def update(self) -> bool:
        """更新粒子,返回是否存活"""
        self.x += self.vx
        self.y += self.vy
        self.vy += self.gravity
        
        # 空气阻力 - 使用drag_factor
        self.vx *= self.drag_factor
        self.vy *= self.drag_factor
        
        # 生命周期
        self.life -= 1
        if self.life <= 0:
            return False
            
        # 透明度衰减
        self.alpha -= self.decay
        if self.alpha <= 0:
            return False
        
        # 简化效果:只保留旋转
        self.rotation += self.rotation_speed
        
        # 随机播放火花声音
        if self.particle_type == "sparkle" and random.random() < 0.01:
            sound_manager.play_sparkle()
        
        return True
    
    def draw(self, surface: pygame.Surface):
        """绘制粒子到surface"""
        if self.alpha <= 0:
            return
            
        alpha = int(self.alpha)
        if alpha <= 0:
            return
            
        # 简化绘制:只绘制圆形,去掉复杂效果
        radius = max(1, int(self.size))
        if radius <= 0:
            return
            
        # 直接绘制,避免创建临时surface
        color = self.color
        if alpha < 255:
            # 如果需要透明度,创建小surface
            temp_surf = pygame.Surface((radius * 2, radius * 2), pygame.SRCALPHA)
            pygame.draw.circle(temp_surf, (*color, alpha), (radius, radius), radius)
            surface.blit(temp_surf, (int(self.x - radius), int(self.y - radius)))
        else:
            pygame.draw.circle(surface, color, (int(self.x), int(self.y)), radius)

# 优化版烟花发射器(添加声音支持)
class Firework:
    __slots__ = ('x', 'y', 'color', 'speed', 'target_y', 'exploded', 
                 'particles', 'size', 'trail', 'sparks', 'explosion_count',
                 'max_explosions', 'firework_type', 'trail_counter', 'has_played_launch_sound')
    
    # 预计算爆炸参数
    EXPLOSION_PARAMS = {
        "normal": {"count": (100, 150), "types": ["normal", "fast"], "sound": "medium"},
        "big": {"count": (150, 200), "types": ["normal", "special"], "sound": "large"},
        "multi": {"count": (80, 120), "types": ["normal", "sparkle"], "sound": "small"}
    }
    
    def __init__(self, firework_type="normal"):
        self.firework_type = firework_type
        self.reset()
        self.trail_counter = 0
        self.has_played_launch_sound = False
    
    def reset(self):
        self.x = random.randint(100, WIDTH - 100)
        self.y = HEIGHT
        self.color = random.choice(COLORS)
        self.speed = 8 + random.random() * 7
        self.target_y = 50 + random.randint(0, HEIGHT // 4)  # 调整爆炸高度
        self.exploded = False
        self.particles = []  # 使用列表但会定期清理
        self.size = 3
        self.trail = []  # 简化轨迹存储
        self.sparks = []  # 简化火花效果
        self.explosion_count = 0
        self.has_played_launch_sound = False
        
        # 减少爆炸次数提升性能
        if self.firework_type == "multi":
            self.max_explosions = 2
        else:
            self.max_explosions = 1
    
    def update(self):
        """更新烟花状态"""
        if not self.exploded:
            # 播放发射声音(只播放一次)
            if not self.has_played_launch_sound and self.y < HEIGHT - 100:
                sound_manager.play_launch()
                self.has_played_launch_sound = True
            
            # 简化轨迹记录:每2帧记录一次
            self.trail_counter += 1
            if self.trail_counter % 2 == 0:
                self.trail.append((self.x, self.y))
                if len(self.trail) > 8:  # 减少轨迹长度
                    self.trail.pop(0)
            
            # 上升移动
            self.y -= self.speed
            self.x += (random.random() - 0.5) * 1.6  # 简化随机移动
            
            # 简化火花效果
            if random.random() < 0.2 and len(self.sparks) < 5:  # 限制火花数量
                spark = Particle(self.x, self.y, self.color, "sparkle")
                spark.speed = 1 + random.random() * 2
                spark.vx = (random.random() - 0.5) * 2
                spark.vy = -random.random() * 2
                spark.life = 10 + random.randint(0, 10)
                self.sparks.append(spark)
            
            # 更新火花
            alive_sparks = []
            for spark in self.sparks:
                if spark.update():
                    alive_sparks.append(spark)
            self.sparks = alive_sparks
            
            # 检查爆炸
            if self.y <= self.target_y:
                self.explode()
        else:
            # 更新粒子
            alive_particles = []
            for particle in self.particles:
                if particle.update():
                    alive_particles.append(particle)
            self.particles = alive_particles
    
    def explode(self):
        """执行爆炸"""
        self.exploded = True
        self.explosion_count += 1
        
        params = self.EXPLOSION_PARAMS.get(self.firework_type, self.EXPLOSION_PARAMS["normal"])
        min_count, max_count = params["count"]
        particle_types = params["types"]
        sound_type = params["sound"]
        
        # 播放爆炸声音
        sound_manager.play_explosion(sound_type)
        
        # 如果是特殊烟花,播放额外声音
        if self.firework_type == "special":
            sound_manager.play_special()
        
        # 减少粒子数量
        particle_count = min_count + random.randint(0, max_count - min_count)
        
        # 预计算角度和速度
        for i in range(particle_count):
            angle = (i / particle_count) * PI2
            speed = 2 + random.random() * 6
            
            # 使用查表法选择粒子类型
            if len(particle_types) > 1:
                particle_type = particle_types[i % 2]  # 交替使用两种类型
            else:
                particle_type = particle_types[0]
            
            particle = Particle(self.x, self.y, self.color, particle_type)
            particle.angle = angle
            cos_a = fast_cos(angle)
            sin_a = fast_sin(angle)
            particle.vx = cos_a * speed
            particle.vy = sin_a * speed
            
            self.particles.append(particle)
    
    def draw(self, surface: pygame.Surface):
        """绘制烟花"""
        if not self.exploded:
            # 绘制简化轨迹
            for i, (trail_x, trail_y) in enumerate(self.trail):
                alpha = 100 + int(155 * (i / len(self.trail)) if self.trail else 0)
                size = max(1, int(self.size * (i / len(self.trail))))
                
                pygame.draw.circle(surface, (*self.color, alpha), 
                                 (int(trail_x), int(trail_y)), size)
            
            # 绘制火花
            for spark in self.sparks:
                spark.draw(surface)
            
            # 绘制烟花主体
            pygame.draw.circle(surface, WHITE, (int(self.x), int(self.y)), self.size)
            pygame.draw.circle(surface, self.color, (int(self.x), int(self.y)), self.size // 2)
        else:
            # 批量绘制粒子
            for particle in self.particles:
                particle.draw(surface)
    
    def is_done(self) -> bool:
        """检查烟花是否结束"""
        if not self.exploded:
            return False
        return len(self.particles) == 0 and self.explosion_count >= self.max_explosions

# 简化版星星背景
class Star:
    __slots__ = ('x', 'y', 'size', 'brightness', 'twinkle_speed', 'phase')
    
    def __init__(self):
        self.x = random.randint(0, WIDTH)
        self.y = random.randint(0, HEIGHT // 2)
        self.size = random.random() * 1.5  # 减小最大尺寸
        self.brightness = 100 + random.randint(0, 155)
        self.twinkle_speed = 0.01 + random.random() * 0.04
        self.phase = random.random() * PI2
    
    def update(self):
        self.phase += self.twinkle_speed
        self.brightness = 100 + int(105 * (0.5 + 0.5 * fast_sin(self.phase)))
    
    def draw(self, surface: pygame.Surface):
        brightness = min(255, max(0, self.brightness))
        color = (brightness, brightness, brightness)
        pygame.draw.circle(surface, color, (int(self.x), int(self.y)), max(0.5, self.size))

# 优化绘制函数
def draw_background_gradient(surface: pygame.Surface):
    """优化背景绘制"""
    # 使用填充代替逐行绘制
    top_color = (10, 10, 40)
    bottom_color = (30, 30, 60)
    
    # 创建渐变surface(缓存)
    if not hasattr(draw_background_gradient, 'gradient_surf'):
        gradient = pygame.Surface((1, HEIGHT))
        for y in range(HEIGHT):
            ratio = y / HEIGHT
            r = int(top_color[0] * (1 - ratio) + bottom_color[0] * ratio)
            g = int(top_color[1] * (1 - ratio) + bottom_color[1] * ratio)
            b = int(top_color[2] * (1 - ratio) + bottom_color[2] * ratio)
            gradient.set_at((0, y), (r, g, b))
        draw_background_gradient.gradient_surf = gradient
    
    # 拉伸填充
    pygame.transform.scale(draw_background_gradient.gradient_surf, (WIDTH, HEIGHT), surface)

# 主程序
def main():
    # 创建表面
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    particle_surface = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
    
    # 创建对象
    fireworks = [Firework("normal") for _ in range(2)]  # 减少初始烟花数量
    stars = [Star() for _ in range(150)]  # 减少星星数量
    
    # 字体
    try:
        font = pygame.font.SysFont("simhei", 48)  # 使用较小字体
        small_font = pygame.font.SysFont("simhei", 24)
    except:
        font = pygame.font.SysFont(None, 48)
        small_font = pygame.font.SysFont(None, 24)
    
    # 预渲染文本(减少每帧渲染)
    title_text = font.render("新年快乐!2026", True, (255, 215, 0))
    
    clock = pygame.time.Clock()
    running = True
    frame = 0
    last_firework_time = 0
    
    # 游戏循环
    while running:
        # 事件处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    running = False
                elif event.key == pygame.K_SPACE:
                    # 限制添加烟花数量
                    if len(fireworks) < 16:  # 最大8个烟花
                        firework_type = random.choice(["normal", "normal", "big"])
                        fireworks.append(Firework(firework_type))
        
        # 清空表面
        screen.fill(BLACK)
        draw_background_gradient(screen)
        particle_surface.fill((0, 0, 0, 0))
        
        # 更新和绘制星星
        for star in stars:
            star.update()
            star.draw(screen)
        
        # 自动添加烟花(限制频率)
        current_time = pygame.time.get_ticks()
        if (current_time - last_firework_time > 2000 and  # 每2秒
            len(fireworks) < 6 and  # 不超过6个
            random.random() < 0.1):  # 10%概率
            firework_type = random.choice(["normal", "normal", "big", "multi"])
            fireworks.append(Firework(firework_type))
            last_firework_time = current_time
        
        # 更新和绘制烟花
        alive_fireworks = []
        total_particles = 0
        
        for firework in fireworks:
            firework.update()
            
            # 统计粒子数量
            total_particles += len(firework.particles)
            
            # 绘制到粒子表面
            firework.draw(particle_surface)
            
            # 移除完成的烟花
            if not firework.is_done():
                alive_fireworks.append(firework)
            elif random.random() < 0.3 and len(alive_fireworks) < 4:  # 30%概率立即添加新烟花
                firework_type = random.choice(["normal", "big"])
                alive_fireworks.append(Firework(firework_type))
        
        fireworks = alive_fireworks
        
        # 绘制粒子表面
        screen.blit(particle_surface, (0, 0))
        
        # 绘制UI
        frame += 1

        # 计算彩虹颜色
        hue = (frame * 0.01) % 1.0  # 色相在0-1之间循环
        rgb = colorsys.hsv_to_rgb(hue, 0.9, 1.0)  # 高饱和度,高亮度
        color = (int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255))

        # 重新渲染文本(使用新颜色)
        title_text = font.render("新年快乐!2026", True, color)

        # 添加文字阴影增强效果
        shadow_color = (int(rgb[0] * 150), int(rgb[1] * 150), int(rgb[2] * 150))
        shadow_text = font.render("新年快乐!2026", True, shadow_color)

        # 绘制阴影(偏移2像素)
        screen.blit(shadow_text, (WIDTH//2 - shadow_text.get_width()//2 + 2, 52))
        # 绘制主文字
        screen.blit(title_text, (WIDTH//2 - title_text.get_width()//2, 50))
        
        # 更新显示
        pygame.display.flip()
        
        # 动态调整帧率目标
        target_fps = 60
        if total_particles > 2000:
            target_fps = 45
        elif total_particles > 1000:
            target_fps = 50
        
        clock.tick(target_fps)
    
    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()
相关推荐
智算菩萨2 小时前
【Python机器学习】主成分分析(PCA):高维数据的“瘦身术“
开发语言·python·机器学习
stars-he2 小时前
单相可控整流电路的MATLAB仿真设计(2)
开发语言·matlab
AC赳赳老秦3 小时前
政务数据处理:DeepSeek 适配国产化环境的统计分析与报告生成
开发语言·hadoop·spring boot·postgresql·测试用例·政务·deepseek
540_5403 小时前
ADVANCE Day33
人工智能·python·机器学习
水龙吟啸3 小时前
基于Orbbec-Gemini深度相机与SFM-2D to 3D重建算法、手部识别视觉算法、Unity运动控制的3D水果切割游戏
python·深度学习·神经网络·c#·游戏引擎·3d视觉·3d重建
xlxxy_3 小时前
abap 批量创建供应商
运维·开发语言·sap·abap·pp·mm
独自破碎E3 小时前
Java是怎么实现跨平台的?
java·开发语言
墨有6664 小时前
C++ string 部分功能详解:迭代器、初始化与常用函数
开发语言·c++
BBB努力学习程序设计4 小时前
深入理解 Python 中的深浅拷贝(Shallow Copy & Deep Copy):避免数据引用的 “坑”
python