预览

代码结构概述

这段代码使用了 pygame 库来创建一个动态的图形窗口,绘制一个心形图案,并在其中显示闪烁的文本。代码主要分为以下几个部分:

  1. 初始化和设置
  2. 心形曲线的计算
  3. 粒子类的定义
  4. 生成粒子
  5. 文本设置
  6. 主循环

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()
    
    

    补充解说

    让我详细解释这个代码的运行逻辑:

    1. 初始化阶段
    # 初始化基本设置
    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
    
    1. 粒子系统
    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)
    
    1. 粒子移动和重置逻辑
    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
        # 重置其他属性
    

    运行流程:

    1. 初始化

    2. 创建10000个随机分布的粒子
    3. 每个粒子有随机的位置、大小、速度和生命周期
    4. 主循环

    while running:
        screen.fill(BLACK)  # 清空屏幕
        
        # 绘制心形轮廓
        # 更新和绘制所有粒子
        for particle in particles:
            particle.move()
            particle.draw(screen)
    
    1. 粒子运动机制
    2. 每个粒子按照自己的角度和速度移动
    3. 当粒子生命周期结束时,通过reset()函数重生在心形曲线上
    4. 这创造了粒子似乎在"靠近"心形的视觉效果

    关于"靠近"效果的实现:

  • 这个效果是通过粒子的生命周期系统实现的
  • 当粒子消失时,它们会在心形曲线上重生(reset()函数)
  • 随着时间推移,越来越多的粒子会重生在心形曲线上
  • 这创造了粒子群似乎在向心形聚集的视觉效果
  • 核心实现在reset()函数:

    def reset(self):
        angle_rad = random.uniform(0, math.pi * 2)
        x, y = heart_curve(angle_rad)  # 在心形曲线上选择新位置
    

    这不是真正的"靠近"运动,而是通过粒子的死亡和重生机制,逐渐在心形轮廓上形成粒子群,创造出视觉上的聚集效果。

    作者:百年孤独_

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python:动态粒子爱心

    发表回复