Skip to content

Instantly share code, notes, and snippets.

@f0ursqu4r3
Created November 27, 2021 06:04
Show Gist options
  • Save f0ursqu4r3/9aaf810377c7b94948a5230735672253 to your computer and use it in GitHub Desktop.
Save f0ursqu4r3/9aaf810377c7b94948a5230735672253 to your computer and use it in GitHub Desktop.
Not very efficient particle emitter
import random
import pygame
from pygame import Vector2
class ParticleEmitter:
__slots__ = [
'pos', 'vel', 'speed',
'particles', 'age', 'spawn_rate',
'last_spawn', 'shape', 'particle_class',
'particle_kwargs', 'active',
'deactivate_after_burst', 'debug'
]
class Shape:
pass
class Point(Shape):
def __init__(self, spread=0):
self.spread = spread
class Line(Shape):
def __init__(self, vec):
self.vec = Vector2(vec)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
class Rectangle(Shape):
def __init__(self, size):
self.size = Vector2(size)
def __init__(self, pos, vel=None, speed=None,
spawn_rate=None, shape=None,
particle_class=None, particle_kwargs=None,
debug=False):
self.pos = Vector2(pos)
self.vel = Vector2(vel).normalize() if vel else None
self.speed = speed
self.spawn_rate = spawn_rate if spawn_rate is not None else 10
self.last_spawn = 0
self.shape = shape if shape is not None and isinstance(
shape, self.Shape) else self.Point()
self.particle_class = particle_class or Particle
self.particle_kwargs = particle_kwargs or {}
self.active = True
self.deactivate_after_burst = False
self.debug = debug
self.particles = []
self.age = 0
def update(self, dt):
self.last_spawn += dt
spawn_rate = (1/self.spawn_rate) if self.spawn_rate > 0 else 0
if spawn_rate and self.last_spawn >= spawn_rate:
for _ in range(max(1, int(dt/spawn_rate))):
self.create_particle()
self.last_spawn = 0
for particle in self.particles[:]:
particle.update(dt)
if not particle.alive:
self.particles.remove(particle)
if self.deactivate_after_burst and not self.particles:
self.active = False
self.age += dt
def create_particle(self):
vel = self.vel
if not vel:
vel = Vector2(
random.random() - .5,
random.random() - .5
).normalize()
speed = self.speed or random.randint(5, 10)
pos = self.pos
if isinstance(self.shape, self.Point):
# alter the velocity angle +/- point spread
vel = vel.rotate(
random.uniform(-self.shape.spread, self.shape.spread))
if isinstance(self.shape, self.Line):
length = self.shape.vec.length()
particle_pos = self.shape.vec.normalize() * (random.random() * length)
half = self.shape.vec / 2
pos = pos - half + particle_pos
elif isinstance(self.shape, self.Circle):
pos = pos + Vector2(
random.random() - .5,
random.random() - .5
).normalize() * random.random() * self.shape.radius
elif isinstance(self.shape, self.Rectangle):
center = self.shape.size/2
point = Vector2(
random.random() * self.shape.size.x,
random.random() * self.shape.size.y
)
pos = pos - center + point
self.particles.append(
self.particle_class(pos, vel * speed, **self.particle_kwargs)
)
def burst(self, count=None, deactivate_after=False):
if isinstance(count, list):
count = range(*count) if len(count) == 2 else count[0]
else:
count = range(random.randint(5, 10)
) if count is None else range(count)
for _ in count:
self.create_particle()
self.deactivate_after_burst = deactivate_after
def draw(self, surface):
if self.debug:
c = (0, 200, 200)
if isinstance(self.shape, self.Point):
surface.set_at([*map(int, self.pos)], c)
elif isinstance(self.shape, self.Line):
half = self.shape.vec/2
start = self.pos - half
end = self.pos + half
pygame.draw.line(surface, c, start, end)
elif isinstance(self.shape, self.Circle):
pygame.draw.circle(surface, c, self.pos, self.shape.radius, 1)
elif isinstance(self.shape, self.Rectangle):
center = self.shape.size/2
pygame.draw.rect(
surface, c, (self.pos - center, self.shape.size), 1)
for particle in self.particles:
particle.draw(surface)
class Particle:
__slots__ = [
'pos', 'vel', 'age',
'lifetime', 'color', 'speed'
]
def __init__(self, pos, vel):
self.pos = Vector2(pos)
self.vel = Vector2(vel)
self.age = 0
self.lifetime = 3
self.color = [0, 200, 0, 255]
self.speed = random.randint(5, 10)
@property
def alive(self):
return self.age < self.lifetime
def update(self, dt):
self.pos += self.vel * self.speed * dt
self.age += dt
def draw(self, surface):
surface.set_at([*map(int, self.pos)], self.color)
class FadeOutParticle(Particle):
def __init__(self, pos, vel, color):
super().__init__(pos, vel)
self.color = color
self.lifetime = .5
self.speed = random.randint(1, 3)
self.surf = pygame.Surface((1, 1))
self.surf.fill(self.color)
def update(self, dt):
super().update(dt)
self.surf.set_alpha(max(0, (1 - (self.age / self.lifetime)) * 255))
def draw(self, surface):
surface.blit(self.surf, self.pos)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment