阅读 8387

100行整个烟花仰望星空,纪念人类登月|Python 主题月

本文正在参加「Python主题月」,详情查看 活动链接

效果预览

作业要求的效果

XDJMM,我还是那只不守妇道的花喵。

其实阿婆主不想纪念人类登月的,阿婆主只想发文章的,实在想不出标题,阿婆主想到要头秃,一口老~血都快吐出来了,算了,我们用100行写个烟花庆祝下人类登月日😜。

1. 初始化

引入pygame

import pygame
复制代码

初始化

pygame.init()
复制代码

设置宽高

win = pygame.display.set_mode((1024, 640))
复制代码

设置标题

pygame.display.set_caption("今夜仰望星空,纪念人类登月")
复制代码

设置时钟

clock = pygame.time.Clock()
复制代码

填充背景颜色

win.fill((94,255,162))
复制代码

渲染,区别于上一篇【手摸手】154行写个冒险岛,用前端看得懂的语言描述,换个写法

while running:
    # 设置时钟,60帧
    clock.tick(60)
    # 监听关闭事件,不然会无法用x关闭窗口
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    pygame.display.update()
# 非运行状态则退出
pygame.quit()
quit()
复制代码

看看效果先: WX20210720-205752@2x.png

你没有看错,是的,一片黑

代码整理如下:

import pygame

# 初始化
pygame.init()
# 设置舞台宽高
win = pygame.display.set_mode((1024, 640))
# 设置标题
pygame.display.set_caption("今夜仰望星空,纪念人类登月")
# 设置时钟
clock = pygame.time.Clock()
# 默认运行
running = True
# 填充背景颜色
win.fill((0,0,0))

# 渲染
while running:
    # 设置时钟,60帧
    clock.tick(60)
    # 监听关闭事件,不然会无法用x关闭窗口
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    pygame.display.update()
# 非运行状态则退出
pygame.quit()
quit()
复制代码

2. 实现一个点向上升起再落下

引入矢量

vector = pygame.math.Vector2
复制代码

定义重力矢量

gravity = vector(0, 0.3)
复制代码

抽取变量

# 设置舞台宽高
DISPLAY_WIDTH, DISPLAY_HEIGHT = 1024,640
win = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
复制代码

新建一个烟花,我们将在DISPLAY_WIDTH随机位置生成,你也可以稍微缩短,或者定义具体位置,上升到粒子为白色3个255

fireworks = Particle(randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, (255, 255, 255))
复制代码

Particle类还没定义,我们后面定义,记得引入

from random import randint
复制代码

显示更新改为方法,因为后面需要时时更新,把窗口和烟花都传进去

    # 显示更新
    update(win, fireworks)
复制代码

定义update方法

def update(win, fw):
    # 我们需要定Particle类方法update来更新我们的烟花
    fw.update(win)
    # 显示更新
    pygame.display.update()
复制代码

定义类及初始化方法

class Particle:
    def __init__(self, x, y, colour):
        # 获取坐标
        self.pos = vector(x, y)
        # 默认未移除状态,暂时无用
        self.remove = False
        # 设置爆炸半径
        self.explosion_radius = randint(8, 12)
        # 生命值
        self.life = 0
        # 上升、下降加速度参数
        self.vel = vector(0, -randint(16, 19))
        # 粒子大小
        self.size = 2
        # 粒子颜色
        self.colour = colour
复制代码

移动方法

    def move(self):
        # 上升、下降都是加速度
        self.vel += gravity
        self.pos += self.vel
        # 生命累加,预留
        self.life += 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 update(self, win):
        self.move()
        self.show(win)
复制代码

来试试

WX20210720-205843@2x.png

WX20210720-205853@2x.png

上升有了,加速度也有了,随机屏幕位置也有了

完整代码如下:

import pygame
from random import randint

# 初始化
pygame.init()
# 设置舞台宽高
DISPLAY_WIDTH, DISPLAY_HEIGHT = 1024,640
win = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
# 设置标题
pygame.display.set_caption("今夜仰望星空,纪念人类登月")
# 设置时钟
clock = pygame.time.Clock()
# 默认运行
running = True
# 填充背景颜色
win.fill((0,0,0))
# 矢量
vector = pygame.math.Vector2
# 重力矢量
gravity = vector(0, 0.3)

def update(win, fw):
    # 我们需要定Particle类方法update来更新我们的烟花
    fw.update(win)
    # 显示更新
    pygame.display.update()

class Particle:
    def __init__(self, x, y, colour):
        # 获取坐标
        self.pos = vector(x, y)
        # 默认未移除状态,预留
        self.remove = False
        # 设置爆炸半径
        self.explosion_radius = randint(8, 12)
        # 生命值
        self.life = 0
        # 上升、下降加速度参数
        self.vel = vector(0, -randint(16, 19))
        # 粒子大小
        self.size = 2
        # 粒子颜色
        self.colour = colour

    # 移动
    def move(self):
        # 上升、下降都是加速度
        self.vel += gravity
        self.pos += self.vel
        # 生命累加,预留
        self.life += 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 update(self, win):
        self.move()
        self.show(win)

fireworks = Particle(randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, (255, 255, 255))

# 渲染
while running:
    # 设置时钟,60帧
    clock.tick(60)
    # 监听关闭事件,不然会无法用x关闭窗口
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    # 显示更新
    update(win, fireworks)
# 非运行状态则退出
pygame.quit()
quit()
复制代码

3. 随机位置生成多个烟花

随机生成第一个烟花,改为列表

fireworks = [Particle(randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, (255, 255, 255))]
复制代码

创建一个新的烟花,设置频率。这边取0,1,2,总共三个数,意思是1/3多概率生成一个烟花,添加到fireworks列表,60fps x 1/3 = 20个,预计1s有20个烟花生成

    if randint(0, 2) == 1:
        fireworks.append(Particle(randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, (255, 255, 255)))
复制代码

全局update方法更新下,循环列表

def update(win, fireworks):
    # 我们需要定Particle类方法update来更新我们的烟花
    for fw in fireworks:
        fw.update(win)
    # 显示更新
    pygame.display.update()
复制代码

看看效果

WX20210720-205939@2x.png

4. 拆分Particle类,新建Firework类

这边我们需要拆分Particle类,让Particle类更纯粹,去处理粒子相关的,我们新建一个Firework类来处理烟花放出的第一个例子,管理其状态,比如爆炸,移除等等

这边先解决一个问题,这些点都重复渲染了,一个点的上升过程,过去的位置不应该渲染出来,我们把之前的设置背景win.fill((0,0,0))改在while执行

# 渲染
while running:
    # 设置时钟,60帧
    clock.tick(60)
    # 监听关闭事件,不然会无法用x关闭窗口
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    # 创建一个新的烟花,设置频率
    if randint(0, 2) == 1:
        fireworks.append(Firework())
    # 设置背景
    win.fill((0,0,0))
    # 显示更新
    update(win, fireworks)
# 非运行状态则退出
pygame.quit()
quit()
复制代码

这样粒子在上升的过程就不会残留轨迹

定义Firework类

class Firework:
    def __init__(self):
        # 烟花初始喷射出去粒子的颜色
        self.colour = (255,255,255)
        # 在舞台宽度内随机生成粒子
        self.firework = Particle(randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, True, self.colour)
        # 默认非爆炸状态
        self.exploded = False
        # 粒子数组
        self.particles = []
        # 爆炸生成最小最大粒子数量矢量
        self.min_max_particles = vector(400, 600)
复制代码

播放每一帧,我们在全局方法update会执行本方法用于更新粒子

def update(self, win):
        # 非爆炸状态
        if not self.exploded:
            # 粒子移动
            self.firework.move()
            # 粒子显示在舞台
            self.show(win)
            # 当y轴大于0爆炸
            if self.firework.vel.y >= 0:
                # 更改为爆炸状态
                self.exploded = True
                # 执行爆炸方法
                self.explode()
        # 爆炸状态产生的多个粒子,分别移动、显示
        else:
            for particle in self.particles:
                # 粒子移动
                particle.move()
                # 显示舞台
                particle.show(win)
复制代码

爆炸之后我们添加之前预设范围的随机数量粒子,在下一帧就会更新出来

    def explode(self):
        # 爆炸产生的粒子
        for i in range(randint(self.min_max_particles.x, self.min_max_particles.y)):
            self.particles.append(Particle(self.firework.pos.x, self.firework.pos.y, False, self.colour))
复制代码

初始粒子显示

    def show(self, win):
        pygame.draw.circle(win, self.colour, (int(self.firework.pos.x), int(self.firework.pos.y)), self.firework.size)
复制代码

5. Particle类改造

新增firework参数判断是否初始粒子

class Particle:
    def __init__(self, x, y, firework, colour):
        # 是否初始化烟花射出的粒子
        self.firework = firework
        # 获取坐标
        self.pos = vector(x, y)
        # 默认未移除状态,预留
        self.remove = False
        # 设置爆炸半径
        self.explosion_radius = randint(8, 12)
        # 生命值
        self.life = 0
        # 初始升空的粒子
        if self.firework:
            # 上升
            self.vel = vector(0, -randint(16, 19))
            # 粒子大小
            self.size = 2
            # 粒子颜色
            self.colour = colour
        else:
            # 上下左右爆炸放大效果
            self.vel = vector(uniform(-1, 1), uniform(-1, 1))
            self.vel.x *= randint(12, 15)
            self.vel.y *= randint(12, 15)
            # 粒子大小
            self.size = 1
            # 粒子颜色
            self.colour = colour
复制代码

粒子移动方法

    # 移动
    def move(self):
        # 非初始粒子,收缩计算
        if not self.firework:
            self.vel.x *= 0.8
            self.vel.y *= 0.8
        # 上升、下降都是加速度
        self.vel += gravity
        self.pos += self.vel
复制代码

粒子显示方法

    def show(self, win):
        pygame.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size)
复制代码

删除Particle类update方法

    # 播放每一帧
    def update(self, win):
        self.move()
        self.show(win)
复制代码

看下效果

WX20210720-210014@2x.png

6. bug修复

有几个问题:

  1. 不会消失
  2. 不是圆形的

Particle类调整移动方法,检查粒子是否在爆炸半径范围外,如果是,删除掉

        # 检查粒子是否在爆炸半径范围外,如果是,删除掉
        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
复制代码

Firework类,移除方法

    # 移除计算
    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
复制代码

全局update方法调整下

def update(win, fireworks):
    # 我们需要定Particle类方法update来更新我们的烟花
    for fw in fireworks:
        fw.update(win)
        # 移除烟花
        if fw.remove():
            fireworks.remove(fw)
    # 显示更新
    pygame.display.update()
复制代码

看下效果

2021-07-20 21.02.55.gif

这个效果其实也还可以,我们需要爆炸更久点再消失

Particle类移动方法添加

        # 生命累加
        self.life += 1
复制代码

2021-07-20 18.58.37.gif 看,这熊样,不消失

我们来写个衰变的方法

Particle类添加衰变方法

    # 粒子的随机衰变,随机判断是否移除
    def decay(self):
        # 较早阶段衰变的几率很小
        if 50 > self.life > 10:
            # 1/30
            ran = randint(0, 30)
            if ran == 0:
                self.remove = True
        elif self.life > 50:
            # 1/5
            ran = randint(0, 5)
            if ran == 0:
                self.remove = True
复制代码

再看看效果

2021-07-20 21.01.24.gif 效果还行

完整代码:

import pygame
from random import randint, uniform, choice
import math

# 初始化
pygame.init()
# 设置舞台宽高
DISPLAY_WIDTH, DISPLAY_HEIGHT = 1024,640
win = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
# 设置标题
pygame.display.set_caption("今夜仰望星空,纪念人类登月")
# 设置时钟
clock = pygame.time.Clock()
# 默认运行
running = True
# 矢量
vector = pygame.math.Vector2
# 重力矢量
gravity = vector(0, 0.3)

def update(win, fireworks):
    # 我们需要定Particle类方法update来更新我们的烟花
    for fw in fireworks:
        fw.update(win)
        # 移除烟花
        if fw.remove():
            fireworks.remove(fw)
    # 显示更新
    pygame.display.update()

class Firework:
    def __init__(self):
        # 烟花初始喷射出去粒子的颜色
        self.colour = (255,255,255)
        # 烟花,在舞台宽度内随机生成
        self.firework = Particle(randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, True, self.colour)
        # 默认非爆炸状态
        self.exploded = False
        # 粒子数组
        self.particles = []
        # 爆炸生成最小最大粒子数量矢量
        self.min_max_particles = vector(400, 600)

    # 播放每一帧,我们在全局方法update会执行本方法用于更新粒子
    def update(self, win):
        # 非爆炸状态
        if not self.exploded:
            # 粒子移动
            self.firework.move()
            # 粒子显示在舞台
            self.show(win)
            # 当y轴大于0爆炸
            if self.firework.vel.y >= 0:
                # 更改为爆炸状态
                self.exploded = True
                # 执行爆炸方法
                self.explode()
        # 爆炸状态产生的多个粒子,分别移动、显示
        else:
            for particle in self.particles:
                # 粒子移动
                particle.move()
                # 显示舞台
                particle.show(win)

    # 爆炸之后我们添加之前预设范围的随机数量粒子,在下一帧就会更新出来
    def explode(self):
        # 爆炸产生的粒子
        for i in range(randint(self.min_max_particles.x, self.min_max_particles.y)):
            self.particles.append(Particle(self.firework.pos.x, self.firework.pos.y, False, self.colour))

    # 显示
    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.remove = False
        # 设置爆炸半径
        self.explosion_radius = randint(8, 12)
        # 生命值
        self.life = 0
        # 初始升空的粒子
        if self.firework:
            # 上升
            self.vel = vector(0, -randint(16, 19))
            # 粒子大小
            self.size = 2
            # 粒子颜色
            self.colour = colour
        else:
            # 上下左右爆炸放大效果
            self.vel = vector(uniform(-1, 1), uniform(-1, 1))
            self.vel.x *= randint(12, 15)
            self.vel.y *= randint(12, 15)
            # 粒子大小
            self.size = 1
            # 粒子颜色
            self.colour = colour

    # 移动
    def move(self):
        # 非初始粒子,收缩计算
        if not self.firework:
            self.vel.x *= 0.8
            self.vel.y *= 0.8
        # 上升、下降都是加速度
        self.vel += gravity
        self.pos += self.vel
        # 检查粒子是否在爆炸半径范围外,如果是,删除掉
        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.life += 1

    # 绘制
    def show(self, win):
        pygame.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size)

    # 粒子的随机衰变,随机判断是否移除
    def decay(self):
        # 较早阶段衰变的几率很小
        if 50 > self.life > 10:
            # 1/30
            ran = randint(0, 30)
            if ran == 0:
                self.remove = True
        elif self.life > 50:
            # 1/5
            ran = randint(0, 5)
            if ran == 0:
                self.remove = True
    # 播放每一帧
    # def update(self, win):
    #     self.move()
    #     self.show(win)

# 随机生成第一个烟花
fireworks = [Firework()]

# 渲染
while running:
    # 设置时钟,60帧
    clock.tick(60)
    # 监听关闭事件,不然会无法用x关闭窗口
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    # 创建一个新的烟花,设置频率
    if randint(0, 2) == 1:
        fireworks.append(Firework())
    # 设置背景
    win.fill((0,0,0))
    # 显示更新
    update(win, fireworks)
# 非运行状态则退出
pygame.quit()
quit()
复制代码

7. 优化细节

添加爆炸音效

全局upadate方法添加

def update(win, fireworks):
    # 我们需要定Particle类方法update来更新我们的烟花
    for fw in fireworks:
        fw.update(win)
        # 爆炸且生命0播放音效
        if fw.exploded is True and fw.particles[0].life == 0:
            # 设置爆炸声音,需要初始化后设置
            explode_sound = pygame.mixer.Sound('bomb.wav')
            # 设置爆炸声音音量
            explode_sound.set_volume(0.5)
            # 播放爆炸音效
            explode_sound.play()
        # 移除烟花
        if fw.remove():
            fireworks.remove(fw)
    # 显示更新
    pygame.display.update()
复制代码

弄个月球背景

上bing图弄个

1920.jpg

# 加载背景图片
sky_surface = pygame.image.load('bg.jpg').convert()
复制代码

渲染改为

while running:
    # 设置时钟,60帧
    clock.tick(60)
    # 监听关闭事件,不然会无法用x关闭窗口
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    # 创建一个新的烟花,设置频率
    if randint(0, 2) == 1:
        fireworks.append(Firework())
    # 设置背景
    win.blit(sky_surface,(0,0))
    update(win, fireworks)
# 非运行状态则退出
pygame.quit()
quit()
复制代码

烟花颜色改为金黄色

class Firework:
    def __init__(self):
        # 烟花初始喷射出去粒子的颜色,和爆炸粒子的颜色目前是一致的,可以按需调整
        self.colour = (255, 255, 185)
复制代码

最后的效果

完整代码

import pygame
from random import randint, uniform, choice
import math

# 初始化
pygame.init()
# 设置舞台宽高
DISPLAY_WIDTH, DISPLAY_HEIGHT = 1024,640
win = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
# 设置标题
pygame.display.set_caption("今夜仰望星空,纪念人类登月")
# 设置时钟
clock = pygame.time.Clock()
# 默认运行
running = True
# 矢量
vector = pygame.math.Vector2
# 重力矢量
gravity = vector(0, 0.3)

def update(win, fireworks):
    # 我们需要定Particle类方法update来更新我们的烟花
    for fw in fireworks:
        fw.update(win)
        # 爆炸且生命0播放音效
        if fw.exploded is True and fw.particles[0].life == 0:
            # 设置爆炸声音,需要初始化后设置
            explode_sound = pygame.mixer.Sound('bomb.wav')
            # 设置爆炸声音音量
            explode_sound.set_volume(0.5)
            # 播放爆炸音效
            explode_sound.play()
        # 移除烟花
        if fw.remove():
            fireworks.remove(fw)
    # 显示更新
    pygame.display.update()

class Firework:
    def __init__(self):
        # 烟花初始喷射出去粒子的颜色
        # self.colour = (255,200,140)
        self.colour = (255, 255, 185)
        # 烟花,在舞台宽度内随机生成
        self.firework = Particle(randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, True, self.colour)
        # 默认非爆炸状态
        self.exploded = False
        # 粒子数组
        self.particles = []
        # 爆炸生成最小最大粒子数量矢量
        self.min_max_particles = vector(400, 600)

    # 播放每一帧,我们在全局方法update会执行本方法用于更新粒子
    def update(self, win):
        # 非爆炸状态
        if not self.exploded:
            # 粒子移动
            self.firework.move()
            # 粒子显示在舞台
            self.show(win)
            # 当y轴大于0爆炸
            if self.firework.vel.y >= 0:
                # 更改为爆炸状态
                self.exploded = True
                # 执行爆炸方法
                self.explode()
        # 爆炸状态产生的多个粒子,分别移动、显示
        else:
            for particle in self.particles:
                # 粒子移动
                particle.move()
                # 显示舞台
                particle.show(win)

    # 爆炸之后我们添加之前预设范围的随机数量粒子,在下一帧就会更新出来
    def explode(self):
        # 爆炸产生的粒子
        for i in range(randint(self.min_max_particles.x, self.min_max_particles.y)):
            self.particles.append(Particle(self.firework.pos.x, self.firework.pos.y, False, self.colour))

    # 显示
    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.remove = False
        # 设置爆炸半径
        self.explosion_radius = randint(8, 12)
        # 生命值
        self.life = 0
        # 初始升空的粒子
        if self.firework:
            # 上升
            self.vel = vector(0, -randint(16, 19))
            # 粒子大小
            self.size = 2
            # 粒子颜色
            self.colour = colour
        else:
            # 上下左右爆炸放大效果
            self.vel = vector(uniform(-1, 1), uniform(-1, 1))
            self.vel.x *= randint(12, 15)
            self.vel.y *= randint(12, 15)
            # 粒子大小
            self.size = 1
            # 粒子颜色
            self.colour = colour

    # 移动
    def move(self):
        # 非初始粒子,收缩计算
        if not self.firework:
            self.vel.x *= 0.8
            self.vel.y *= 0.8
        # 上升、下降都是加速度
        self.vel += gravity
        self.pos += self.vel
        # 检查粒子是否在爆炸半径范围外,如果是,删除掉
        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.life += 1

    # 绘制
    def show(self, win):
        pygame.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size)

    # 粒子的随机衰变,随机判断是否移除
    def decay(self):
        # 较早阶段衰变的几率很小
        if 50 > self.life > 10:
            # 1/30
            ran = randint(0, 30)
            if ran == 0:
                self.remove = True
        elif self.life > 50:
            # 1/5
            ran = randint(0, 5)
            if ran == 0:
                self.remove = True
    # 播放每一帧
    # def update(self, win):
    #     self.move()
    #     self.show(win)

# 随机生成第一个烟花
fireworks = [Firework()]
# 加载背景图片
sky_surface = pygame.image.load('bg.jpg').convert()
# 渲染
while running:
    # 设置时钟,60帧
    clock.tick(60)
    # 监听关闭事件,不然会无法用x关闭窗口
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    # 创建一个新的烟花,设置频率
    if randint(0, 2) == 1:
        fireworks.append(Firework())
    # 设置背景
    win.blit(sky_surface,(0,0))
    update(win, fireworks)
# 非运行状态则退出
pygame.quit()
quit()
复制代码

去掉注释和空行

import pygame
from random import randint, uniform, choice
import math
pygame.init()
DISPLAY_WIDTH, DISPLAY_HEIGHT = 1024,640
win = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
pygame.display.set_caption("今夜仰望星空,纪念人类登月")
clock = pygame.time.Clock()
running = True
vector = pygame.math.Vector2
gravity = vector(0, 0.3)
def update(win, fireworks):
    for fw in fireworks:
        fw.update(win)
        if fw.exploded is True and fw.particles[0].life == 0:
            explode_sound = pygame.mixer.Sound('bomb.wav')
            explode_sound.set_volume(0.5)
            explode_sound.play()
        if fw.remove():
            fireworks.remove(fw)
    pygame.display.update()
class Firework:
    def __init__(self):
        self.colour = (255, 255, 185)
        self.firework = Particle(randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, True, self.colour)
        self.exploded = False
        self.particles = []
        self.min_max_particles = vector(400, 600)
    def update(self, win):
        if not self.exploded:
            self.firework.move()
            self.show(win)
            if self.firework.vel.y >= 0:
                self.exploded = True
                self.explode()
        else:
            for particle in self.particles:
                particle.move()
                particle.show(win)
    def explode(self):
        for i in range(randint(self.min_max_particles.x, self.min_max_particles.y)):
            self.particles.append(Particle(self.firework.pos.x, self.firework.pos.y, False, self.colour))
    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.remove = False
        self.explosion_radius = randint(8, 12)
        self.life = 0
        if self.firework:
            self.vel = vector(0, -randint(16, 19))
            self.size = 2
            self.colour = colour
        else:
            self.vel = vector(uniform(-1, 1), uniform(-1, 1))
            self.vel.x *= randint(12, 15)
            self.vel.y *= randint(12, 15)
            self.size = 1
            self.colour = colour
    def move(self):
        if not self.firework:
            self.vel.x *= 0.8
            self.vel.y *= 0.8
        self.vel += gravity
        self.pos += self.vel
        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.life += 1
    def show(self, win):
        pygame.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size)
    def decay(self):
        if 50 > self.life > 10:
            if randint(0, 30) == 0: self.remove = True
        elif self.life > 50:
            if randint(0, 5) == 0: self.remove = True
fireworks = [Firework()]
sky_surface = pygame.image.load('bg.jpg').convert()
while running:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    if randint(0, 2) == 1: fireworks.append(Firework())
    win.blit(sky_surface,(0,0))
    update(win, fireworks)
pygame.quit()
quit()
复制代码

正好100行!今夜仰望星空,纪念人类登月!

作业

  1. 自己整理优化下代码
  2. 自己动参数做出不同烟花效果
  3. 根据今天这节课教授的知识点,课后每个同学还原下【4K】70周年国庆烟花高清全记录!美不胜收!

以上。

文章分类
后端
文章标签