Python编程者的元旦狂欢:一场别样的烟花盛宴

文章目录

  • 一、前言
  • 二、实现步骤
    • 1.前期准备
    • 2.部分代码
      • 2.1 窗口设置
      • 2.2 烟花类 Firework
      • 2.3 粒子类 Particle
      • 2.4 轨迹类 Trail
      • 2.5 更新函数
      • 2.6 导入音频
      • 2.7 显示元旦快乐
      • 2.8 Esc键结束
    • 3.完整代码(详细注释)
    • 4.视频效果
    • 5.可执行文件
  • 三、总结


一、前言

2023已经过去,我们即将迎来2024年的元旦节。在这个值得庆祝的日子里,许多人都会想要为自己或者与亲朋好友共同创造一些难忘的瞬间。而作为一名Python初学者,也可以不出门,在家中利用所学编写一个元旦烟花秀,为元旦节增添一份喜庆的气氛。通过编写这个小程序,你不仅可以锻炼自己的编程能力,还可以在其中感受到自己的创造力和乐趣。

在本篇博客中,我们将介绍如何使用Python编写一个简单的元旦烟花秀程序,并帮助大家实现自己的创意和想象。


二、实现步骤

1.前期准备

安装python和pygame,PyCharm(IDE可不安装)和pyinstaller(打包可不安装)

安装请参考以下教程:

Python新手上路:“用Python和Pygame创造你的流星雨”

一键启动Python世界:PyCharm安装全攻略与pyinstaller魔法转换

2.部分代码

2.1 窗口设置

以下示例代码获取当前屏幕的分辨率,并将窗口尺寸设置为全屏分辨率。

示例代码如下:

pygame.init() # 初始化pygame库

#获取当前屏幕的分辨率
sceen_info = pygame.display.Info()
sceen_width = sceen_info.current_w
sceen_height = sceen_info.current_h

#设置窗口尺寸为屏幕分辨率
DISPLAY_WIDTH = sceen_width
DISPLAY_HEIGHT = sceen_height 

win = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT)) # 创建指定大小的窗口,并赋值给变量win     

2.2 烟花类 Firework

以下示例代码定义了烟花的属性和行为,包括烟花的颜色、位置、是否爆炸以及爆炸后产生的粒子等。

示例代码如下:

class Firework: # 定义烟花类
 
    def __init__(self): # 初始化烟花属性
        self.colour = (randint(0, 255), randint(0, 255), randint(0, 255)) # 烟花爆炸前的颜色
        self.colours = (
            (randint(0, 255), randint(0, 255), randint(0, 255)
             ), (randint(0, 255), randint(0, 255), randint(0, 255)),
            (randint(0, 255), randint(0, 255), randint(0, 255)))  # 烟花爆炸过程中粒子的颜色
        self.firework = Particle(randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, True,self.colour) # 初始化烟花的位置
        self.exploded = False # 表示烟花是否已经爆炸
        self.particles = [] # 空列表,用于储存烟花爆炸后生成的粒子
        self.min_max_particles = vector(200, 255) # 定义烟花爆炸产生的粒子数量的范围 200-255

2.3 粒子类 Particle

以下示例代码定义了烟花爆炸后产生的粒子的属性和行为,包括粒子的位置、速度、颜色、尾迹效果等。

示例代码如下:

  class Particle: # 定义烟花粒子类
 
    def __init__(self, x, y, firework, colour): # 初始化烟花粒子属性
        self.firework = firework # 表示粒子所属的烟花对象
        self.pos = vector(x, y) # 表示粒子的当前位置
        self.origin = vector(x, y) # 表示粒子的起始位置
        self.radius = 20 # 表示粒子的半径
        self.remove = False # 表示是否需要移除该粒子
        self.explosion_radius = randint(20, 40) # 表示粒子爆炸时的扩散半径
        self.life = 0 # 表示粒子的生命周期即存在时间
        self.acc = vector(0, 0) # 表示粒子的加速度
        self.trails = [] # 存储粒子的尾迹对象
        self.prev_posx = [-10] * 10  # 储存粒子过去10个位置的x坐标
        self.prev_posy = [-10] * 10  # 储存粒子过去10个位置的y坐标      

2.4 轨迹类 Trail

以下示例代码定义了烟花和粒子的尾迹效果,包括尾迹的位置、颜色和大小等。

  1. init(self, n, size, dynamic):构造方法,初始化烟花粒子尾迹的属性。其中 n 表示烟花轨迹在轨迹数组中的位置,size 表示尾迹的初始大小,dynamic 是一个布尔值,表示轨迹是否是动态的。
  2. get_pos(self, x, y):用于设置尾迹的位置,接收 x 和 y 作为坐标参数。
  3. show(self, win):用于在显示窗口中绘制尾迹。使用 pygame.draw.circle() 绘制一个圆形轨迹点,颜色由 self.colour 指定,位置由 self.pos 的坐标确定,大小由 self.size 决定。

示例代码如下:

    def __init__(self, n, size, dynamic):  # 初始化烟花粒子尾迹属性
        self.pos_in_line = n # 烟花轨迹在轨迹数组中的位置
        self.pos = vector(-10, -10) # 烟花轨迹的位置
        self.dynamic = dynamic # 表示轨迹是动态的
 
        if self.dynamic: # 判断是否是动态轨迹
            self.colour = trail_colours[n] # 根据轨迹在数组中的位置选择颜色
            self.size = int(size - n / 2) # 根据轨迹在数组中的位置调整轨迹点的大小
        else:
            self.colour = (255, 255, 200) # 静态轨迹就设置颜色为浅黄色
            self.size = size - 2 # 设置轨迹点的大小为原始大小 - 2
            if self.size < 0: # 如果计算后的轨迹点大小小于0
                self.size = 0 # 设置轨迹点为0
 
    def get_pos(self, x, y): # 用于获取尾迹的位置
        self.pos = vector(x, y)
 
    def show(self, win): # 用于在显示窗口中绘制尾迹
        pygame.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size) # 绘制一个圆形轨迹点,使用指定的颜色、位置和大小 

2.5 更新函数

以下示例代码用于更新烟花和粒子的状态,并在窗口上绘制它们的轨迹和形状。

示例代码如下:

   #实时更新和展示烟花效果 
def update(win, fireworks): # 用于更新烟花的状态并进行绘制
    for fw in fireworks: # 对于烟花效果数组
        fw.update(win) # 更新烟花效果的状态
        if fw.remove(): # 判断烟花效果是否达到移除条件
            fireworks.remove(fw) # 移除
 
    pygame.display.update() # 刷新显示窗口,将更新后的烟花效果显示出来 

2.6 导入音频

以下示例代码,我们假设有一个名为"firework.wav"的音频文件,然后使用pygame.mixer.Sound()加载该文件,并通过play()方法播放声音。这样就可以在烟花秀中添加音效了。请注意,在使用pygame.mixer.Sound()之前,需要先初始化pygame.mixer,即pygame.mixer.init()。这样才能正确地加载和播放声音文件。

示例代码如下:

# 初始化 Pygame 音频
pygame.mixer.init()
firework_sound = pygame.mixer.Sound("firework.wav")
         
# 播放声音
firework_sound.play()  

2.7 显示元旦快乐

以下示例代码展示了如何加载Windows自带的中文字体文件,并在窗口上显示“元旦快乐”的文本。其中msyh.ttc是微软雅黑字体文件的示例。

示例代码如下:

# 加载 Windows 自带的中文字体文件
font_path = "C:/Windows/Fonts/msyh.ttc"

pygame.font.init()  # 初始化字体模块

        # 在窗口上显示"元旦快乐"文本
        font = pygame.font.Font(font_path, 100) # 设置字体和大小
#        text = font.render("元旦快乐", True, (148, 0, 211))  # 创建文本对象 
        random_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))  # 生成随机颜色
        text = font.render("元旦快乐", True, random_color)  # 创建文本对象并设置随机颜色        
        text_width, text_height = font.size("元旦快乐")  # 获取文本的宽度和高度
        text_x = (DISPLAY_WIDTH - text_width) // 2  # 计算文本放置的 x 坐标
        text_y = (DISPLAY_HEIGHT - text_height) // 4  # 计算文本放置的 y 坐标
        win.blit(text, (text_x, text_y))  # 将文本绘制到窗口上 

2.8 Esc键结束

以下示例代码展示了一个主循环,该循环会不断检测键盘按键事件,如果按下的是Esc键,则将标志变量running设置为False,从而退出主循环并结束程序。

示例代码如下:

    while running: # 进入主循环,直到标志变量running为False时结束循环
        for event in pygame.event.get(): # 遍历事件队列中的所有事件
            if event.type == pygame.KEYDOWN: # 如果事件类型是KEYDOWN(即键盘按下事件)
                if event.key == pygame.K_ESCAPE: # 检测按下的按键是否为Esc键
                    running = False # 将标志变量running设置为False,表示退出主循环,从而退出程序。

3.完整代码(详细注释)

总体来说,该程序使用 Pygame 库实现了烟花动画效果,包括烟花的发射、爆炸和粒子效果,并在窗口上绘制出烟花的轨迹和形状。同时,程序还实现了在窗口中显示“元旦快乐”文本和播放烟花爆炸的声音效果。

代码如下(示例):

import math # 导入math模块(数学函数)
import pygame # 导入pygame模块
import os # 导入os模块
import random # 导入random模块(随机数)
import numpy as np #导入numpy库,用于进行数值计算和数组操作
from random import randint, uniform, choice # 从random库中导入randint、uniform、chioce函数

# 初始化 Pygame 音频
pygame.mixer.init()
firework_sound = pygame.mixer.Sound("firework.wav")

# 加载 Windows 自带的中文字体文件
font_path = "C:/Windows/Fonts/msyh.ttc"
 
vector = pygame.math.Vector2 #处理二维向量
gravity = vector(0, 0.2) #重力加速度,方向向下
pygame.init() # 初始化pygame库

#获取当前屏幕的分辨率
sceen_info = pygame.display.Info()
sceen_width = sceen_info.current_w
sceen_height = sceen_info.current_h

#设置窗口尺寸为屏幕分辨率
DISPLAY_WIDTH = sceen_width
DISPLAY_HEIGHT = sceen_height
 
trail_colours = [(45, 45, 45), (60, 60, 60), (75, 75, 75),
                 (125, 125, 125), (150, 150, 150)] # 烟花的尾迹颜色
dynamic_offset = 1 # 动态偏移
static_offset = 5 # 静态偏移
 
class Firework: # 定义烟花类
 
    def __init__(self): # 初始化烟花属性
        self.colour = (randint(0, 255), randint(0, 255), randint(0, 255)) # 烟花爆炸前的颜色
        self.colours = (
            (randint(0, 255), randint(0, 255), randint(0, 255)
             ), (randint(0, 255), randint(0, 255), randint(0, 255)),
            (randint(0, 255), randint(0, 255), randint(0, 255)))  # 烟花爆炸过程中粒子的颜色
        self.firework = Particle(randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, True,self.colour) # 初始化烟花的位置
        self.exploded = False # 表示烟花是否已经爆炸
        self.particles = [] # 空列表,用于储存烟花爆炸后生成的粒子
        self.min_max_particles = vector(200, 255) # 定义烟花爆炸产生的粒子数量的范围 200-255
 
# 焰火效果的更新函数,用于在每一帧更新焰花和粒子的位置,并在窗口上显示它们的轨迹和形状 
    def update(self, win): # 更新烟花的状态,参数win表示显示窗口 
        if not self.exploded: # 检查烟花是否爆炸
            self.firework.apply_force(gravity) # 未爆炸施加重力
            self.firework.move() # 移动烟花的位置
            for tf in self.firework.trails: # 烟花的每一个轨迹
                tf.show(win) # 在窗口上显示轨迹形状
 
            self.show(win) # 在窗口上显示焰火形状
 
            if self.firework.vel.y >= 0: # 如果焰火的纵向加速度大于等于0,表示烟花达到顶点
                self.exploded = True # 设置烟花爆炸
                self.explode() # 触发爆炸效果
        else: # 烟花已经爆炸
            for particle in self.particles: # 对于每一个烟花粒子
                particle.apply_force(  
                    vector(gravity.x + uniform(-2, 2) / 50, gravity.y / 2 + (randint(1, 20) / 255))) # 施加随机力量(重力和额外随机力的作用)
                particle.move() # 移动粒子的位置
                for t in particle.trails: # 对于每一个粒子轨迹
                    t.show(win) # 在窗口上显示轨迹形状
                particle.show(win) # 在窗口上显示粒子形状
 
    def explode(self): # 用于烟花爆炸时生成粒子的过程
        amount = randint(int(self.min_max_particles.x), int(self.min_max_particles.y)) #定义生成粒子的数量是一个随机数
        for i in range(amount):
            self.particles.append( # 创建一个新的粒子对象
                Particle(self.firework.pos.x, self.firework.pos.y, False, self.colours)) # 传入爆炸发生位置的x和y坐标(即烟花的位置)
 
    def show(self, win): # 用于在显示窗口绘制烟花
        pygame.draw.circle(win, self.colour, (int(self.firework.pos.x), int(self.firework.pos.y)), self.firework.size) # 在显示窗口中以烟花的位置为圆心,绘制一个指定颜色和大小的圆形,从而呈现出烟花的效果
 
    def remove(self): # 用于判断烟花是否需要移除
        if self.exploded: # 判断烟花是否已经爆炸
            for p in self.particles: 
                if p.remove is True:
                    self.particles.remove(p) # 已经爆炸需要移除烟花粒子
 
            if len(self.particles) == 0: 
                return True
            else:
                return False
 
 
class Particle: # 定义烟花粒子类
 
    def __init__(self, x, y, firework, colour): # 初始化烟花粒子属性
        self.firework = firework # 表示粒子所属的烟花对象
        self.pos = vector(x, y) # 表示粒子的当前位置
        self.origin = vector(x, y) # 表示粒子的起始位置
        self.radius = 20 # 表示粒子的半径
        self.remove = False # 表示是否需要移除该粒子
        self.explosion_radius = randint(20, 40) # 表示粒子爆炸时的扩散半径
        self.life = 0 # 表示粒子的生命周期即存在时间
        self.acc = vector(0, 0) # 表示粒子的加速度
        self.trails = [] # 存储粒子的尾迹对象
        self.prev_posx = [-10] * 10  # 储存粒子过去10个位置的x坐标
        self.prev_posy = [-10] * 10  # 储存粒子过去10个位置的y坐标
 
        if self.firework: # 判断烟花状态是否爆炸
            self.vel = vector(0, -randint(13, 18)) # 设置烟花的速度向量,使得烟花能够垂直向上发射
            self.size = 4 # 设置烟花粒子的大小为4
            self.colour = colour # 配置和烟花一样的随机颜色
            for i in range(5):
                self.trails.append(Trail(i, self.size, True)) # 设置拖尾效果对象
        else:
            self.vel = vector(uniform(-1, 1), uniform(-1, 1)) # 设置烟花粒子的速度向量,使得烟花粒子具有随机的方向
            self.vel.x *= randint(10, self.explosion_radius + 2) # 模拟烟花爆炸时粒子的速度 水平方向
            self.vel.y *= randint(10, self.explosion_radius + 2) # 模拟烟花爆炸时粒子的速度 垂直方向
            self.size = randint(1, 5) # 设置烟花爆炸后产生的粒子的大小
            self.colour = choice(colour) # 配置和烟花一样的随机颜色
            for i in range(5):
                self.trails.append(Trail(i, self.size, False))  # 设置拖尾效果对象
 
    def apply_force(self, force): # 用于给粒子施加力
        self.acc += force 
 
    def move(self): # 用于更新粒子的位置和速度
        if not self.firework: # 如果粒子步数烟花的核心火花
            self.vel.x *= 0.85 # 模拟阻尼效果,进行减速
            self.vel.y *= 0.85 # 模拟阻尼效果,进行减速
 
        self.vel += self.acc # 更新粒子速度
        self.pos += self.vel # 更新粒子位置
        self.acc *= 0 # 粒子加速度清零
 
        if self.life == 0 and not self.firework:  
            distance = math.sqrt((self.pos.x - self.origin.x) ** 2 + (self.pos.y - self.origin.y) ** 2)
            if distance > self.explosion_radius: # 判断粒子是否在烟花的爆炸半径之外
                self.remove = True
 
        self.decay() # 衰减处理,逐渐降低粒子的半径和透明度,模拟粒子的消失过程
 
        self.trail_update() # 更新粒子的尾迹效果
 
        self.life += 1 # 粒子的生命周期加1
 
    def show(self, win): # 显示粒子效果的窗口
        pygame.draw.circle(win, (self.colour[0], self.colour[1], self.colour[2], 0), (int(self.pos.x), int(self.pos.y)),self.size) # 用于在窗口中绘制粒子

# 实现粒子效果的显示和衰减逻辑,使得粒子在特定的生命周期内以一定的概率消散.从而模拟出烟花效果 
    def decay(self):  # 用于模拟粒子的衰减效果
        if 80 > self.life > 10: # 检查粒子的生命周期是否在指定范围内
            ran = randint(0, 30) # 衰减事件的概率
            if ran == 0: # 概率为0
                self.remove = True # 移除粒子
        elif self.life > 80: # 检查粒子的生命周期是否超过设置值
            ran = randint(0, 5)  # 衰减事件的概率范围减少
            if ran == 0: # 概率为0
                self.remove = True # 移除粒子

# 更新粒子效果中的轨迹信息,它将当前的粒子位置信息插入位置列表的开头,然后根据轨迹类型选择正确的位置信号,并更新轨迹对象的位置. 
    def trail_update(self): # 用于更新粒子的尾迹位置
        self.prev_posx.pop() # 移除最后一个元素x
        self.prev_posx.insert(0, int(self.pos.x)) # 插入到表头
        self.prev_posy.pop() # 移除最后一个元素y
        self.prev_posy.insert(0, int(self.pos.y)) # 插入到表头
 
        for n, t in enumerate(self.trails): # 遍历粒子效果中的轨迹列表
            if t.dynamic: # 检查当前轨迹是否为动态轨迹
                t.get_pos(self.prev_posx[n + dynamic_offset],
                          self.prev_posy[n + dynamic_offset])
            else:
                t.get_pos(self.prev_posx[n + static_offset],
                          self.prev_posy[n + static_offset])
 
 
class Trail: # 定义烟花粒子尾迹类
 
    def __init__(self, n, size, dynamic):  # 初始化烟花粒子尾迹属性
        self.pos_in_line = n # 烟花轨迹在轨迹数组中的位置
        self.pos = vector(-10, -10) # 烟花轨迹的位置
        self.dynamic = dynamic # 表示轨迹是动态的
 
        if self.dynamic: # 判断是否是动态轨迹
            self.colour = trail_colours[n] # 根据轨迹在数组中的位置选择颜色
            self.size = int(size - n / 2) # 根据轨迹在数组中的位置调整轨迹点的大小
        else:
            self.colour = (255, 255, 200) # 静态轨迹就设置颜色为浅黄色
            self.size = size - 2 # 设置轨迹点的大小为原始大小 - 2
            if self.size < 0: # 如果计算后的轨迹点大小小于0
                self.size = 0 # 设置轨迹点为0
 
    def get_pos(self, x, y): # 用于获取尾迹的位置
        self.pos = vector(x, y)
 
    def show(self, win): # 用于在显示窗口中绘制尾迹
        pygame.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size) # 绘制一个圆形轨迹点,使用指定的颜色、位置和大小 

#实时更新和展示烟花效果 
def update(win, fireworks): # 用于更新烟花的状态并进行绘制
    for fw in fireworks: # 对于烟花效果数组
        fw.update(win) # 更新烟花效果的状态
        if fw.remove(): # 判断烟花效果是否达到移除条件
            fireworks.remove(fw) # 移除
 
    pygame.display.update() # 刷新显示窗口,将更新后的烟花效果显示出来

def main(): # 程序的入口,用于初始化并开始循环
    pygame.init() # 初始化pygame库
    pygame.font.init()  # 初始化字体模块
    
    pygame.display.set_caption("Firework in Pygame") # 设置窗口标题
    
    win = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT)) # 创建指定大小的窗口,并赋值给变量win        
 
    fireworks = [Firework() for i in range(2)]  # 创建包含两个Firework对象的列表,作为初始的烟花效果
    running = True # 设置程序为运行状态
 
    while running: # 进入主循环,直到标志变量running为False时结束循环
        for event in pygame.event.get(): # 遍历事件队列中的所有事件
            if event.type == pygame.KEYDOWN: # 如果事件类型是KEYDOWN(即键盘按下事件)
                if event.key == pygame.K_ESCAPE: # 检测按下的按键是否为Esc键
                    running = False # 将标志变量running设置为False,表示退出主循环,从而退出程序。                 
 
        win.fill((20, 20, 30))  # 用深蓝色填充窗口,作为背景色       
    
        if randint(0, 2) == 1:  # 以一定概率创建新的烟花效果
            fireworks.append(Firework())  
        
        # 在窗口上显示"元旦快乐"文本
        font = pygame.font.Font(font_path, 100) # 设置字体和大小
#        text = font.render("元旦快乐", True, (148, 0, 211))  # 创建文本对象 
        random_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))  # 生成随机颜色
        text = font.render("元旦快乐", True, random_color)  # 创建文本对象并设置随机颜色        
        text_width, text_height = font.size("元旦快乐")  # 获取文本的宽度和高度
        text_x = (DISPLAY_WIDTH - text_width) // 2  # 计算文本放置的 x 坐标
        text_y = (DISPLAY_HEIGHT - text_height) // 4  # 计算文本放置的 y 坐标
        win.blit(text, (text_x, text_y))  # 将文本绘制到窗口上 
     
        # 播放声音
        firework_sound.play()        
        
        update(win, fireworks) # 更新并展示当前所有的烟花效果
                
    pygame.quit() # 退出pygame
    quit() # 退出程序
 
 
main() # 调用main函数来启动程序

4.视频效果

该视频附带真实烟花音效,有点吵,不喜欢也可以自行去掉。

New Year‘s Day fireworks show

5.可执行文件

如果是还没有安装python环境和软件的小伙伴也想看到美丽的元旦烟花秀,可以下载资源或者直接私信我,我已经打包好了可执行文件,直接双击就能运行,无需下载python环境。


三、总结

本文主要介绍了如何使用Python编写一个简单的元旦烟花秀程序。通过本文的学习,我们可以学习到如何使用Python中的random模块生成随机的烟花效果,并利用pygame库实现动态显示。同时,我们还介绍了如何为烟花效果添加音效,并将整个程序打包成可执行文件。总之,这篇文章为Python初学者提供了一个实践项目,让大家在编程的过程中感受到创造的乐趣和实现想象的快感。

感谢你的观看,谢谢!

Python编程者的元旦狂欢:一场别样的烟花盛宴_第1张图片

你可能感兴趣的:(Python学习,python,pygame,开发语言,学习)