Advertisement
Windspar

Pygame Manager State Machine With Fireworks Example

Jan 6th, 2023 (edited)
2,075
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.78 KB | Gaming | 0 0
  1. import pygame
  2. import random
  3.  
  4. class State:
  5.     def __init__(self, manager):
  6.         self.manager = manager
  7.  
  8.     def on_focus(self): pass
  9.     def on_leave(self): pass
  10.     def on_init(self, *args): pass
  11.  
  12.     def on_draw(self, surface): pass
  13.     def on_event(self, event): pass
  14.     def on_update(self, delta, ticks): pass
  15.  
  16.     def on_quit(self):
  17.         self.manager.quit()
  18.  
  19. class StateMachine:
  20.     def __init__(self, manager):
  21.         self.state = None
  22.         self.manager = manager
  23.         self.next_state = None
  24.  
  25.     def __call__(self):
  26.         self.update()
  27.         if self.state:
  28.             return self.state
  29.         else:
  30.             State(self.manager)
  31.  
  32.     def set(self, state):
  33.         if state:
  34.             if self.state is None:
  35.                 state.on_focus()
  36.                 self.state = state
  37.             else:
  38.                 self.next_state = state
  39.  
  40.     def update(self):
  41.         if self.next_state:
  42.             if self.state:
  43.                 self.state.on_leave()
  44.  
  45.             self.state = self.next_state
  46.             self.state.on_focus()
  47.             self.next_state = None
  48.  
  49. class DisplayEngine:
  50.     def __init__(self, manager, caption, width, height, fps, flags):
  51.         pygame.display.set_caption(caption)
  52.         self.surface = pygame.display.set_mode((width, height), flags)
  53.         self.rect = self.surface.get_rect()
  54.         self.clock = pygame.time.Clock()
  55.         self.running = False
  56.         self.delta = 0
  57.         self.fps = fps
  58.  
  59.         self.state_machine = StateMachine(manager)
  60.  
  61.     def loop(self, state=None):
  62.         self.running = True
  63.         self.state_machine.set(state)
  64.  
  65.         while self.running:
  66.             state = self.state_machine()
  67.  
  68.             for event in pygame.event.get():
  69.                 if event.type == pygame.QUIT:
  70.                     state.on_quit()
  71.                 else:
  72.                     state.on_event(event)
  73.  
  74.             ticks = pygame.time.get_ticks()
  75.             state.on_draw(self.surface)
  76.             state.on_update(self.delta, ticks)
  77.             pygame.display.flip()
  78.             self.delta = self.clock.tick(self.fps) * 0.001
  79.  
  80. class StateControl:
  81.     def __init__(self, state, *args):
  82.         self.active = isinstance(state, State)
  83.         self.state = state
  84.         self.args = args
  85.  
  86.     def get(self, manager, *args):
  87.         if not self.active:
  88.             return self.state(smanager, *self.args)
  89.  
  90.         return self.state
  91.  
  92.  # Store what to be share across all states
  93. class Manager:
  94.     def __init__(self, caption, width, height, fps=60, flags=0):
  95.         self.engine = DisplayEngine(self, caption, width, height, fps, flags)
  96.         self.controls = {}
  97.         self.images = None
  98.  
  99.     def add_state(self, key, state, *args):
  100.         self.controls[key] = StateControl(state, *args)
  101.  
  102.     def run(self, state=None):
  103.         self.engine.loop(state)
  104.  
  105.     def set_state(self, state, *args):
  106.         if isinstance(state, State):
  107.             state.on_init(*args)
  108.             self.engine.state_machine.next_state = state
  109.         elif isinstance(state, (int, str)):
  110.             self.engine.state_machine.next_state = self.controls[state].get(self, *args)
  111.         else:
  112.             print("Must be state or state key")
  113.  
  114.     def quit(self):
  115.         self.engine.running = False
  116.  
  117. # *** ****************** *** #
  118. # *** Example Code Below *** #
  119. # *** ****************** *** #
  120.  
  121. FIREWORK_ALPHA = 200
  122.  
  123. class TickTimer:
  124.     def __init__(self, interval, callback, user_data=None):
  125.         self.next_tick = pygame.time.get_ticks() + interval
  126.         self.user_data = user_data
  127.         self.callback = callback
  128.         self.interval = interval
  129.  
  130.     def tick(self, ticks):
  131.         count = 0
  132.         while ticks > self.next_tick:
  133.             self.next_tick += self.interval
  134.             count += 1
  135.  
  136.         if count > 0:
  137.             self.callback(self, count)
  138.  
  139. class ImageHandler:
  140.     def __init__(self):
  141.         self.firework = self.create_fireworks()
  142.  
  143.     def create_fireworks(self):
  144.         colors = 'red', 'yellow', 'blue', 'purple', 'pink'
  145.         firework = []
  146.         for color in colors:
  147.             size = random.randint(5, 9)
  148.             surface = pygame.Surface((size, size))
  149.             surface.fill(color)
  150.             surface.set_alpha(FIREWORK_ALPHA)
  151.             firework.append(surface)
  152.  
  153.         return firework
  154.  
  155. class FireWork(pygame.sprite.Sprite):
  156.     def __init__(self, image, position, vector, speed, *groups):
  157.         super().__init__(*groups)
  158.         self.oimage = image
  159.         self.image = image
  160.         self.rect = image.get_rect(center=position)
  161.         self.center = pygame.Vector2(self.rect.center)
  162.         self.start = pygame.Vector2(self.rect.center)
  163.         self.vector = vector
  164.         self.speed = speed
  165.         self.timer = TickTimer(500, self.timer_update)
  166.  
  167.     def shrink(self):
  168.         if self.rect.width >= 3:
  169.             size = self.rect.width - 1
  170.             alpha = self.image.get_alpha()
  171.             self.image = pygame.transform.scale(self.oimage, (size, size))
  172.             self.image.set_alpha(alpha)
  173.             self.rect = self.image.get_rect(center=self.center)
  174.  
  175.     def timer_update(self, timer, counter):
  176.         self.shrink()
  177.  
  178.     def update(self, delta, ticks):
  179.         alpha = FIREWORK_ALPHA - int(self.center.distance_to(self.start) * 1.5)
  180.         self.center += self.vector * delta * self.speed
  181.         self.rect.center = self.center
  182.         self.image.set_alpha(alpha)
  183.         self.timer.tick(ticks)
  184.         if alpha < 50:
  185.             self.kill()
  186.  
  187. class Example(State):
  188.     def __init__(self, manager):
  189.         super().__init__(manager)
  190.         self.fireworks = pygame.sprite.Group()
  191.  
  192.     def add_firework(self, event):
  193.         rnd = random.randint
  194.         n = rnd(4, 6)
  195.         a_begin = 180
  196.         a_interval = a_begin // n
  197.         a_end = a_begin + a_interval
  198.         for i in range(n):
  199.             angle = rnd(a_begin, a_end)
  200.             a_begin += a_interval
  201.             a_end += a_interval
  202.             vector = pygame.Vector2()
  203.             vector.from_polar((1, angle))
  204.             image = random.choice(self.manager.images.firework)
  205.             pos = vector * 2 + event.pos
  206.             speed = rnd(40, 60)
  207.             FireWork(image, pos, vector, speed, self.fireworks)
  208.  
  209.     def on_draw(self, surface):
  210.         surface.fill('black')
  211.         self.fireworks.draw(surface)
  212.  
  213.     def on_event(self, event):
  214.         if event.type == pygame.MOUSEBUTTONDOWN:
  215.             if event.button == 1:
  216.                 self.add_firework(event)
  217.  
  218.     def on_update(self, delta, ticks):
  219.         self.fireworks.update(delta, ticks)
  220.  
  221. if __name__ == '__main__':
  222.     pygame.init()
  223.     manager = Manager("Example", 800, 600)
  224.     manager.images = ImageHandler()
  225.     state = Example(manager)
  226.     manager.run(state)
  227.     pygame.quit()
  228.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement