cjjJasonchen 发表于 2023-7-10 16:27:16

【pygame】 用粒子特效生成火焰 -- 7/25最新更新

本帖最后由 cjjJasonchen 于 2023-7-25 11:23 编辑



效果演示:




使用python+pygame制作的粒子特效


在鼠标点击处生成火焰



背景颜色默认灰色,

按1:背景调整为白色
按2:背景调整为黑色

左键释放红色火焰,右键释放蓝色火焰

火焰生成后20秒自动消失

若场景中存在多个火焰,可能导致卡顿,帧数变低



在线展示:

https://www.bilibili.com/video/BV1bx4y197kv



源码:
希望各位大大给个改进方案,能不能在画质不变差的条件下减少火焰的内存占用,或者帮我改改代码也可以啊

求评分

import pygame
import sys
import random
from pygame.locals import *


class Particle(pygame.sprite.Sprite):
    def __init__(self,color=(255,255,255),position=,radius=0,speed=,time=-1):
      super().__init__()
      self.position = position
      self.image = pygame.Surface().convert_alpha()
      self.rect = self.image.get_rect()
      self.rect.centerx,self.rect.centery = position,position
      self.color = color
      self.radius = radius
      self.speed = speed
      self.time = time

      pygame.draw.rect(self.image,(0,0,0,0),)
      
      pygame.draw.circle(self.image,
                           color=self.color,
                           center=(radius,radius),
                           radius=self.radius)

      

    def update(self):
      self.time -= 1
      self.rect.centerx += self.speed
      self.rect.centery += self.speed
      #self.rect.centerx,self.rect.centery = self.position,self.position


class SpurtFire(Particle):
    def __init__(self, position,
               color=(255,255,255,random.randint(50,255)),
               time=,
               speed=[[-2,2],[-2,-2]],
               num = 4,
               radius=,
               burn_time=600,
               timer = 0):
      self.particles = pygame.sprite.Group()
      self.position = position
      self.color = color
      self.time = time
      self.speed = speed
      self.num = num
      self.radius = radius
      self.burn_time = burn_time
      self.timer0 = timer
      self.timer = timer

    def update(self):
            self.particles.update()
            for i in self.particles:
                if i.time == 0:
                  self.particles.remove(i)
                i.speed = ,self.speed),random.randint(self.speed,self.speed)]
            if self.timer == 0:
                self.timer = self.timer0
                self.create_particle()
            else:
                self.timer -= 1
            self.burn_time -= 1
      

    def draw(self,screen):
      self.particles.draw(screen)
      
    def create_particle(self):
      for i in range(self.num):
            self.particles.add(Particle(color=self.color,
                                    position=,self.position],
                                    radius=random.randint(self.radius,self.radius),
                                    speed=,self.speed),random.randint(self.speed,self.speed)],
                                    time = random.randint(self.time,self.time)))
      





if __name__ == "__main__":
    size = width, height = 800,600

    screen = pygame.display.set_mode(size)

    pygame.display.set_caption("title")

    clock = pygame.time.Clock()

    delay = 60 # 延时计时器
    time = 0

    # 是否全屏
    fullscreen = False
    screen_change = False

    running = True

    bg = (85,85,85)

    # 粒子效果列表
    particles = []

    # 火焰1对象实例化
    #fire1 = SpurtFire()
    #particles.append(fire1)

    while running:
      clock.tick(60)

      # 得到鼠标位置
      pos = pygame.mouse.get_pos()

      # 检测是否全屏
      if fullscreen and screen_change:
            screen = pygame.display.set_mode(size,FULLSCREEN,HWSURFACE)
            screen_change = False
      elif screen_change:
            screen = pygame.display.set_mode(size)
            screen_change = False

      for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
               
            if event.type == MOUSEBUTTONDOWN:
                if event.button == 1:
                  '''
                  particles.append(SpurtFire((pos,pos-3),
                                             color=(100,100,100,random.randint(180,200)),
                                             time=,
                                             speed=[[-1,1],[-1,-1]],
                                             num = 1,
                                             radius = ,
                                             burn_time = 1200))'''
                  particles.append(SpurtFire(pos,
                                             color=(255,0,0,random.randint(50,200)),
                                             time=,
                                             speed=[[-2,+2],[-1,-1]],
                                             num = 50,
                                             radius = ,
                                             burn_time = 1200))
                  particles.append(SpurtFire((pos,pos-3),
                                             color=(255,255,0,random.randint(50,200)),
                                             time=,
                                             speed=[[-2,+2],[-1,-1]],
                                             num = 20,
                                             radius = ,
                                             burn_time = 1200))
                  particles.append(SpurtFire((pos,pos-3),
                                             color=(255,255,200,random.randint(50,200)),
                                             time=,
                                             speed=[[-1,+1],[-1,-1]],
                                             num = 10,
                                             radius = ,
                                             burn_time = 1200))

                if event.button == 3:
                  particles.append(SpurtFire(pos,
                                             color=(0,0,255,random.randint(50,200)),
                                             time=,
                                             speed=[[-2,+2],[-1,-1]],
                                             num = 50,
                                             radius = ,
                                             burn_time = 1200))
                  particles.append(SpurtFire((pos,pos-3),
                                             color=(0,225,225,random.randint(50,200)),
                                             time=,
                                             speed=[[-2,+2],[-1,-1]],
                                             num = 20,
                                             radius = ,
                                             burn_time = 1200))
                  particles.append(SpurtFire((pos,pos-3),
                                             color=(200,200,225,random.randint(50,200)),
                                             time=,
                                             speed=[[-1,+1],[-1,-1]],
                                             num = 10,
                                             radius = ,
                                             burn_time = 1200))


            if event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                  pygame.quit()
                  sys.exit()
                  
                #F11切换全屏
                elif event.key == K_F11:
                  fullscreen = not fullscreen
                  screen_change = True

                elif event.key == K_1:
                  bg = (255,255,255)

                elif event.key == K_2:
                  bg = (0,0,0)


      #画背景
      screen.fill(bg)

      #画 xxxx
      for particle in particles:
            particle.draw(screen)

      #刷新
      for particle in particles:
            particle.update()
            if not particle.burn_time:
                particles.remove(particle)

      # 刷新界面
      pygame.display.update()





第一页 (后续正在持续跟新。。。。)

手把手教你手搓火球!!!教程开始!
1. 从一个粒子开始!
带火看到这里应该都已经知道了(不知道的托出去看看标题{:10_256:}),这个火焰是一个粒子特效~

所以,首先,我们需要一个粒子~

我们需要在屏幕上画一个圆,这个圆就是我们看到的粒子啦~

然后把这个圆画到一个Surface对象上,这个Surface对象就是粒子的本体,之后粒子的移动会以此为基础

注意! 这个对象要是有透明度的!(见代码)



然后 这个圆需要有一些变量(额 或者叫属性):=============================================

1. 粒子(圆)的颜色 :color

2. 粒子(圆)的半径 : radius# Surface对象的大小为 [(宽:radius*2, 高: radius*2)]

3. 粒子(圆心位置)的初始位置 : position =

4. 粒子的速度 :speed =

5. 粒子的生命值(存在时间) : life



之后 还需要一个方法:刷新——update()方法================================================================

这个方法每帧都要调用一次!

1. 将位置更新,x轴和y轴位置分别 加上 <x方向的速度和y方向的速度>的值

2. 将生命值life 减1





ok ~ 现在我们来看看生成粒子的代码:{:10_305:}

import pygame
import sys
import random
from pygame.locals import *


class Particle(pygame.sprite.Sprite):
    def __init__(self,color=(255,255,255),position=,radius=0,speed=,life = -1):
      super().__init__()
      self.position = position
      self.image = pygame.Surface().convert_alpha()
      self.rect = self.image.get_rect()
      self.rect.centerx,self.rect.centery = position,position
      self.color = color
      self.radius = radius
      self.speed = speed
      self.life = life

      pygame.draw.rect(self.image,(0,0,0,0),)
      
      pygame.draw.circle(self.image,
                           color=self.color,
                           center=(radius,radius),
                           radius=self.radius)

    def update(self):
      self.rect.x += self.speed
      self.rect.y += self.speed
      self.life -= 1



怎么样,是不是很简单呢~{:10_305:}


第二页 (后续正在持续跟新。。。。)







2. 试着生成粒子

好的,既然已经看到这里了,我们就尝试一下在屏幕上生成一些粒子吧~{:10_254:}


先看看代码,把下边的代码追加在上面那段代码的后面{:10_312:}:


if __name__ == "__main__":

    # 颜色常量
    WHITE = (255,255,255)
    BLACK = (0,0,0)

    size = width, height = 800,600

    screen = pygame.display.set_mode(size)

    pygame.display.set_caption("title")

    clock = pygame.time.Clock()

    delay = 60 # 延时计时器(1秒)

    # 是否全屏
    fullscreen = False
    screen_change = False

    # 背景颜色设定
    bg_color = WHITE

    # 粒子组
    particles = pygame.sprite.Group()

    running = True

    while running:

      # 设定帧数
      clock.tick(60)

      # 延时计时器刷新
      if delay == 0:
            delay = 60

      delay -= 1

      # 检测是否全屏
      if fullscreen and screen_change:
            screen = pygame.display.set_mode(size,FULLSCREEN,HWSURFACE)
            screen_change = False
      elif screen_change:
            screen = pygame.display.set_mode(size)
            screen_change = False

      # 事件检测
      for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            # 鼠标
            if event.type == MOUSEBUTTONDOWN:
                if event.button == 1: # 左键按下,获取鼠标位置
                  pos = pygame.mouse.get_pos()
                  print(f"左键按下:{pos}")

                  
                  particles.add(Particle(color=(255,0,0,100),
                                           radius=4,
                                           position=pos,
                                           speed=,
                                           life=120)
                                  )
                  
                if event.button == 3: # 右键按下,获取鼠标位置
                  pos = pygame.mouse.get_pos()
                  print(f"右键按下:{pos}")

                  
                  particles.add(Particle(color=(255,255,0,100),
                                           radius=random.randint(4,10),
                                           position=pos,
                                           speed=,
                                           life=120)
                                  )
                  

            # 按键按下事件
            if event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                  pygame.quit()
                  sys.exit()
                  
                #F11切换全屏
                if event.key == K_F11:
                  fullscreen = not fullscreen
                  screen_change = True

            # 按键抬起事件
            if event.type == KEYUP:
                pass


      #画背景
      screen.fill(bg_color)

      #画 xxxx
      particles.draw(screen)

      # 刷新xxx
      particles.update()

      # 刷新界面
      pygame.display.update()




运行一下:



先看看左键的连续点击效果~
发现左键连续点击之后粒子按照我们的要求排成了一条直线,非常好{:10_298:}


于是呢 你准备让粒子向随机方向移动,于是你把粒子方向设定成随机(-1,1)的值(在上面代码中代表右键点击)~{:10_257:}

如下图:



结果你悲催的发现:粒子排成了三条直线,没有向想象中粒子向随即方向运动,<集点成面>的效果{:10_266:}

那么该怎么办呢{:10_284:}?


请自行思考后看下一页~

第三页 (后续正在持续跟新。。。。)

3. 集点成面

好! 同学们都想出来了吗?{:10_256:}

是的,没错,有一个很简单的方法————在每一次调用粒子的update()方法之前,先更改一下speed的值就可以了

有()些鱼油可能会说:欸?在输入speed的时候给他输入四个值————[,],然后再把粒子的update方法改成这样:

    # 原版:
    def update(self):
      self.rect.x += self.speed
      self.rect.y += self.speed
      self.life -= 1

    # 更改后:
    def update(self):
      self.rect.x += self.speed,self.speed)]
      self.rect.y += self.speed,self.speed]
      self.life -= 1这种操作看起来完全没问题,我们今天用这种操作也确实可以~


但是,    楼主没有选择这种方式,因为这样会使粒子类的实例化变得过于复杂,{:10_269:}
这会不便于我们使用它去做其他的事情例如:爆炸效果,风效果,剑气效果等等




正确的选择应该是————{:10_279:}



创建一个类,用来管理这些粒子{:10_304:}

SpurtParticle()类


老规矩,这个类需要一些变量 ==============================================

1. 粒子生成的初始位置:position

2. 粒子的颜色:color =

3. 粒子的生命值: particle_life =

4. 粒子的速度:speed = [.]

5. 每一轮生成粒子的数量:num =

6. 粒子的半径:radius =

7. 火焰的存在时间(帧):life = 600

8. 延迟计时器:delay = 0


还有用来存放粒子的——particles = pygame.sprite.Group()




需要vdraw() 和 update() 两个方法===================


update()
首先,通过for循环生成num数量的粒子,加入粒子组中

然后,分别调用他们的update()方法

life-1,particle_life-1

如果生命值为0,删除粒子


draw()===================


依次调用粒子的draw()方法

===============================

好,看看代码~{:10_335:}

class SpurtParticle(Particle):
    def __init__(self,
               position=,
               color=(255,255,255,100),
               particle_life=,
               speed=[,],
               num = ,
               radius=,
               life=600):
      self.particles = pygame.sprite.Group()
      self.position = position
      self.color = color
      self.particle_life = particle_life
      self.speed = speed
      self.num = num
      self.radius = radius
      self.life = life

    def update(self):
      for i in range(random.randint(self.num,self.num)):
            self.particles.add(Particle(color=self.color,
                                        position=,self.position],
                                        radius=random.randint(self.radius,self.radius),
                                        speed=,self.speed),
                                             random.randint(self.speed,self.speed)],
                                        life = random.randint(self.particle_life,self.particle_life)))
               
      self.particles.update()
      
      for i in self.particles:
            if i.life == 0:
                self.particles.remove(i)
            i.speed = ,self.speed),random.randint(self.speed,self.speed)]

      self.life -= 1
      

    def draw(self,screen):
      self.particles.draw(screen)

怎么样,是不是已经学会了呢~{:10_305:}





第4页 (后续正在持续跟新。。。。)




4. 火焰!火焰!


下面我们来更改一下主循环的代码~要改的地方不多,
先把存放粒子的组换成存放喷射粒子对象的列表,然后改一下后面的刷新和绘制就可以啦~{:10_304:}

下面是源码:
if __name__ == "__main__":

    # 颜色常量
    WHITE = (255,255,255)
    BLACK = (0,0,0)

    size = width, height = 800,600

    screen = pygame.display.set_mode(size)

    pygame.display.set_caption("title")

    clock = pygame.time.Clock()

    delay = 60 # 延时计时器(1秒)

    # 是否全屏
    fullscreen = False
    screen_change = False

    # 背景颜色设定
    bg_color = WHITE

    # 粒子列表
    sps = []

    running = True

    while running:

      # 设定帧数
      clock.tick(60)

      # 延时计时器刷新
      if delay == 0:
            delay = 60

      delay -= 1

      # 检测是否全屏
      if fullscreen and screen_change:
            screen = pygame.display.set_mode(size,FULLSCREEN,HWSURFACE)
            screen_change = False
      elif screen_change:
            screen = pygame.display.set_mode(size)
            screen_change = False

      # 事件检测
      for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            # 鼠标
            if event.type == MOUSEBUTTONDOWN:
                if event.button == 1: # 左键按下,获取鼠标位置
                  pos = pygame.mouse.get_pos()
                  print(f"左键按下:{pos}")

                  
                  sps.append(SpurtParticle(color=(255,0,0,100),
                                          radius=,
                                          num=,
                                          position=,pos+3],
                                          speed=[[-2,2],[-1,-1]],
                                          particle_life=,
                                          life=600)
                                  )
                  
                if event.button == 3: # 右键按下,获取鼠标位置
                  pos = pygame.mouse.get_pos()
                  print(f"右键按下:{pos}")

                  
                  sps.append(SpurtParticle(color=(255,255,0,100),
                                          radius=,
                                          num=,
                                          position=pos,
                                          speed=[[-2,2],[-1,-1]],
                                          particle_life=,
                                          life=600)
                                  )
                  

            # 按键按下事件
            if event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                  pygame.quit()
                  sys.exit()
                  
                #F11切换全屏
                if event.key == K_F11:
                  fullscreen = not fullscreen
                  screen_change = True

            # 按键抬起事件
            if event.type == KEYUP:
                pass


      #画背景
      screen.fill(bg_color)

      #画 xxxx
      for sp in sps:
            sp.draw(screen)

      # 刷新xxx
      for sp in sps:
            sp.update()
            if sp.life == 0:
                sps.remove(sp)

      # 刷新界面
      pygame.display.update()




注意!在运行之前请不要忘记导入库并粘贴上类(和前面一样,能看到现在的你一定明白吧~)

效果展示:









第五页 (请看下一页~)



static/image/hrline/4.gif

怎么样,看到这里是不是已经可以手搓火焰啦{:10_257:}~


学会的同学请把你的个性火焰发在评论区吧~
让大家看看你的脑洞{:10_305:}

{:10_297:}{:10_297:}{:10_281:}{:10_281:}别忘了评分!评论!{:10_281:}{:10_281:}{:10_297:}{:10_297:}

大火相互学习哦~



哈哈哈!没想到吧本教程还在更新!
楼主经过这些天的努力,终于在昨天半夜完成了代码优化!现在,它可以正式被使用在游戏开发中!
先看看对比!

这是以前的:



这是现在的:




(上面的图片上看起来不太好看,但当他动起来的话至少看上去开始可以接受的)


新的火焰看起来小了很多,但是它是真正可以使用的,可以当作飞机、导弹的尾焰,飞行的火球,等等


当然啦,一定要画质的话有没有问题,但是同样,会卡。。。。


新版的原理是把粒子的颜色改成动态传参,随着移动改变粒子的颜色,使其越来越淡,

通过几个能从深色变为浅色大粒子代替了 原来使用n多个浅色小粒子,

并且新设定了timer参数:空开几帧生成一次粒子

代码如下:

import pygame
import sys
import random
from pygame.locals import *


class Particle(pygame.sprite.Sprite):
    def __init__(self,color=(255,255,255),position=,radius=0,speed=,time=-1):
      super().__init__()
      self.position = position
      self.image = pygame.Surface().convert_alpha()
      self.rect = self.image.get_rect()
      self.rect.centerx,self.rect.centery = position,position
      self.color = color
      self.radius = radius
      self.speed = speed
      self.time = time

      pygame.draw.rect(self.image,(0,0,0,0),)

      

    def update(self):
      self.time -= 1
      self.rect.centerx += self.speed
      self.rect.centery += self.speed
      pygame.draw.circle(self.image,
                           color=self.color,
                           center=(self.radius,self.radius),
                           radius=self.radius)
      #self.rect.centerx,self.rect.centery = self.position,self.position


class SpurtFire(Particle):
    def __init__(self, position,
               color=,
               time=,
               speed=[[-2,2],[-2,-2]],
               num = 4,
               radius=,
               burn_time=600,
               timer = 0):
      self.particles = pygame.sprite.Group()
      self.position = position
      self.color = color
      self.time = time
      self.speed = speed
      self.num = num
      self.radius = radius
      self.burn_time = burn_time
      self.timer0 = timer
      self.timer = timer

    def update(self):
            self.particles.update()
            for i in self.particles:
                if i.time == 0:
                  self.particles.remove(i)
                i.speed = ,self.speed),random.randint(self.speed,self.speed)]
                i.color = self.color/self.time*i.time
                #print(i.color, "=", self.color,"/",self.time, "*", i.time,i.color is self.color)
            if self.timer == 0:
                self.timer = self.timer0
                self.create_particle()
            else:
                self.timer -= 1
            self.burn_time -= 1
      

    def draw(self,screen):
      self.particles.draw(screen)
      
    def create_particle(self):
      for i in range(self.num):
            self.particles.add(Particle(color=self.color.copy(),
                                    position=,self.position],
                                    radius=random.randint(self.radius,self.radius),
                                    speed=,self.speed),random.randint(self.speed,self.speed)],
                                    time = random.randint(self.time,self.time)
                                        )
                               )
      





if __name__ == "__main__":
    size = width, height = 800,600

    screen = pygame.display.set_mode(size)

    pygame.display.set_caption("title")

    clock = pygame.time.Clock()

    delay = 60 # 延时计时器
    time = 0

    # 是否全屏
    fullscreen = False
    screen_change = False

    running = True

    bg = (85,85,85)

    # 粒子效果列表
    particles = []


    while running:
      clock.tick(60)

      # 得到鼠标位置
      pos = pygame.mouse.get_pos()

      # 检测是否全屏
      if fullscreen and screen_change:
            screen = pygame.display.set_mode(size,FULLSCREEN,HWSURFACE)
            screen_change = False
      elif screen_change:
            screen = pygame.display.set_mode(size)
            screen_change = False

      for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
               
            if event.type == MOUSEBUTTONDOWN:
                if event.button == 1:
                  particles.append(SpurtFire(pos,
                                             color=,
                                             time=,
                                             speed=[[-2,+2],[-2,-1]],
                                             num = 4,
                                             radius = ,
                                             burn_time = 1200,
                                             timer=10))
                  particles.append(SpurtFire(pos,
                                             color=,
                                             time=,
                                             speed=[[-1,+1],[-1,-1]],
                                             num = 4,
                                             radius = ,
                                             burn_time = 1200,
                                             timer=5))
                  particles.append(SpurtFire(pos,
                                             color=,
                                             time=,
                                             speed=[[-1,+1],[-1,-1]],
                                             num = 4,
                                             radius = ,
                                             burn_time = 1200,
                                             timer=5))

                if event.button == 3:
                  particles.append(SpurtFire(,pos],
                                             color=,
                                             time=,
                                             speed=[[-2,+2],[-2,-1]],
                                             num = 4,
                                             radius = ,
                                             burn_time = 1200,
                                             timer=5))
                  
                  particles.append(SpurtFire(,pos+3],
                                             color=,
                                             time=,
                                             speed=[[-1,+1],[-2,-1]],
                                             num = 4,
                                             radius = ,
                                             burn_time = 1200,
                                             timer=5))
                  particles.append(SpurtFire(,pos+3],
                                             color=,
                                             time=,
                                             speed=[[-1,+1],[-2,-1]],
                                             num = 4,
                                             radius = ,
                                             burn_time = 1200,
                                             timer=5))

                  
                  


            if event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                  pygame.quit()
                  sys.exit()
                  
                #F11切换全屏
                elif event.key == K_F11:
                  fullscreen = not fullscreen
                  screen_change = True

                elif event.key == K_1:
                  bg = (255,255,255)

                elif event.key == K_2:
                  bg = (0,0,0)


      #画背景
      screen.fill(bg)

      #画 xxxx
      for particle in particles:
            particle.draw(screen)

      #刷新
      for particle in particles:
            particle.update()
            if not particle.burn_time:
                particles.remove(particle)

      # 刷新界面
      pygame.display.update()







希望可以在更多作品中看到这个特效!




我记得刚发出来的时候有鱼油说我这火焰像是在模糊的背景中看到的,嘿嘿嘿,今天打脸滴来了{:10_302:}————

旧版最高画质展示:



怎么样,是不是其实还是很清楚的{:10_298:}


再看看新版:




这里很明显就可以对比出来新版的更真实

虽然在极限画质下二者的性能相差不大,但新版无论是在低画质还是高画质都表现更好~




好啦下面看看代码上是怎么实现的


先看看蓝色的:
                  particles.append(SpurtFire(,pos],
                                             color=,
                                             time=,
                                             speed=[[-3,+3],[-2,-1]],
                                             num = 100,
                                             radius = ,
                                             burn_time = 1200,
                                             timer=0))
                  
                  particles.append(SpurtFire(,pos+3],
                                             color=,
                                             time=,
                                             speed=[[-2,+2],[-2,-1]],
                                             num = 50,
                                             radius = ,
                                             burn_time = 1200,
                                             timer=0))
                  particles.append(SpurtFire(,pos+3],
                                             color=,
                                             time=,
                                             speed=[[-1,+1],[-2,-1]],
                                             num = 20,
                                             radius = ,
                                             burn_time = 1200,
                                             timer=0))
嗨嗨嗨~

这样大家对比前面的代码是不是就知道该如何

平衡火焰的画质和性能了呢~

{:10_298:}现在这个帖子差不多就该结束了,鱼油们还有什么想知道的别忘了来告诉我哦!评论区见!






ttk设置火焰颜色

展示:



直接给大伙看代码喽,记得安装pygame库和ttkbootstrap库哦!

""" 这个版本是专门用来体验的,如果要追求性能请调节SpurtFire的timer"""

import pygame
import sys
import random
import ttkbootstrap as tk
import ttkbootstrap.constants as tkc
from pygame.locals import *
from threading import Thread, current_thread


class Particle(pygame.sprite.Sprite):
    def __init__(self,color=(255,255,255),position=,radius=0,speed=,time=-1):
      super().__init__()
      self.position = position
      self.image = pygame.Surface().convert_alpha()
      self.rect = self.image.get_rect()
      self.rect.centerx,self.rect.centery = position,position
      self.color = color
      self.radius = radius
      self.speed = speed
      self.time = time

      pygame.draw.rect(self.image,(0,0,0,0),)

      

    def update(self):
      self.time -= 1
      self.rect.centerx += self.speed
      self.rect.centery += self.speed
      pygame.draw.circle(self.image,
                           color=self.color,
                           center=(self.radius,self.radius),
                           radius=self.radius)


class SpurtFire(Particle):
    def __init__(self, position,
               color=,
               time=,
               speed=[[-2,2],[-2,-2]],
               num = 4,
               radius=,
               burn_time=600,
               timer = 0):
      self.particles = pygame.sprite.Group()
      self.position = position
      self.color = color
      self.time = time
      self.speed = speed
      self.num = num
      self.radius = radius
      self.burn_time = burn_time
      self.timer0 = timer
      self.timer = timer

    def update(self):
            self.particles.update()
            for i in self.particles:
                if i.time == 0:
                  self.particles.remove(i)
                i.speed = ,self.speed),random.randint(self.speed,self.speed)]
                i.color = self.color/self.time*i.time
            if self.timer == 0:
                self.timer = self.timer0
                self.create_particle()
            else:
                self.timer -= 1
            self.burn_time -= 1
      

    def draw(self,screen):
      self.particles.draw(screen)
      
    def create_particle(self):
      for i in range(self.num):
            self.particles.add(Particle(color=self.color.copy(),
                                    position=,self.position],
                                    radius=random.randint(self.radius,self.radius),
                                    speed=,self.speed),random.randint(self.speed,self.speed)],
                                    time = random.randint(self.time,self.time)
                                        )
                               )
      

def set_up():

    global color,num_list,particle_size,root

    #变量
    color = {"outside":,
             "inside":,
             "core":,
             "bg":}
    num_list =
    particle_size =
   

    # 创建窗口
    root = tk.Window(themename="superhero")
    root.geometry("700x700+300+10")

    tk.Label(root, text="请输入外焰、內焰和焰芯的颜色",font=("楷体", 30)).pack(padx=5,pady=10)


    # 提示

    def set_c(color):
      c_dic= {
            "红":(,,),
            "蓝":(,,),
            "紫":(,,),
            "绿":(,,)
            }
      outer_e1.delete(0, tk.END)
      outer_e1.insert(0,c_dic)
      outer_e2.delete(0, tk.END)
      outer_e2.insert(0,c_dic)
      outer_e3.delete(0, tk.END)
      outer_e3.insert(0,c_dic)
      outer_e4.delete(0, tk.END)
      outer_e4.insert(0,c_dic)

      inside_e1.delete(0, tk.END)
      inside_e1.insert(0,c_dic)
      inside_e2.delete(0, tk.END)
      inside_e2.insert(0,c_dic)
      inside_e3.delete(0, tk.END)
      inside_e3.insert(0,c_dic)
      inside_e4.delete(0, tk.END)
      inside_e4.insert(0,c_dic)

      core_e1.delete(0, tk.END)
      core_e1.insert(0,c_dic)
      core_e2.delete(0, tk.END)
      core_e2.insert(0,c_dic)
      core_e3.delete(0, tk.END)
      core_e3.insert(0,c_dic)
      core_e4.delete(0, tk.END)
      core_e4.insert(0,c_dic)
            
   
    menubar1 = tk.Menu(root)
    menubar1.add_command(label = "示例1:蓝色火焰",command = lambda : set_c(("蓝")))
    menubar1.add_command(label = "示例2:红色火焰",command = lambda : set_c(("红")))
    menubar1.add_command(label = "示例2:紫色火焰",command = lambda : set_c(("紫")))
    menubar1.add_command(label = "示例2:绿色火焰",command = lambda : set_c(("绿")))
   
    key_l = tk.Label(root, text="点它!——>    (红)(绿)(蓝)(透明度)    <——点它!",font=("楷体", 15)) # 提示
    key_l.pack(padx = 5,pady = 5)

    def popup(event):
      menubar1.post(event.x_root,event.y_root)

    key_l.bind("<Button-1>",popup)
    key_l.bind("<Button-3>",popup)
   
   
   
    # 主题结构
    frame1 = tk.Frame(root,width = 400,height = 300)
    frame1.pack(padx=5,pady=5)

   
    # 输入检验
    def validate_0to255(x):
      if x.isdigit() and 0<=int(x)<=255:
            return True
      elif x == "":
            return False
      else:
            return False


    validate1 = root.register(validate_0to255) # 检验赋值


    tk.Label(frame1, text="",font=("楷体", 40)).grid(row=0,column=0)
    # 外焰
    tk.Label(frame1, text="外焰颜色(RGBA):(",font=("楷体", 20)).grid(row=1,column=0)
   
    outer_e1 = tk.Entry(frame1,width=3,font=("楷体", 20),bootstyle="PRIMARY", validate="focus", validatecommand=(validate1, '%P'))
    outer_e1.grid(row=1,column=1)
    outer_e1.insert(0,0)

    tk.Label(frame1, text=")(",font=("楷体", 20)).grid(row=1,column=2)

    outer_e2 = tk.Entry(frame1,width=3,font=("楷体", 20),bootstyle="PRIMARY", validate="focus", validatecommand=(validate1, '%P'))
    outer_e2.grid(row=1,column=3)
    outer_e2.insert(0,0)

    tk.Label(frame1, text=")(",font=("楷体", 20)).grid(row=1,column=4)

   
    outer_e3 = tk.Entry(frame1,width=3,font=("楷体", 20),bootstyle="PRIMARY", validate="focus", validatecommand=(validate1, '%P'))
    outer_e3.grid(row=1,column=5)
    outer_e3.insert(0,0)

    tk.Label(frame1, text=")(",font=("楷体", 20)).grid(row=1,column=6)

    outer_e4 = tk.Entry(frame1,width=3,font=("楷体", 20),bootstyle="PRIMARY", validate="focus", validatecommand=(validate1, '%P'))
    outer_e4.grid(row=1,column=7)
    outer_e4.insert(0,0)

    tk.Label(frame1, text=")",font=("楷体", 20)).grid(row=1,column=8)
   
   
    tk.Label(frame1, text="",font=("楷体", 30)).grid(row=2,column=0)
    # 内焰
    tk.Label(frame1, text="内焰颜色(RGBA):(",font=("楷体", 20)).grid(row=3,column=0)
   
    inside_e1 = tk.Entry(frame1,width=3,font=("楷体", 20),bootstyle="PRIMARY", validate="focus", validatecommand=(validate1, '%P'))
    inside_e1.grid(row=3,column=1)
    inside_e1.insert(0,0)

    tk.Label(frame1, text=")(",font=("楷体", 20)).grid(row=3,column=2)

    inside_e2 = tk.Entry(frame1,width=3,font=("楷体", 20),bootstyle="PRIMARY", validate="focus", validatecommand=(validate1, '%P'))
    inside_e2.grid(row=3,column=3)
    inside_e2.insert(0,0)

    tk.Label(frame1, text=")(",font=("楷体", 20)).grid(row=3,column=4)

   
    inside_e3 = tk.Entry(frame1,width=3,font=("楷体", 20),bootstyle="PRIMARY", validate="focus", validatecommand=(validate1, '%P'))
    inside_e3.grid(row=3,column=5)
    inside_e3.insert(0,0)

    tk.Label(frame1, text=")(",font=("楷体", 20)).grid(row=3,column=6)

    inside_e4 = tk.Entry(frame1,width=3,font=("楷体", 20),bootstyle="PRIMARY", validate="focus", validatecommand=(validate1, '%P'))
    inside_e4.grid(row=3,column=7)
    inside_e4.insert(0,0)

    tk.Label(frame1, text=")",font=("楷体", 20)).grid(row=3,column=8)


    tk.Label(frame1, text="",font=("楷体", 30)).grid(row=4,column=0)
    # 焰芯
    tk.Label(frame1, text="焰芯颜色(RGBA):(",font=("楷体", 20)).grid(row=5,column=0)
   
    core_e1 = tk.Entry(frame1,width=3,font=("楷体", 20),bootstyle="PRIMARY", validate="focus", validatecommand=(validate1, '%P'))
    core_e1.grid(row=5,column=1)
    core_e1.insert(0,0)

    tk.Label(frame1, text=")(",font=("楷体", 20)).grid(row=5,column=2)

    core_e2 = tk.Entry(frame1,width=3,font=("楷体", 20),bootstyle="PRIMARY", validate="focus", validatecommand=(validate1, '%P'))
    core_e2.grid(row=5,column=3)
    core_e2.insert(0,0)

    tk.Label(frame1, text=")(",font=("楷体", 20)).grid(row=5,column=4)

   
    core_e3 = tk.Entry(frame1,width=3,font=("楷体", 20),bootstyle="PRIMARY", validate="focus", validatecommand=(validate1, '%P'))
    core_e3.grid(row=5,column=5)
    core_e3.insert(0,0)

    tk.Label(frame1, text=")(",font=("楷体", 20)).grid(row=5,column=6)

    core_e4 = tk.Entry(frame1,width=3,font=("楷体", 20),bootstyle="PRIMARY", validate="focus", validatecommand=(validate1, '%P'))
    core_e4.grid(row=5,column=7)
    core_e4.insert(0,0)

    tk.Label(frame1, text=")",font=("楷体", 20)).grid(row=5,column=8)


    tk.Label(frame1, text="",font=("楷体", 30)).grid(row=6,column=0)
    # 粒子数量
    num = tk.IntVar()
    num.set(50)
   
    particel_num = tk.StringVar()
    particel_num.set(f"粒子数量:外焰{int(num.get())}、內焰{int(num.get()/2)}、焰芯{int(num.get()/4)}")
   
    tk.Label(frame1, textvariable=particel_num, font=("楷体", 20)).grid(row=7, column=0,columnspan=7)
    tk.Label(frame1, text="少",style="primary", font=("楷体", 15)).grid(row=8,column=0)
    tk.Label(frame1, text="多",style="primary", font=("楷体", 15)).grid(row=8,column=7)

    def change_particle_num(event):
      particel_num.set(f"粒子数量:外焰{int(num.get())}、內焰{int(num.get()/2)}、焰芯{int(num.get()/4)}")
   
    num_s = tk.Scale(frame1, from_=0, to=80, length=300, variable=num,command=change_particle_num)
    num_s.grid(row=8,column=0,columnspan=7,padx=5,pady=5,sticky="e")


    tk.Label(frame1, text="",font=("楷体", 30)).grid(row=9,column=0)
    # 粒子大小

    tk.Label(frame1, text="粒子大小:", font=("楷体", 20)).grid(row=10, column=0)
    tk.Label(frame1, text="最小:",style="primary", font=("楷体", 15)).grid(row=10,column=1)
    tk.Label(frame1, text="最大:",style="primary", font=("楷体", 15)).grid(row=10,column=4)

    size_e1 = tk.Entry(frame1, width=3,font=("楷体", 15))
    size_e1.grid(row=10,column=2,padx=5,pady=5,sticky="e")
    size_e1.insert(0,1)

    size_e2 = tk.Entry(frame1, width=3,font=("楷体", 15))
    size_e2.grid(row=10,column=5,padx=5,pady=5)
    size_e2.insert(0,10)
   

    # 背景
    tk.Label(frame1, text="背景\n更改\n   |\n﹀",style="primary").grid(row=0,column=9)
   
    tk.Button(frame1, text="白",style="light").grid(row=10,column=9,padx=10)

    tk.Button(frame1, text="黑",style="secondary").grid(row=1,column=9,padx=10)
   
    bg_s = tk.Scale(frame1, from_=0, to=255, orient=tk.VERTICAL, length=300,style="light")
    bg_s.grid(row=1,rowspan=10,column=9,padx=30,pady=10)


   

    # 确定

    def return_color():
      try:
            color["outside"] =
            color["inside"] =
            color["core"] =
            color["bg"] =

            num_list=int(num_s.get())
            num_list=int(num_s.get()/2)
            num_list=int(num_s.get()/4)

            particle_size=int(size_e1.get())
            particle_size=int(size_e2.get())
      except:
            pass
      
      root.withdraw()
      
    tk.Style().configure('my.TButton', font=('楷体', 35))
    b = tk.Button(root, text='确定', style='my.TButton',command=return_color)
    b.pack(padx=10,pady = 20,fill="both")

    # 阻断关闭窗口
    def on_closing():
      root.withdraw()
   
    root.protocol("WM_DELETE_WINDOW", on_closing)
    root.mainloop()




if __name__ == "__main__":

    pygame.init()

    screen_list = pygame.display.list_modes() # 获取屏幕尺寸
   
    size = width, height = screen_list,screen_list

    screen = pygame.display.set_mode(size)

    pygame.display.set_caption("title")

    clock = pygame.time.Clock()

    delay = 60 # 延时计时器
    time = 0

    # 是否全屏
    fullscreen = False
    screen_change = False

    running = True


    # 粒子效果列表
    particles = []

    # 颜色,数量,大小
    color = {"outside":,
             "inside":,
             "core":,
             "bg":}
    num_list =

    particle_size =

    if_root=0

    while running:
      clock.tick(60)

      # 得到鼠标位置
      pos = pygame.mouse.get_pos()

      # 检测是否全屏
      if fullscreen and screen_change:
            screen = pygame.display.set_mode(size,FULLSCREEN,HWSURFACE)
            screen_change = False
      elif screen_change:
            screen = pygame.display.set_mode(size)
            screen_change = False

      for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
               
            if event.type == MOUSEBUTTONDOWN:
                if event.button == 1:
                  
                  particles.append(SpurtFire(pos,
                                             color=color["outside"],
                                             time=,
                                             speed=[[-3,+3],[-2,-1]],
                                             num = num_list,
                                             radius = particle_size,
                                             burn_time = 1200,
                                             timer=0))
                  particles.append(SpurtFire(pos,
                                             color=color["inside"],
                                             time=,
                                             speed=[[-2,+2],[-2,-1]],
                                             num = num_list,
                                             radius = particle_size,
                                             burn_time = 1200,
                                             timer=0))
                  particles.append(SpurtFire(pos,
                                             color=color["core"],
                                             time=,
                                             speed=[[-1,+1],[-2,-1]],
                                             num = num_list,
                                             radius = particle_size,
                                             burn_time = 1200,
                                             timer=0))

                  
                  


            if event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                  pygame.quit()
                  sys.exit()
                  
                #F11切换全屏
                elif event.key == K_F11:
                  fullscreen = not fullscreen
                  screen_change = True

                elif event.key == K_1:
                  bg = (255,255,255)

                elif event.key == K_2:
                  bg = (0,0,0)

                elif event.key == K_o:
                  if not if_root:
                        thread01=Thread(target=set_up, name="线程1")
                        thread01.start()
                        if_root=1
                  else:
                        root.deiconify()
                  
                  pygame.event.clear()
                  

                elif event.key == K_c:
                  particles.clear()


      #画背景
      screen.fill(color["bg"])

      #画 xxxx
      for particle in particles:
            particle.draw(screen)

      #刷新
      for particle in particles:
            particle.update()
            if not particle.burn_time:
                particles.remove(particle)

      # 刷新界面
      pygame.display.update()






这是使用手册{:10_279:}

exe压缩包是有的,但是太大了没法附件上来{:10_282:}



sfqxx 发表于 2023-7-10 16:31:20

付费主题没人看啊

cjjJasonchen 发表于 2023-7-10 16:32:40

sfqxx 发表于 2023-7-10 16:31
付费主题没人看啊

缺实妹人

Ewan-Ahiouy 发表于 2023-7-10 16:46:00

泰裤辣!{:10_257:}

Ewan-Ahiouy 发表于 2023-7-10 16:48:05

有一种在模糊背景下看的感觉{:10_256:}评分了{:5_106:}

cjjJasonchen 发表于 2023-7-10 16:48:20

Ewan-Ahiouy 发表于 2023-7-10 16:46
泰裤辣!

{:10_257:}

cjjJasonchen 发表于 2023-7-10 16:49:09

Ewan-Ahiouy 发表于 2023-7-10 16:48
有一种在模糊背景下看的感觉评分了

谢谢!

Ewan-Ahiouy 发表于 2023-7-10 16:51:04

唉,才4个火焰我的电脑就扛不住了{:10_266:}

cjjJasonchen 发表于 2023-7-10 16:53:43

Ewan-Ahiouy 发表于 2023-7-10 16:51
唉,才4个火焰我的电脑就扛不住了

大多数情况下两个就掉帧了,因为会同时生成几千个对象(粒子),所以会占用很多内存,如果减少对象数量的话,画质就要做出妥协了,没法这么好看,所有发上来希望大火可以帮忙想法子改进{:10_298:}

Ewan-Ahiouy 发表于 2023-7-10 16:57:07

cjjJasonchen 发表于 2023-7-10 16:53
大多数情况下两个就掉帧了,因为会同时生成几千个对象(粒子),所以会占用很多内存,如果减少对象数量的 ...

加油{:5_108:}

额外减小 发表于 2023-7-10 17:21:17

666

liuhongrun2022 发表于 2023-7-10 18:09:09

支持,好好看

歌者文明清理员 发表于 2023-7-10 18:10:37

pygame同行,考虑加个好友?

Ewan-Ahiouy 发表于 2023-7-10 18:59:51

歌者文明清理员 发表于 2023-7-10 18:10
pygame同行,考虑加个好友?

能给我评点分吗,还差17积分{:10_254:}

歌者文明清理员 发表于 2023-7-10 19:02:02

Ewan-Ahiouy 发表于 2023-7-10 18:59
能给我评点分吗,还差17积分

好了

sfqxx 发表于 2023-7-10 20:33:31

设置回帖奖励可以增加人气{:10_256:}

cjjJasonchen 发表于 2023-7-10 20:43:30

sfqxx 发表于 2023-7-10 20:33
设置回帖奖励可以增加人气

我的下一个帖子会考虑地{:10_325:}

python爱好者. 发表于 2023-7-10 20:53:44

高手,厉害!

cjjJasonchen 发表于 2023-7-10 21:03:28

python爱好者. 发表于 2023-7-10 20:53
高手,厉害!

这个帖子以后可能会出教程,要持续关注哦!

sfqxx 发表于 2023-7-10 21:09:02

cjjJasonchen 发表于 2023-7-10 20:43
我的下一个帖子会考虑地

{:10_323:}
页: [1] 2 3 4 5
查看完整版本: 【pygame】 用粒子特效生成火焰 -- 7/25最新更新