#python #pygame
#питон #pygame
Вопрос:
Я пытаюсь произвести взрыв с частицами в pygame, но это очень, очень медленно. Одна из самых медленных частей кода-это часть, которая создает поверхности с требуемым цветом и прозрачностью. Я публикую здесь весь код для ясности, но функция вызывается makeSurface
. Это и в SmokeParticle
том, и Spark
в другом классе.
import pygame import math import random ##draw functions## def drawSpark(window, spark): if not spark.dead(): window.blit(spark.surface, spark.position) def drawParticle(window, particle): window.blit(particle.surface, particle.position) def drawCluster(window, cluster): for particle in cluster.particles: drawParticle(window, particle) def drawSmoke(window, smoke): for cluster in smoke.clusters: drawCluster(window, cluster) def drawExplosions(window, em): for s in em.smoke: drawSmoke(window, s) for spark in em.sparks: drawSpark(window, spark) ##other functions## #thank you rabbid76 def randomDistBiasedMiddle(_min, _max): r = lambda : random.uniform(-1, 1) r1, r2 = r(), r() bias = lambda _r: _r**3 * (_max - _min) / 2 (_min _max) / 2 return (bias(r1), bias(r2)) ########################################## class Spark: def __init__(self, travel, position, rotation, scale): self.position = list(position) self.rotation = rotation self.goTowards = (math.sin(rotation), math.cos(rotation)) self.scale = scale self.speed = random.randint(70, 130) * scale self.maxTravel = travel * self.scale * 2 self.maxAlpha = 150 self.minAlpha = 0 self.randomColor = random.choice(( (188, 98, 5), (255, 215, 0), (255, 127, 80), (255, 140, 0))) self.travelled = 0 self.makeSurface() def shoot(self): if self.travelled lt; self.maxTravel: self.travelled = 5 #could use actual distance but performance :( . 5 seems to be a good fit tho self.position[0] = self.goTowards[0] * (3/self.travelled) * self.speed self.position[1] = self.goTowards[1] * (3/self.travelled) * self.speed def makeSurface(self): self.alpha = ((1 - (self.travelled / self.maxTravel)) * (self.maxAlpha - self.minAlpha)) self.minAlpha self.color = (*self.randomColor, self.alpha) if self.alpha gt; 0: self.surface = pygame.Surface((3, 15), pygame.SRCALPHA) self.surface.fill(self.color) self.surface = pygame.transform.rotate(self.surface, math.degrees(self.rotation)) def dead(self): return self.alpha lt; 1 class SmokeParticle: def __init__(self, position, distFromcentre, explosionRange, centre): self.position = list(position) self.distFromCentre = distFromcentre #distance from centre of explosion, not the cluster self.possibleMoves = ( (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0) ) self.blowSpeed = 2 self.blowDir = (self.position[0] - centre[0], self.position[1] - centre[1]) self.blowDir = (self.blowDir[0] / distFromcentre, self.blowDir[1] / distFromcentre) self.blowDistance = math.hypot(*self.blowDir) self.explosionRange = explosionRange self.maxAlpha = 200 self.minAlpha = 80 self.cf = (255, 140, 0)#smoke color farthest from the explosion centre self.cn = (20, 20, 20)#smoke color closest to the explosion centre self.makeSurface() def makeSurface(self): self.alpha = ((1 - (self.distFromCentre / self.explosionRange)) * (self.maxAlpha - self.minAlpha)) self.minAlpha findColor = lambda c : max(0, min(((1 - (self.distFromCentre / self.explosionRange)) * (self.cf[c] - self.cn[c])) self.cn[c], 255)) self.color = (findColor(0), findColor(1), findColor(2), self.alpha) self.surface = pygame.Surface((8, 8), pygame.SRCALPHA) if self.alpha gt; 0: self.surface.fill(self.color) def blowAway(self, dt): move = random.choice(self.possibleMoves) self.position[0] = move[0] self.position[1] = move[1] self.position[0] = self.blowDir[0] * self.blowSpeed self.position[1] = self.blowDir[1] * self.blowSpeed self.distFromCentre = self.blowDistance * self.blowSpeed def dead(self): return self.distFromCentre gt; self.explosionRange class SmokeCluster: def __init__(self, position, explosionRange, distFromCentre, centre): self.particles = [] self.explosionRange = explosionRange self.radius = 5 self.nParticels = 10 self.position = position self.distFromCentre = distFromCentre for i in range(self.nParticels): randomPos = randomDistBiasedMiddle(-self.radius, self.radius) pos = (self.position[0] randomPos[0], self.position[1] randomPos[1]) self.particles.append(SmokeParticle(pos, self.distFromCentre, self.explosionRange, centre)) def do(self, dt): for particle in self.particles: particle.blowAway(dt) particle.makeSurface() def removeDeadParticles(self): self.particles = [particle for particle in self.particles if not particle.dead()] def dead(self): return not self.particles class SmokeMass: def __init__(self, explosionRange, mousePos): self.clusters = [] self.explosionRange = explosionRange self.nClusters = int(200 * (explosionRange * 0.1)) for i in range(self.nClusters): randomPos = randomDistBiasedMiddle(-self.explosionRange, self.explosionRange) clusterPos = (randomPos[0] mousePos[0], randomPos[1] mousePos[1]) self.clusters.append(SmokeCluster(clusterPos, self.explosionRange, math.dist(clusterPos, mousePos), mousePos)) def do(self, dt): for cluster in self.clusters: cluster.do(dt) def removeDeadClusters(self): for cluster in self.clusters: cluster.removeDeadParticles() self.clusters = [cluster for cluster in self.clusters if not cluster.dead()] class ExplosionManager: def __init__(self, scale): self.sparks = [] self.smoke = [] self.scale = scale self.cloudPatches = [] self.maxSpread = 100 self.removeSparkles = pygame.USEREVENT 0 pygame.time.set_timer(self.removeSparkles, 2500) def generateSparks(self, mousePos): for i in range(int(self.scale * 20)): rotation = random.uniform(0, 2 * math.pi) self.sparks.append(Spark(self.maxSpread, mousePos, rotation, self.scale)) def generateSmoke(self, mousePos): self.smoke.append(SmokeMass(200 * self.scale, mousePos)) def triggered(self, events): for event in events: if event.type == pygame.MOUSEBUTTONDOWN: return True def explode(self, dt): for spark in self.sparks: spark.shoot() spark.makeSurface() for s in self.smoke: s.do(dt) def removeDeadSparks(self, events): for event in events: if event.type == self.removeSparkles: self.sparks = [spark for spark in self.sparks if not spark.dead()] def removeDeadSmoke(self): for s in self.smoke: s.removeDeadClusters() pygame.init() winSize = (600, 600) window = pygame.display.set_mode(winSize) clock = pygame.time.Clock() fps = 100 explosionScale = 1 explosionScale = min(max(explosionScale, 0), 1)#because i never know what I will do em = ExplosionManager(explosionScale) while True: events = pygame.event.get() mousePos = pygame.mouse.get_pos() mousePressed = pygame.mouse.get_pressed() dt = clock.tick(fps) * 0.001 pygame.display.set_caption(f"FPS: {clock.get_fps()}") gen = False for event in events: if event.type == pygame.QUIT: pygame.quit() raise SystemExit if em.triggered(events): em.generateSparks(mousePos) em.generateSmoke(mousePos) em.explode(dt) em.removeDeadSparks(events) em.removeDeadSmoke() window.fill((40, 50, 50)) drawExplosions(window, em) pygame.display.flip()
Ответ №1:
Очевидное улучшение заключается не в том, чтобы создавать поверхность для каждой частицы каждый раз, когда она движется. Создайте поверхность только один раз и залейте ее новым цветом, когда цвет изменится
class SmokeParticle: def __init__(self, position, distFromcentre, explosionRange, centre): # [...] self.surface = pygame.Surface((8, 8), pygame.SRCALPHA) self.color = None self.makeSurface() def makeSurface(self): self.alpha = ((1 - (self.distFromCentre / self.explosionRange)) * (self.maxAlpha - self.minAlpha)) self.minAlpha findColor = lambda c : max(0, min(((1 - (self.distFromCentre / self.explosionRange)) * (self.cf[c] - self.cn[c])) self.cn[c], 255)) color = (findColor(0), findColor(1), findColor(2), self.alpha) if self.alpha gt; 0 and self.color != color: self.color = color self.surface.fill(self.color)
Этого все равно будет недостаточно для повышения производительности, но это будет лучше.
Комментарии:
1. о, я не знал, что функции pygame.draw могут принимать цвета с альфа-значениями. Спасибо. Я подожду некоторое время, чтобы посмотреть, получу ли я какие-либо другие ответы, и приму ваш ответ, если я этого не сделаю
2. @Cool_Cornflakes Моя вина.
pygame.draw.rect
невозможно нарисовать прозрачные прямоугольники. Я изменил ответ.