I'm using pygame Vector2 math.
My math need more work. Added alot more sparks.
But I am still getting no lag. I using wind effect with mouse.
Do you get lag when you run my program ?
import pygame
import random
class State:
def __init__(self, engine):
self.engine = engine
def draw(self, surface):
pass
def event(self, event):
pass
def update(self, ticks, delta):
pass
class DisplayEngine:
def __init__(self, caption, width, height, flags=0):
pygame.display.set_caption(caption)
self.surface = pygame.display.set_mode((width, height), flags)
self.rect = self.surface.get_rect()
self.clock = pygame.time.Clock()
self.running = True
self.delta = 0
self.ticks = 0
self.fps = 60
self.state = State(self)
self.next_state = None
self.on_quit = self.quit
def quit(self):
self.running = False
def main_loop(self):
while self.running:
if self.next_state:
self.state = self.next_state
self.next_state = None
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.on_quit()
self.state.event(event)
self.ticks = pygame.time.get_ticks()
self.state.update(self.ticks, self.delta)
self.state.draw(self.surface)
pygame.display.flip()
self.delta = self.clock.tick(self.fps)
class Emitter:
def __init__(self, position, color, choices, size, decay):
self.vector = pygame.Vector2(random.choice(choices[0]), random.choice(choices[1]))
if self.vector.x != 0 and self.vector.y != 0:
self.vector.normalize_ip()
self.vector *= 0.03
self.position = pygame.Vector2(position)
self.rect = pygame.Rect(0, 0, size, size)
self.color = color
self.decay = decay
def draw(self, surface):
self.rect.center = int(self.position.x), int(self.position.y)
surface.fill(self.color, self.rect)
def update(self, delta, effects):
self.decay -= delta
self.position += self.vector * delta
for effect in effects:
self.position += effect.effect(self.position, delta)
class Sparks:
def __init__(self, entity, colors, tick, choices, sizes, decays):
self.entity = entity
self.choices = choices
self.colors = colors
self.decays = decays
self.sizes = sizes
self.timer = tick
self.tick = tick
self.sparks = []
@property
def count(self):
return len(self.sparks)
def create(self):
position = self.entity.position
color = self.get_data(self.colors)
decay = self.get_data(self.decays)
size = self.get_data(self.sizes)
self.sparks.append(Emitter(position, color, self.choices, size, decay))
def draw(self, surface):
for spark in self.sparks:
spark.draw(surface)
def get_data(self, data):
if isinstance(data, (tuple, list)):
return random.choice(data)
return data
def update(self, ticks, delta, effects):
alive_sparks = []
for spark in self.sparks:
spark.update(delta, effects)
if spark.decay > 0:
alive_sparks.append(spark)
self.sparks = alive_sparks
if ticks > self.timer:
self.timer += self.tick
self.create()
# Dummy entity
class Entity:
def __init__(self, position):
self.position = position
class MovementEffects:
def __init__(self, position, distance, strength):
self.position = pygame.Vector2(position)
self.distance = distance
self.strength = strength
def effect(self, position, delta):
#position = pygame.Vector2(position)
gap = (position - self.position).normalize()
distance = self.position.distance_to(position)
if distance < self.distance:
strength = self.strength / self.distance * (self.distance - distance)
return gap * strength * delta
return pygame.Vector2()
class LabelUpdater:
def __init__(self, font, text_format, color, callback, position, anchor, tick):
self.text_format = text_format
self.callback = callback
self.position = position
self.anchor = anchor
self.color = color
self.font = font
self.timer = tick
self.tick = tick
self.update_text(0)
def draw(self, surface):
surface.blit(self.image, self.rect)
def update(self, ticks):
if ticks > self.timer:
self.timer += self.tick
self.callback(self)
def update_text(self, data):
text = self.text_format.format(data)
self.image = self.font.render(text, 1, self.color)
self.rect = self.image.get_rect()
setattr(self.rect, self.anchor, self.position)
class IntroState(State):
def __init__(self, engine):
State.__init__(self, engine)
self.background_color = pygame.Color("navy")
choices = [x / 1000 for x in range(-30, 30)], [y / 1000 for y in range(-30, 0)]
color = pygame.Color('firebrick')
self.sparks = []
for x in range(20, engine.rect.width - 10, 30):
life = random.randint(1200, 1400)
emit = random.randint(80, 120)
size = 2
spark = Sparks(Entity((x, engine.rect.centery)), color, emit, choices, size, life)
self.sparks.append(spark)
font = pygame.font.Font(None, 28)
color = pygame.Color("lawngreen")
padding = 8
self.label_fps = LabelUpdater(font, "FPS: {}", color, self.update_fps, (padding, padding), "topleft", 100)
position = self.engine.rect.right - padding, padding
self.label_spark = LabelUpdater(font, "{} Sparks", color, self.update_sparks, position, "topright", 100)
self.wind_effect = MovementEffects((0, 0), 30, 10)
self.effects = [self.wind_effect]
def draw(self, surface):
surface.fill(self.background_color)
for spark in self.sparks:
spark.draw(surface)
self.label_fps.draw(surface)
self.label_spark.draw(surface)
def event(self, event):
if event.type == pygame.MOUSEMOTION:
self.wind_effect.position = pygame.Vector2(event.pos)
def update(self, ticks, delta):
for spark in self.sparks:
spark.update(ticks, delta, self.effects)
self.label_fps.update(ticks)
self.label_spark.update(ticks)
def update_sparks(self, label):
label.update_text(sum([spark.count for spark in self.sparks]))
def update_fps(self, label):
label.update_text(int(self.engine.clock.get_fps()))
def main():
pygame.init()
engine = DisplayEngine("Emmitter test", 800, 600)
engine.state = IntroState(engine)
engine.main_loop()
if __name__ == '__main__':
main()