Pergunta

So I have this mini particle effect that produces circles that move up. I want to make it look like smoke. I'm having a lot of problems though.

  1. First off I want to make it recursive so that when the particle reaches the top it returns to its original spot and begins again, what I have works a little but not exactly.
  2. The other thing is I don't feel like it really looks like smoke can anyone suggest changes to make it look better?
  3. Also I want to add this into my game, how would I make this callable for my game so I could just call particle and give it a location and it appears there. Can anyone help with any of this?

My Code

import pygame,random
from pygame.locals import *

xmax = 1000    #width of window
ymax = 600     #height of window

class Particle():
    def __init__(self, x, y, dx, dy, col):
        self.x = x
        self.y = y
        self.col = col
        self.ry = y
        self.rx = x
        self.dx = dx
        self.dy = dy

    def move(self):
        if self.y >= 10:
            if self.dy < 0:
                self.dy = -self.dy

        self.ry -= self.dy
        self.y = int(self.ry + 0.5)

        self.dy -= .1
        if self.y < 1:
            self.y += 500

def main():
    pygame.init()
    screen = pygame.display.set_mode((xmax,ymax))
    white = (255, 255, 255)
    black = (0,0,0)
    grey = (128,128,128)

    particles = []
    for part in range(25):
        if part % 2 > 0: col = black
        else: col = grey
        particles.append( Particle(random.randint(500, 530), random.randint(0, 500), 0, 0, col))

    exitflag = False
    while not exitflag:
        for event in pygame.event.get():
            if event.type == QUIT:
                exitflag = True
            elif event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    exitflag = True

        screen.fill(white)
        for p in particles:
            p.move()
            pygame.draw.circle(screen, p.col, (p.x, p.y), 8)

        pygame.display.flip()
    pygame.quit()

if __name__ == "__main__":
    main()
Foi útil?

Solução

I have made some major edits to your code. For starters, I cleaned up your class a great deal. Let's start with the arguments and the __init__ function.

First of all, instead of going down 500 to reset, particles go to the location that was set as their starting point. It's location that it starts out in is now chosen randomly in the __init__ function rather than in the game. I got rid of some of your unnecessary arguments as well.

In the move function of your class, I simplified quite a bit. In order for the particle to detect if it should reset, it simply sees if it's above 0. The going up is just a simple decrease of the y by 1. A change I added in is that the x changes randomly and goes to the right and left. This will make the smoke look a lot better / more realistic.

I didn't make many changes to the rest of your code. I changed your calling of the Particle class to fit the new arguments. I made a ton more particles, once again for visual effect. I also massively decreased the size of the circles drawn for (can you guess it?) visual effect. I added in a clock as well to keep the particles from going at supersonic speed.

Here is the final code. I hope you like it.

import pygame,random
from pygame.locals import *

xmax = 1000    #width of window
ymax = 600     #height of window

class Particle():
    def __init__(self, startx, starty, col):
        self.x = startx
        self.y = random.randint(0, starty)
        self.col = col
        self.sx = startx
        self.sy = starty

    def move(self):
        if self.y < 0:
            self.x=self.sx
            self.y=self.sy

        else:
            self.y-=1

        self.x+=random.randint(-2, 2)

def main():
    pygame.init()
    screen = pygame.display.set_mode((xmax,ymax))
    white = (255, 255, 255)
    black = (0,0,0)
    grey = (128,128,128)

    clock=pygame.time.Clock()

    particles = []
    for part in range(300):
        if part % 2 > 0: col = black
        else: col = grey
        particles.append( Particle(515, 500, col) )

    exitflag = False
    while not exitflag:
        for event in pygame.event.get():
            if event.type == QUIT:
                exitflag = True
            elif event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    exitflag = True

        screen.fill(white)
        for p in particles:
            p.move()
            pygame.draw.circle(screen, p.col, (p.x, p.y), 2)

        pygame.display.flip()
        clock.tick(50)
    pygame.quit()

if __name__ == "__main__":
    main()

update

In order to add particles into your code, just do what you did in the code above. It works fine. If you wanted to do something to show the smoke starting, just put a pause time into your arguments and inhibit the movement of the smoke until that amount of time has passed. New class with that added in:

class Particle():
    def __init__(self, startx, starty, col, pause):
        self.x = startx
        self.y = starty
        self.col = col
        self.sx = startx
        self.sy = starty
        self.pause = pause

    def move(self):
        if self.pause==0:
            if self.y < 0:
                self.x=self.sx
                self.y=self.sy

            else:
                self.y-=1

            self.x+=random.randint(-2, 2)

        else:
            self.pause-=1

The code you will need to create new particles:

for part in range(1, A):
    if part % 2 > 0: col = black
    else: col = grey
    particles.append( Particle(515, B, col, round(B*part/A)) )

A and B are variables (I reccomend around 300 for A, B will be the Y value)

The new code will make particles spawn at the start location, and rise continously with no breaks. Hope you enjoy.

Outras dicas

I have made a lot of changes to your code, especially in the Particle class.
Although, there are rather puzzling things in this code, it will be more flexible than your current code.

Here,
I have quite literately rewritten you Particle class.
Other than changing the __init__, to take many arguments (7 to be exact),
I have used trigonometry and the math module to move the particle, making it easier to manage (if you are good at math!). And I have also added bounce and draw methods to the Particle, and made the code more readable.
Just like @PygameNerd, I have added a clock, for restricting the max fps. I haven't changed any event handling, but have used the bounce and draw funcs in the for p in particles: loop.

import pygame, random, math

def radians(degrees):
    return degrees*math.pi/180

class Particle:
    def __init__(self, (x, y), radius, speed, angle, colour, surface):
        self.x = x
        self.y = y
        self.speed = speed
        self.angle = angle
        self.radius = 3
        self.surface = surface
        self.colour = colour
        self.rect = pygame.draw.circle(surface,(255,255,0),
                           (int(round(x,0)),
                            int(round(y,0))),
                           self.radius)
    def move(self):
        """ Update speed and position based on speed, angle """
        # for constant change in position values.
        self.x += math.sin(self.angle) * self.speed
        self.y -= math.cos(self.angle) * self.speed
        # pygame.rect likes int arguments for x and y
        self.rect.x = int(round(self.x))
        self.rect.y = int(round(self.y))

    def draw(self):
        """ Draw the particle on screen"""
        pygame.draw.circle(self.surface,self.colour,self.rect.center,self.radius)

    def bounce(self):
        """ Tests whether a particle has hit the boundary of the environment """

        if self.x > self.surface.get_width() - self.radius: # right
            self.x = 2*(self.surface.get_width() - self.radius) - self.x
            self.angle = - self.angle

        elif self.x < self.radius: # left
            self.x = 2*self.radius - self.x
            self.angle = - self.angle            

        if self.y > self.surface.get_height() - self.radius: # bottom
            self.y = 2*(self.surface.get_height() - self.radius) - self.y
            self.angle = math.pi - self.angle

        elif self.y < self.radius: # top
            self.y = 2*self.radius - self.y
            self.angle = math.pi - self.angle

def main():
    xmax = 640    #width of window
    ymax = 480     #height of window
    white = (255, 255, 255)
    black = (0,0,0)
    grey = (128,128,128)

    pygame.init()
    screen = pygame.display.set_mode((xmax,ymax))
    clock = pygame.time.Clock()

    particles = []

    for i in range(1000):
        if i % 2:
            colour = black
        else:
            colour = grey
        # for readability
        x = random.randint(0, xmax)
        y = random.randint(0, ymax)
        speed = random.randint(0,20)*0.1
        angle = random.randint(0,360)
        radius = 3
        particles.append( Particle((x, y), radius, speed, angle, colour, screen) )

    done = False
    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
                break
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    done = True
                    break
        if done:
            break

        screen.fill(white)
        for p in particles:
            p.move()
            p.bounce()
            p.draw()

        clock.tick(40)

        pygame.display.flip()
    pygame.quit()

if __name__ == "__main__":
    main()
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top