用Python和Pygame打造绚丽烟花效果+节日祝福语

简介: 本文介绍了一款基于Python和Pygame库实现的烟花效果程序,模拟烟花发射、爆炸及粒子轨迹,结合动态文本显示祝福语,营造逼真的节日氛围。程序包括烟花类、粒子类、痕迹类和动态文本显示功能,通过随机化颜色、速度和粒子数量增加效果多样性。用户可以看到烟花从屏幕底部发射、上升并在空中爆炸,伴随粒子轨迹和动态祝福语“蛇年大吉”、“Happy Spring Festival”。文章详细解析了核心代码逻辑和技术要点,帮助读者理解如何利用Pygame库实现复杂视觉效果,并提供了未来改进方向,如优化性能、增加特效和增强交互性。

用Python和Pygame打造绚丽烟花效果+节日祝福语


摘要
一年一度的春节即将到来,为了渲染节日气氛,绚丽的烟花表演是必不可少的。本文介绍了一款基于Python和Pygame库实现的烟花效果程序。该程序通过模拟烟花的发射、爆炸和粒子轨迹,结合动态文本显示,营造出逼真的烟花表演效果。文章详细解析了程序的核心代码逻辑、关键技术和实现细节,旨在帮助读者理解如何利用Pygame库实现复杂的视觉效果。


1.引言

烟花表演一直是节日庆典中不可或缺的视觉盛宴。如今,通过计算机图形学和编程技术,我们可以在屏幕上重现这种绚丽多彩的效果。本文将通过一个完整的Python程序,展示如何利用Pygame库实现烟花效果,并结合动态文本展示,为用户带来沉浸式的视觉体验。

2.程序概述

该程序的核心功能是模拟烟花的发射、爆炸和粒子轨迹。程序的主要组成部分包括:

• 烟花类(Firework):负责模拟烟花的发射和爆炸效果。

• 粒子类(Particle):模拟烟花爆炸后产生的粒子运动。

• 痕迹类(Trail):用于绘制粒子运动轨迹,增强视觉效果。

• 动态文本显示:在烟花表演过程中动态显示祝福语。

程序运行时,用户将看到烟花从屏幕底部发射、上升并在空中爆炸,同时伴随粒子轨迹和动态文本的显示。

3.核心代码解析

3.1 烟花类(Firework)

烟花类是程序的核心,负责模拟烟花的发射和爆炸效果。以下是其主要功能:

• 初始化:随机生成烟花的颜色和发射位置。

• 更新状态(update):模拟烟花的上升和爆炸过程。当烟花达到最高点时,触发爆炸效果。

• 爆炸(explode):生成大量粒子,模拟烟花爆炸的效果。同时播放烟花音效。

• 显示(show):绘制烟花的当前位置。

• 移除(remove):检查烟花是否已经完全消失,如果是,则从列表中移除。

class Firework:
    def __init__(self):
        self.colour = (ra.randint(10, 255), ra.randint(10, 255), ra.randint(10, 255))
        self.firework = Particle(ra.randint(0, screenWidth), screenHeight, True, self.colour)
        self.exploded = False
        self.particles = []
        self.min_max_particles = vector(666, 999)

    def update(self, win):
        g = vector(0, ra.uniform(0.15, 0.4))
        if not self.exploded:
            self.firework.apply_force(g)
            self.firework.move()
            for tf in self.firework.trails:
                tf.show(win)
            self.show(win)
            if self.firework.vel.y >= 0:
                self.exploded = True
                self.explode()
        else:
            for particle in self.particles:
                particle.apply_force(vector(g.x + ra.uniform(-1, 1) / 20, g.y / 2 + (ra.randint(1, 8) / 100)))
                particle.move()
                for t in particle.trails:
                    t.show(win)
                particle.show(win)

    def explode(self):
        amount = ra.randint(int(self.min_max_particles.x), int(self.min_max_particles.y))
        firework_sound.play()  # 播放烟花声音
        for i in range(amount):
            self.particles.append(Particle(self.firework.pos.x, self.firework.pos.y, False, self.colours))

3.2 粒子类(Particle)

粒子类用于模拟烟花爆炸后产生的碎片。以下是其主要功能:

• 初始化:根据是否为烟花主体,设置粒子的初始位置、速度和颜色。

• 应用力(apply_force):模拟重力和空气阻力对粒子的影响。

• 移动(move):更新粒子的位置,并根据其生命周期决定是否移除。

• 显示(show):绘制粒子的当前位置。

• 轨迹更新(trail_update):记录粒子的运动轨迹。

class Particle:
    def __init__(self, x, y, firework, colour):
        self.firework = firework
        self.pos = vector(x, y)
        self.vel = vector(ra.uniform(-1, 1), ra.uniform(-1, 1))
        self.size = ra.randint(2, 4)
        self.colour = colour
        self.trails = [Trail(i, self.size, False) for i in range(5)]

    def move(self):
        self.vel.x *= 0.8
        self.vel.y *= 0.8
        self.vel += self.acc
        self.pos += self.vel
        self.acc *= 0
        self.decay()
        self.trail_update()

    def show(self, win):
        pg.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size)

3.3 痕迹类(Trail)

痕迹类用于记录和绘制粒子的运动轨迹,增强视觉效果。其核心功能是根据粒子的当前位置动态更新轨迹。

class Trail:
    def __init__(self, n, size, dynamic):
        self.pos = vector(-10, -10)
        self.dynamic = dynamic
        self.colour = trail_colors[n]
        self.size = int(size - n / 2)

    def get_pos(self, x, y):
        self.pos = vector(x, y)

    def show(self, win):
        pg.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size)

3.4 动态文本显示

程序在烟花表演过程中动态显示祝福语。通过逐字符渲染文本,实现文本的动态显示效果。

font = pg.font.Font("/System/Library/Fonts/PingFang.ttc", 99)
text1 = "蛇 年 大 吉 ! "
displayed_text1 = ""
for char in text1:
    displayed_text1 += char
    rendered_text1 = font.render(displayed_text1, True, (255, 10, 20))
    screen.blit(rendered_text1, (x, y))

完整源码:

import random
import pygame as pg
import random as ra
import math


pg.init()
pg.display.set_caption("🎇")

winScreen = pg.display.Info()
screenWidth = winScreen.current_w
screenHeight = winScreen.current_h

vector = pg.math.Vector2

trail_colors = [(random.randint(1,55), random.randint(1,55),random.randint(1,55)), (random.randint(40,60), random.randint(40,60), random.randint(40,60)), (random.randint(70,75),random.randint(70,75),random.randint(70,75)), (125, 125, 125), (150, 150, 150)]

# 加载烟花声音
firework_sound = pg.mixer.Sound('烟花音效.mp3')  # 网上自行下载

# 烟花类
class Firework:

    def __init__(self):
        self.colour = (ra.randint(10, 255), ra.randint(10, 255), ra.randint(10, 255))
        self.colours = (
            (ra.randint(10, 255), ra.randint(10, 255), ra.randint(10, 255)),
            (ra.randint(10, 255), ra.randint(10, 255), ra.randint(10, 255)),
            (ra.randint(100, 255), ra.randint(100, 255), ra.randint(60, 255))
        )
        self.firework = Particle(ra.randint(0, screenWidth), screenHeight, True, self.colour)
        self.exploded = False
        self.particles = []
        self.min_max_particles = vector(666, 999)

    def update(self, win):
        g = vector(0, ra.uniform(0.15, 0.4))
        if not self.exploded:
            self.firework.apply_force(g)
            self.firework.move()
            for tf in self.firework.trails:
                tf.show(win)
            self.show(win)
            if self.firework.vel.y >= 0:
                self.exploded = True
                self.explode()
        else:
            for particle in self.particles:
                particle.apply_force(vector(g.x + ra.uniform(-1, 1) / 20, g.y / 2 + (ra.randint(1, 8) / 100)))
                particle.move()
                for t in particle.trails:
                    t.show(win)
                particle.show(win)

    def explode(self):
        amount = ra.randint(int(self.min_max_particles.x), int(self.min_max_particles.y))
        firework_sound.play()  # 播放烟花声音
        for i in range(amount):
            self.particles.append(Particle(self.firework.pos.x, self.firework.pos.y, False, self.colours))

    def show(self, win):
        pg.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:
                    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 = 25
        self.remove = False
        self.explosion_radius = ra.randint(25, 90) # 修改
        self.life = 0
        self.acc = vector(0, 0)
        self.trails = []
        self.prev_posx = [-10] * 10
        self.prev_posy = [-10] * 10
        if self.firework:
            self.vel = vector(0, -ra.randint(17, 20))
            self.size = 5
            self.colour = colour
            for i in range(5):
                self.trails.append(Trail(i, self.size, True))
        else:
            self.vel = vector(ra.uniform(-1, 1), ra.uniform(-1, 1))
            self.vel.x *= ra.randint(7, self.explosion_radius + 2)
            self.vel.y *= ra.randint(7, self.explosion_radius + 2)
            self.size = ra.randint(2, 4)
            self.colour = ra.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.8
            self.vel.y *= 0.8
        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

    def show(self, win):
        pg.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 50 > self.life > 10:
            ran = ra.randint(0, 30)
            if ran == 0:
                self.remove = True
        elif self.life > 50:
            ran = ra.randint(0, 5)
            if ran == 0:
                self.remove = True

    def trail_update(self):
        self.prev_posx.pop()
        self.prev_posx.insert(0, int(self.pos.x))
        self.prev_posy.pop()
        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 + 1], self.prev_posy[n + 1])
            else:
                t.get_pos(self.prev_posx[n + 5], self.prev_posy[n + 5])

# 痕迹类
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_colors[n]
            self.size = int(size - n / 2)
        else:
            self.colour = (random.randint(25,255), random.randint(125,250), random.randint(100, 255))
            self.size = size - 2
            if self.size < 0:
                self.size = 0

    def get_pos(self, x, y):
        self.pos = vector(x, y)

    def show(self, win):
        pg.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)
    pg.display.update()


def fire():
    screen = pg.display.set_mode((screenWidth, screenHeight - 66))
    clock = pg.time.Clock()
    fireworks = [Firework() for i in range(2)]
    running = True
    font = pg.font.Font("/System/Library/Fonts/PingFang.ttc", 99)

    text1 = "蛇 年 大 吉 ! "
    text1_color = (255, 10, 20)
    text2 = "Happy Spring Festival!   "
    text2_color = (255, 110, 20)

    # 用于动态显示的变量
    displayed_text1 = ""
    displayed_text2 = ""

    text1_length = len(text1)
    text2_length = len(text2)
    index1 = 0  # 当前显示到的字符索引,针对文本1
    index2 = 0  # 当前显示到的字符索引,针对文本2

    while running:
        clock.tick(30)  # 调整为适合的帧率
        for event in pg.event.get():
            if event.type == pg.QUIT:
                running = False

        screen.fill((10, 10, 0))

        # 动态添加字符给第一个文本
        if index1 < text1_length:
            displayed_text1 += text1[index1]
            index1 += 1

        rendered_text1 = font.render(displayed_text1, True, text1_color)
        text1_width = rendered_text1.get_width()
        text1_height = rendered_text1.get_height()

        # 从上面进入屏幕
        text1_y = (screenWidth + text1_width) // 2 - 830  # 从上面进入屏幕
        text1_x = (screenHeight + text1_height) // 2 - 53
        screen.blit(rendered_text1, (text1_x, text1_y))

        # 动态添加字符给第二个文本
        if index2 < text2_length and index1 >= text1_length:  # 只有当第一个文本完成后才开始第二个文本
            displayed_text2 += text2[index2]
            index2 += 1

        rendered_text2 = font.render(displayed_text2, True, text2_color)
        text2_width = rendered_text2.get_width()
        text2_height = rendered_text2.get_height()

        # 从右侧进入屏幕
        text2_x = screenWidth - text2_width - (index2 * 5)  # 每次显示一个字符,逐渐向左移动
        text2_y = text1_y + text1_height + 20  # 与第一个文本下方有一些间隔
        screen.blit(rendered_text2, (text2_x, text2_y))

        if ra.randint(0, 10) == 1:
            fireworks.append(Firework())

        update(screen, fireworks)

    pg.quit()
    quit()


if __name__ == "__main__":

    fire()

4.运行效果

程序运行时,用户将看到以下效果:

• 烟花从屏幕底部随机位置发射,上升到一定高度后爆炸。

• 爆炸产生的粒子向四周扩散,形成绚丽的烟花效果。

• 粒子运动轨迹以渐变色显示,增强视觉效果。

• 屏幕上动态显示祝福语,如“蛇年大吉”和“Happy Spring Festival”。

录屏2025-01-23 13

5.技术要点

• Pygame库的使用:通过Pygame实现图形绘制、事件处理和音频播放。

• 向量运算:利用pygame.math.Vector2处理粒子的位置和速度。

• 随机化:通过随机生成颜色、速度和粒子数量,增加烟花效果的多样性。

• 动态文本显示:通过逐字符渲染实现文本的动态显示效果。

6.结论

本文通过一个完整的Python程序,展示了如何利用Pygame库实现烟花效果。程序通过模拟烟花的发射、爆炸和粒子轨迹,结合动态文本显示,为用户带来沉浸式的视觉体验。读者可以通过本文的代码解析和技术要点,快速掌握如何实现类似的视觉效果,并应用于其他项目中。

7.未来改进方向

• 优化性能:通过减少粒子数量或优化渲染逻辑,提高程序的运行效率。

• 增加特效:引入更多烟花类型(如环形、螺旋形等),丰富视觉效果。

• 交互性增强:允许用户通过键盘或鼠标操作控制烟花的发射位置和频率。


希望本文能为读者提供有价值的参考,激发更多创意和探索。

欢迎点赞、转发、收藏、关注!!!

相关文章
|
2月前
|
存储 小程序 Python
农历节日倒计时:基于Python的公历与农历日期转换及节日查询小程序
### 农历节日倒计时:基于Python的公历与农历日期转换及节日查询小程序 该程序通过`lunardate`库实现公历与农历的日期转换,支持闰月和跨年处理,用户输入农历节日名称后,可准确计算距离该节日还有多少天。功能包括农历节日查询、倒计时计算等。欢迎使用! (239字符)
217 86
|
3月前
|
JSON 开发工具 git
基于Python和pygame的植物大战僵尸游戏设计源码
本项目是基于Python和pygame开发的植物大战僵尸游戏,包含125个文件,如PNG图像、Python源码等,提供丰富的游戏开发学习素材。游戏设计源码可从提供的链接下载。关键词:Python游戏开发、pygame、植物大战僵尸、源码分享。
|
4月前
|
人工智能 搜索推荐 API
使用 Python holidays 库获取中国节日
使用 Python holidays 库获取中国节日
293 2
|
4月前
|
数据采集 前端开发 Python
Python pygame 实现游戏 彩色 五子棋 详细注释 附源码 单机版
Python pygame 实现游戏 彩色 五子棋 详细注释 附源码 单机版
117 0
|
6月前
|
定位技术 Python
【python】python基于pygame坦克大战游戏设计(源码+图像+操作说明)【独一无二】
【python】python基于pygame坦克大战游戏设计(源码+图像+操作说明)【独一无二】
132 1
|
6月前
|
Linux iOS开发 MacOS
【Python】Python基于Pygame疯狂赛车游戏设计(源码+报告)【独一无二】
【Python】Python基于Pygame疯狂赛车游戏设计(源码+报告)【独一无二】
159 1
|
6月前
|
Python
【python】python基于pygame弹珠游戏设计(源码)【独一无二】
【python】python基于pygame弹珠游戏设计(源码)【独一无二】
107 0
|
6月前
|
算法 数据安全/隐私保护 UED
【python】python基于Pygame扫雷游戏设计实现(源码+报告)【独一无二】
【python】python基于Pygame扫雷游戏设计实现(源码+报告)【独一无二】
141 0
|
8月前
|
开发框架 Python
Python的`pygame`库用于2D游戏开发,涵盖图形、音频和输入处理。
【6月更文挑战第21天】Python的`pygame`库用于2D游戏开发,涵盖图形、音频和输入处理。要开始,先通过`pip install pygame`安装。基本流程包括:初始化窗口、处理事件循环、添加游戏元素(如玩家和敌人)、响应用户输入、更新游戏状态及结束条件。随着项目发展,可逐步增加复杂性。
204 1
|
8月前
|
Linux 开发工具 开发者
Pygame是一个免费且开源的Python库
【6月更文挑战第12天】Pygame是一个免费且开源的Python库
158 3

热门文章

最新文章