Python:动态粒子爱心
预览
代码结构概述
这段代码使用了 pygame
库来创建一个动态的图形窗口,绘制一个心形图案,并在其中显示闪烁的文本。代码主要分为以下几个部分:
- 初始化和设置
- 心形曲线的计算
- 粒子类的定义
- 生成粒子
- 文本设置
- 主循环
1. 初始化和设置
import pygame
import random
import math
import os
# 初始化pygame
pygame.init()
# 屏幕尺寸
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Dynamic Particle Heart")
- 导入库:首先导入所需的库,
pygame
用于图形处理,random
用于生成随机数,math
用于数学计算。 - 初始化pygame:调用
pygame.init()
来初始化所有的 pygame 模块。 - 设置屏幕尺寸:定义窗口的宽度和高度,并创建一个窗口。
- 设置窗口标题:使用
set_caption
设置窗口的标题。
2. 颜色定义
# 颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
PURPLE = (128, 0, 128) # 紫色的RGB值
- 定义颜色:使用 RGB 颜色模式定义了白色、黑色和紫色。
3. 心形曲线的计算
# 心形方程
def heart_curve(t):
x = 16 * math.sin(t) ** 3
y = 13 * math.cos(t) - 5 * math.cos(2 * t) - 2 * math.cos(3 * t) - math.cos(4 * t)
return x, y
- 心形方程:定义了一个函数
heart_curve
,接受一个参数t
(角度),并计算心形曲线的 x 和 y 坐标。这个公式通过三角函数生成心形的坐标。
4. 计算心形的最大宽度和高度
def get_heart_dimensions():
max_x, min_x, max_y, min_y = -float('inf'), float('inf'), -float('inf'), float('inf')
for i in range(0, 360, 1):
angle_rad = math.radians(i)
x, y = heart_curve(angle_rad)
if x > max_x: max_x = x
if x < min_x: min_x = x
if -y > max_y: max_y = -y # 注意:这里取负是因为pygame坐标系y轴向下
if -y < min_y: min_y = -y
width = max_x - min_x
height = max_y - min_y
return width, height
- 计算心形的尺寸:这个函数遍历从 0 到 360 度的每个角度,计算心形的坐标,并找到心形的最大和最小 x、y 值,以便计算出心形的宽度和高度。
5. 粒子类的定义
class Particle:
def __init__(self, x, y):
self.x = x
self.y = y
self.size = random.randint(2, 5)
self.color = PURPLE
self.angle = random.uniform(0, math.pi * 2)
self.speed = random.uniform(1, 3)
self.lifetime = random.randint(50, 100)
def move(self):
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
self.size -= 0.05
self.lifetime -= 1
if self.size <= 0 or self.lifetime <= 0:
self.reset()
def reset(self):
angle_rad = random.uniform(0, math.pi * 2)
x, y = heart_curve(angle_rad)
self.x = x * scale_factor + WIDTH // 2
self.y = -y * scale_factor + HEIGHT // 2
self.size = random.randint(2, 5)
self.angle = random.uniform(0, math.pi * 2)
self.speed = random.uniform(1, 3)
self.lifetime = random.randint(50, 100)
def draw(self, screen):
s = pygame.Surface((self.size * 2, self.size * 2), pygame.SRCALPHA)
pygame.draw.circle(s, (*self.color[:3], self.lifetime * 2.55), (self.size, self.size), self.size)
screen.blit(s, (self.x - self.size, self.y - self.size))
- 粒子类:定义了一个
Particle
类,表示一个粒子。- 初始化方法:设置粒子的初始位置、大小、颜色、角度、速度和生命周期。
- 移动方法:更新粒子的位置,减少大小和生命周期。如果粒子的大小或生命周期小于等于0,则重置粒子。
- 重置方法:随机生成新的位置、大小、角度和速度。
- 绘制方法:在屏幕上绘制粒子。
6. 生成粒子
# 生成初始粒子
particles = [Particle(random.uniform(-width/2, width/2) * scale_factor + WIDTH // 2,
random.uniform(-height/2, height/2) * scale_factor + HEIGHT // 2) for _ in range(10000)]
- 创建粒子:生成 10,000 个粒子,并将它们存储在
particles
列表中。每个粒子的位置是随机的,基于心形的尺寸和缩放因子。
7. 文本设置
# 字体设置
font_size = 48 # 字体大小
font_path = "./test.ttf" # 确保字体文件在当前目录下
font = pygame.font.Font(font_path, font_size) # 使用指定字体
text = "多想再见你 哪怕一眼匆匆就别离"
text_surface = font.render(text, True, WHITE) # 渲染文本
text_rect = text_surface.get_rect(center=(WIDTH // 2, HEIGHT // 2)) # 文本居中
- 设置字体:定义字体大小和路径,渲染文本并计算文本的矩形区域,以便后续居中显示。
8. 主循环
# 主循环
running = True
clock = pygame.time.Clock()
blink_counter = 0
blink_rate = 10 # 每10帧闪烁一次
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(BLACK)
# 绘制心形曲线
points = []
for i in range(0, 360, 1):
angle_rad = math.radians(i)
x, y = heart_curve(angle_rad)
x = x * scale_factor + WIDTH // 2
y = -y * scale_factor + HEIGHT // 2
points.append((x, y))
pygame.draw.lines(screen, PURPLE, False, points, 2) # 使用lines代替polygon
# 更新和绘制粒子
for particle in particles:
particle.move()
particle.draw(screen)
# 绘制闪烁的文本
if blink_counter % blink_rate < blink_rate / 2: # 每隔一段时间闪烁
# 随机生成颜色
random_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
text_surface = font.render(text, True, random_color) # 渲染文本
screen.blit(text_surface, text_rect) # 绘制文本
blink_counter += 1
pygame.display.flip()
clock.tick(60)
pygame.quit()
- 主循环:程序的核心部分。
- 事件处理:检查是否有退出事件。
- 清屏:用黑色填充屏幕。
- 绘制心形曲线:计算心形的每个点并绘制。
- 更新和绘制粒子:让每个粒子移动并绘制在屏幕上。
- 绘制闪烁的文本:每隔一定的帧数,随机生成文本颜色并绘制文本。
- 更新显示:调用
pygame.display.flip()
更新屏幕,clock.tick(60)
控制帧率为 60 帧每秒。
9. 退出
pygame.quit()
- 退出程序:当主循环结束后,调用
pygame.quit()
关闭窗口并清理资源。
总结
这段代码结合了图形绘制、动画效果和文本渲染,展示了如何使用 pygame
创建一个动态的视觉效果。通过理解每个部分的功能,你可以更好地掌握 pygame
的使用,并进行更复杂的项目开发。
完整代码
import pygame
import random
import math
import os
# 初始化pygame
pygame.init()
# 屏幕尺寸
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Dynamic Particle Heart")
# 颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
PURPLE = (128, 0, 128) # 紫色的RGB值
# 心形方程
def heart_curve(t):
x = 16 * math.sin(t) ** 3
y = 13 * math.cos(t) - 5 * math.cos(2 * t) - 2 * math.cos(3 * t) - math.cos(4 * t)
return x, y
# 计算心形的最大宽度和高度
def get_heart_dimensions():
max_x, min_x, max_y, min_y = -float('inf'), float('inf'), -float('inf'), float('inf')
for i in range(0, 360, 1):
angle_rad = math.radians(i)
x, y = heart_curve(angle_rad)
if x > max_x: max_x = x
if x < min_x: min_x = x
if -y > max_y: max_y = -y # 注意:这里取负是因为pygame坐标系y轴向下
if -y < min_y: min_y = -y
width = max_x - min_x
height = max_y - min_y
return width, height
width, height = get_heart_dimensions()
scale_factor = min(WIDTH / (width * 1.2), HEIGHT / (height * 1.2)) # 增加一点额外的空间
# 粒子类
class Particle:
def __init__(self, x, y):
self.x = x
self.y = y
self.size = random.randint(2, 5)
self.color = PURPLE
self.angle = random.uniform(0, math.pi * 2)
self.speed = random.uniform(1, 3)
self.lifetime = random.randint(50, 100)
def move(self):
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
self.size -= 0.05
self.lifetime -= 1
if self.size <= 0 or self.lifetime <= 0:
self.reset()
def reset(self):
angle_rad = random.uniform(0, math.pi * 2)
x, y = heart_curve(angle_rad)
self.x = x * scale_factor + WIDTH // 2
self.y = -y * scale_factor + HEIGHT // 2
self.size = random.randint(2, 5)
self.angle = random.uniform(0, math.pi * 2)
self.speed = random.uniform(1, 3)
self.lifetime = random.randint(50, 100)
def draw(self, screen):
s = pygame.Surface((self.size * 2, self.size * 2), pygame.SRCALPHA)
pygame.draw.circle(s, (*self.color[:3], self.lifetime * 2.55), (self.size, self.size), self.size)
screen.blit(s, (self.x - self.size, self.y - self.size))
# 生成初始粒子
particles = [Particle(random.uniform(-width/2, width/2) * scale_factor + WIDTH // 2,
random.uniform(-height/2, height/2) * scale_factor + HEIGHT // 2) for _ in range(10000)]
# 字体设置
font_size = 48 # 字体大小
font_path = "./test.ttf" # 确保字体文件在当前目录下
font = pygame.font.Font(font_path, font_size) # 使用指定字体
text = "多想再见你 哪怕一眼匆匆就别离"
text_surface = font.render(text, True, WHITE) # 渲染文本
text_rect = text_surface.get_rect(center=(WIDTH // 2, HEIGHT // 2)) # 文本居中
# 主循环
running = True
clock = pygame.time.Clock()
blink_counter = 0
blink_rate = 10 # 每10帧闪烁一次
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(BLACK)
# 绘制心形曲线
points = []
for i in range(0, 360, 1):
angle_rad = math.radians(i)
x, y = heart_curve(angle_rad)
x = x * scale_factor + WIDTH // 2
y = -y * scale_factor + HEIGHT // 2
points.append((x, y))
pygame.draw.lines(screen, PURPLE, False, points, 2) # 使用lines代替polygon
# 更新和绘制粒子
for particle in particles:
particle.move()
particle.draw(screen)
# 绘制闪烁的文本
if blink_counter % blink_rate < blink_rate / 2: # 每隔一段时间闪烁
# 随机生成颜色
random_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
text_surface = font.render(text, True, random_color) # 渲染文本
screen.blit(text_surface, text_rect) # 绘制文本
blink_counter += 1
pygame.display.flip()
clock.tick(60)
pygame.quit()
补充解说
让我详细解释这个代码的运行逻辑:
- 初始化阶段:
# 初始化基本设置
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
# 心形方程定义
def heart_curve(t):
x = 16 * math.sin(t) ** 3
y = 13 * math.cos(t) - 5 * math.cos(2 * t) - 2 * math.cos(3 * t) - math.cos(4 * t)
return x, y
- 粒子系统:
class Particle:
def __init__(self, x, y):
# 初始化粒子位置、大小、速度等属性
self.x = x
self.y = y
self.size = random.randint(2, 5)
self.angle = random.uniform(0, math.pi * 2)
self.speed = random.uniform(1, 3)
self.lifetime = random.randint(50, 100)
- 粒子移动和重置逻辑:
def move(self):
# 移动粒子
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
self.size -= 0.05
self.lifetime -= 1
# 当粒子生命周期结束时重置
if self.size <= 0 or self.lifetime <= 0:
self.reset()
def reset(self):
# 重置粒子到心形曲线上的随机位置
angle_rad = random.uniform(0, math.pi * 2)
x, y = heart_curve(angle_rad)
self.x = x * scale_factor + WIDTH // 2
self.y = -y * scale_factor + HEIGHT // 2
# 重置其他属性
运行流程:
-
初始化:
- 创建10000个随机分布的粒子
- 每个粒子有随机的位置、大小、速度和生命周期
-
主循环:
while running:
screen.fill(BLACK) # 清空屏幕
# 绘制心形轮廓
# 更新和绘制所有粒子
for particle in particles:
particle.move()
particle.draw(screen)
- 粒子运动机制:
- 每个粒子按照自己的角度和速度移动
- 当粒子生命周期结束时,通过
reset()
函数重生在心形曲线上 - 这创造了粒子似乎在"靠近"心形的视觉效果
关于"靠近"效果的实现:
- 这个效果是通过粒子的生命周期系统实现的
- 当粒子消失时,它们会在心形曲线上重生(
reset()
函数) - 随着时间推移,越来越多的粒子会重生在心形曲线上
- 这创造了粒子群似乎在向心形聚集的视觉效果
核心实现在reset()
函数:
def reset(self):
angle_rad = random.uniform(0, math.pi * 2)
x, y = heart_curve(angle_rad) # 在心形曲线上选择新位置
这不是真正的"靠近"运动,而是通过粒子的死亡和重生机制,逐渐在心形轮廓上形成粒子群,创造出视觉上的聚集效果。