Частица Pygame: удаление проблем из списка

У меня есть классы частиц и взрывов, которые я рисую, используя пигмею.
Взрыв представляет собой группу летящих частиц. Частица исчезает в конце концов; когда его свойство ttl становится равным 0, оно не должно быть видимым и должно быть удалено из Explosion.particles. Я хочу, чтобы программа удаляла мертвые частицы, чтобы они не обновлялись.

У меня проблема с Explosion.update() метод. Кажется, он не удаляет частицы. Я испытываю ошибку, когда якобы "мертвые" частицы все еще рисуются, но их движение не обновляется.
Я экспериментировал со списками в python и убедился, что предпосылка итерации "мертвого" списка для удаления из другого списка работает.
Будем весьма благодарны за любые предложения о том, где лежит ошибка в этом коде.

Изменить: я приложил и сократил оба исходных файла для лучшего контекста.

explosion.py

import sys
import pygame
from particleac import *

class App:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode(SCREEN_SIZE, pygame.HWSURFACE|pygame.DOUBLEBUF)                    
        self.clock = pygame.time.Clock()

        self.running = True
        self.explosions = []
        self.frame_no = 0

    def check_input(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False
            if event.type == pygame.MOUSEBUTTONDOWN or event.type == pygame.MOUSEMOTION:
                x, y = pygame.mouse.get_pos()
                self.explosions.append(Explosion(x, y))

    def update_screen(self):
        self.screen.fill(BLACK)
        dead_explosions = []
        # remove explosion if particles all gone
        for e in self.explosions:
            if len(e.particles) == 0:
                dead_explosions.append(e)
            else:
                for p in e.particles:
                    pygame.draw.circle(self.screen, p.colour, [p.x, p.y], p.size)
                    p.update()

        for e in dead_explosions:
            self.explosions.remove(e)

        pygame.display.flip()
        self.frame_no += 1
        self.frame_no %= 60
        self.clock.tick(FRAMERATE)

    def run(self):
        while self.running:
            self.check_input()
            self.update_screen()

        pygame.quit()       

app = App()
app.run()

particleac.py

import random
import sys

BLACK = [  0,   0,   0]
WHITE = [255, 255, 255]
BLUE = [0, 0, 255]
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 400
SCREEN_CENTRE = [SCREEN_WIDTH/2, SCREEN_HEIGHT/2]
FRAMERATE = 40
SCREEN_SIZE = [SCREEN_WIDTH, SCREEN_HEIGHT]

GRAVITY = 1
TERMINAL_VELOCITY = 10

class Particle:
    def __init__(self, x=SCREEN_CENTRE[0], y=SCREEN_CENTRE[1], colour=WHITE):
        self.x = x
        self.y = y
        self.colour = colour
        self.brightness = 255
        self.size = 2
        self.x_velocity = random.randrange(-4, 4)
        self.y_velocity = random.randrange(-8, 3)
        self.ttl = random.randrange(50, 200)
        self.decay = (self.ttl / FRAMERATE) * 2

    def update(self):            
        if self.ttl > 0:
            self.ttl -= 1

            self.brightness -= self.decay
            if self.brightness < 0:
                self.brightness = 0

            self.colour[0] = self.colour[1] = self.colour[2] = self.brightness 

            self.y_velocity += GRAVITY
            self.y += self.y_velocity
            self.x += self.x_velocity

            if self.y > SCREEN_HEIGHT:
                self.y -= SCREEN_HEIGHT

class Explosion:
    MAX_NUM_PARTICLES = 2

    def __init__(self, x=0, y=0, colour=WHITE):
        self.x = x
        self.y = y
        self.colour = colour
        self.x_velocity = random.randrange(-4, 4)
        self.y_velocity = random.randrange(-6, -1)
        self.num_particles = random.randrange(1, self.MAX_NUM_PARTICLES)
        self.particles = []

        for i in range(self.MAX_NUM_PARTICLES):
            p = Particle(self.x, self.y)
            p.colour = self.colour
            p.x_velocity += self.x_velocity
            p.y_velocity += self.y_velocity

            self.particles.append(p)

    def update(self):
        for p in self.particles:
            p.update()
        self.particles = [p for p in self.particles if p.ttl > 0]
        sys.stdout.write("len(self.particles) == {}".format(len(self.particles)))
        sys.stdout.flush()

3 ответа

Решение

Решение: я никогда не звонил e.update()!

Поэтому взрывы не обновлялись при их повторении, а это значит, что мертвые частицы не удалялись. Aaargh!

я добавил e.update() в app.update_screen() и удалил неправильный p.update() (который вызывается для каждой частицы в e.update()).

def update_screen(self):
    self.screen.fill(BLACK)
    dead_explosions = []
    for e in self.explosions:
        if len(e.particles) == 0:
            dead_explosions.append(e)
        else:
            e.update()
            for p in e.particles:
                pygame.draw.circle(self.screen, p.colour, [p.x, p.y], p.size)

    for e in dead_explosions:
        self.explosions.remove(e)

    pygame.display.flip()
    self.clock.tick(FRAMERATE)

Теперь я знаю, чтобы дать лучший контекст при публикации кода. Спасибо за ваши неприятности, ребята!

Проблема может быть связана с тем, что self.particles определяется на уровне класса. Это может вызвать некоторые проблемы в другом месте вашего кода, вы создаете более одного экземпляра Explosion учебный класс. Попробуйте переместить self.particle в конструкторе и посмотрим, что получится.

(В качестве предложения) Поскольку вы все равно строите список, почему бы не скопировать те, которые живы?

def update(self):
  particles_alive = []
  for p in self.particles:
    p.update()
    sys.out.write("p.ttl == {}\n".format(p.ttl))
    if p.ttl == 0:
      sys.out.write("Particle died.\n")
    else:
      particles_alive.append(p)
  self.particles = particles_alive

Ваш код работает для меня, один раз sys.out.write() изменено на sys.stdout.write(), Поскольку в вашем коде есть эта ошибка, вы уверены, что код в вашем сообщении совпадает с кодом, который не выполняется?

Вы можете упростить Explosion.update() к этому:

    def update(self):
        for p in self.particles:
            p.update()
        self.particles = [p for p in self.particles if p.ttl > 0]

Возможно, это изменение может решить проблему, потому что вы имеете дело только с одним списком, но я считаю, что ваш код должен работать.

Другие вопросы по тегам