Advertisement
Windspar

Pygame Example. How you can organized and refactor code.

Dec 1st, 2022
1,400
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.97 KB | Help | 0 0
  1. import pygame
  2. from pygame.sprite import Group, Sprite, spritecollide
  3. from itertools import count as icount
  4.  
  5. def colora(color, alpha):
  6.     c = pygame.Color(color)
  7.     c.a = alpha
  8.     return c
  9.  
  10. class Entity(Sprite):
  11.     def __init__(self, image, position, anchor):
  12.         super().__init__()
  13.         self.image = image
  14.         self.rect = image.get_rect(**{anchor: position})
  15.         self.center = pygame.Vector2(self.rect.center)
  16.  
  17.     def move(self, movement):
  18.         self.center += movement
  19.         self.rect.center = self.center
  20.  
  21. class Pen:
  22.     def __init__(self, font, color):
  23.         self.font = font
  24.         self.color = color
  25.  
  26.     def render(self, text):
  27.         return self.font.render(text, 1, self.color)
  28.  
  29. class VectorMovement:
  30.     def __init__(self, sprite, vector, distance):
  31.         self.sprite = sprite
  32.         self.vector = pgyame.Vector2(vector)
  33.         self.distance = distance
  34.         self.position = pygame.Vector2(sprite.rect.center)
  35.  
  36.     def update(self, delta):
  37.         if self.distance < self.position.distance_to(self.sprite.center):
  38.             self.sprite.move(self.vector * delta)
  39.  
  40. class State:
  41.     def __init__(self, manager):
  42.         self.manager = manager
  43.  
  44.     def on_draw(self, surface): pass
  45.     def on_event(self, event): pass
  46.     def on_update(self, delta, ticks): pass
  47.  
  48.     def on_quit(self):
  49.         self.manager.quit()
  50.  
  51. class StateMachine:
  52.     def __init__(self, manager):
  53.         self._state = State(manager)
  54.         self.next_state = None
  55.  
  56.     def fast_set(self, state):
  57.         if state:
  58.             self._state = state
  59.  
  60.     def update(self):
  61.         if self.next_state:
  62.             self._state = self.next_state
  63.             self.next_state = None
  64.  
  65.         return self._state
  66.  
  67. class DisplayEngine:
  68.     def __init__(self, caption, width, height, fps, flags):
  69.         pygame.display.set_caption(caption)
  70.         self.surface = pygame.display.set_mode((width, height), flags)
  71.         self.rect = self.surface.get_rect()
  72.         self.clock = pygame.time.Clock()
  73.         self.running = False
  74.         self.delta = 0
  75.         self.fps = fps
  76.  
  77.         self.state_machine = StateMachine(self)
  78.  
  79.     def loop(self, state=None):
  80.         self.running = True
  81.         self.state_machine.fast_set(state)
  82.  
  83.         while self.running:
  84.             state = self.state_machine.update()
  85.  
  86.             for event in pygame.event.get():
  87.                 if event.type == pygame.QUIT:
  88.                     state.on_quit()
  89.                 else:
  90.                     state.on_event(event)
  91.  
  92.             ticks = pygame.time.get_ticks()
  93.             state.on_draw(self.surface)
  94.             state.on_update(self.delta, ticks)
  95.             pygame.display.flip()
  96.             self.delta = self.clock.tick(self.fps) * 0.001
  97.  
  98. class Images:
  99.     def __init__(self, area, ypos):
  100.         self.player = self.create_player((40, 40), "snow", 'black', 2)
  101.         self.items = self.create_items(15, "white", "black", 150)
  102.         self.box = self.create_box(15, "yellow", 3)
  103.         self.background = self.create_background(area, ypos)
  104.  
  105.     def create_background(self, area, ypos):
  106.         surface = pygame.Surface(area.size)
  107.         surface.fill("skyblue")
  108.         surface.fill("lawngreen", (0, area.h - ypos, area.w, ypos))
  109.         return surface
  110.  
  111.     def create_player(self, size, color, outline, width):
  112.         surface = pygame.Surface(size)
  113.         surface.fill(color)
  114.         rect = surface.get_rect()
  115.         w = width * 2
  116.         #rect.inflate_ip(-w, -w)
  117.         pygame.draw.rect(surface, outline, rect, width)
  118.         return surface
  119.  
  120.     def create_items(self, radius, outline, center, alpha):
  121.         surface, rect = self.radius_surface(radius)
  122.  
  123.         outline = pygame.Color(outline)
  124.         outline.a = alpha
  125.         center = pygame.Color(center)
  126.         center.a = alpha
  127.  
  128.         pygame.draw.circle(surface, outline, rect.center, radius)
  129.         pygame.draw.circle(surface, center, rect.center, radius - 1)
  130.         outline.a = 100
  131.         pygame.draw.circle(surface, outline, rect.center, radius, 2)
  132.         center.a = 50
  133.         pygame.draw.circle(surface, center, rect.center, radius - 1, 2)
  134.  
  135.         d = int(radius * 1.5)
  136.         pen = Pen(pygame.font.Font(None, d), "lawngreen")
  137.         images = [surface.copy(), surface.copy(), surface.copy()]
  138.  
  139.         for i, image in enumerate(images):
  140.             entity = Entity(pen.render(str(i + 1)), rect.center, "center")
  141.             image.blit(entity.image, entity.rect)
  142.  
  143.         return images
  144.  
  145.     def create_box(self, radius, color, fade):
  146.         surface, rect = self.radius_surface(radius)
  147.         pygame.draw.circle(surface, color, rect.center, radius - fade)
  148.         color = pygame.Color(color)
  149.         fv = 255 / (fade * 2)
  150.         for i in range(fade):
  151.             color.a = int(fv * i + fv)
  152.             pygame.draw.circle(surface, color, rect.center, radius - i, 2)
  153.  
  154.         d = int(radius * 1.6)
  155.         pen = Pen(pygame.font.Font(None, d), "black")
  156.         entity = Entity(pen.render("?"), rect.center, "center")
  157.         surface.blit(entity.image, entity.rect)
  158.  
  159.         return surface
  160.  
  161.     def radius_surface(self, radius):
  162.         transparent = (0, 0, 0, 0)
  163.         d = radius * 2
  164.         surface = pygame.Surface((d, d), pygame.SRCALPHA)
  165.         surface.fill(transparent)
  166.         return surface, surface.get_rect()
  167.  
  168. class Manager:
  169.     def __init__(self, caption, width, height, fps=60, flags=0):
  170.         self.engine = DisplayEngine(caption, width, height, fps, flags)
  171.         self.states = []
  172.         self.images = Images(self.engine.rect, 100)
  173.  
  174.     def run(self, state=None):
  175.         self.engine.loop(state)
  176.  
  177.     def set_state(self, state):
  178.         self.engine.state_machine.next_state = state
  179.  
  180.     def quit(self):
  181.         self.engine.running = False
  182.  
  183. class Timer:
  184.     def __init__(self, ticks, interval, callback):
  185.         self.next_tick = ticks + interval
  186.         self.interval = interval
  187.         self.callback = callback
  188.         self.active = True
  189.  
  190.     def tick(self, ticks):
  191.         if self.active:
  192.             if ticks > self.next_tick:
  193.                 self.next_tick += self.interval
  194.                 self.callback(self)
  195.  
  196. class JumpKeyMovement:
  197.     def __init__(self, sprite, speed, area):
  198.         self.sprite = sprite
  199.         self.speed = speed
  200.         self.area = area
  201.         self.landing = 0
  202.         self.falling = False
  203.         self.jumping = False
  204.         self.jump_velocity = 1.0
  205.         self.accel = 1.05
  206.  
  207.     def update(self, delta):
  208.         keys = pygame.key.get_pressed()
  209.         direction = pygame.Vector2()
  210.         if any([keys[k] for k in [pygame.K_UP, pygame.K_w, pygame.K_SPACE]]):
  211.             if not self.falling and not self.jumping:
  212.                 self.jumping = True
  213.                 self.landing = self.sprite.rect.bottom
  214.         elif not self.falling and self.jumping:
  215.             self.falling = True
  216.             self.jumping = False
  217.  
  218.         if any([keys[k] for k in [pygame.K_LEFT, pygame.K_a]]):
  219.             direction.x -= 1
  220.  
  221.         if any([keys[k] for k in [pygame.K_RIGHT, pygame.K_d]]):
  222.             direction.x += 1
  223.  
  224.         if self.jumping:
  225.             direction.y -= self.jump_velocity
  226.             self.jump_velocity /= self.accel
  227.             if self.jump_velocity < 0.1:
  228.                 self.jumping = False
  229.                 self.falling = True
  230.  
  231.         if self.falling:
  232.             direction.y += self.jump_velocity
  233.             self.jump_velocity *= self.accel
  234.             if self.jump_velocity > 1:
  235.                 self.jump_velocity = 1
  236.  
  237.             if self.sprite.rect.bottom > self.landing:
  238.                 self.falling = False
  239.                 self.jump_velocity = 1.0
  240.                 self.sprite.rect.bottom = self.landing
  241.                 self.sprite.center.y = self.sprite.rect.centery
  242.                 direction.y = 0
  243.  
  244.         if direction != pygame.Vector2():
  245.             if self.jumping or self.falling:
  246.                 direction.x *= 0.5
  247.                 direction.y *= 1.5
  248.             direction = direction.normalize() * delta * self.speed
  249.             self.sprite.move(direction)
  250.             if not self.area.contains(self.sprite.rect):
  251.                 self.sprite.rect.clamp(self.area)
  252.  
  253. class Player(Entity):
  254.     def __init__(self, image, position, anchor, area):
  255.         super().__init__(image, position, anchor)
  256.         self.movement = JumpKeyMovement(self, 120, area)
  257.  
  258.     def update(self, delta):
  259.         self.movement.update(delta)
  260.  
  261. class Game(State):
  262.     def __init__(self, manager):
  263.         super().__init__(manager)
  264.         self.background = manager.images.background
  265.         rect = manager.engine.rect.copy()
  266.         rect.h -= 100
  267.         self.player = Player(manager.images.player, (50, rect.bottom), "bottomleft", rect)
  268.         self.sprites = Group(self.player)
  269.         self.boxes = Group()
  270.  
  271.         self.add_sprites()
  272.  
  273.     def add_sprites(self):
  274.         rect = self.manager.engine.rect
  275.         images = self.manager.images
  276.         box = Entity(images.box, (140, rect.bottom - 200), "topleft")
  277.         box.add(self.boxes, self.sprites)
  278.         box = Entity(images.box, (rect.right - 160, rect.bottom - 200), "topleft")
  279.         box.add(self.boxes, self.sprites)
  280.  
  281.     def test_sprites(self):
  282.         x = icount(40, 100)
  283.         images = self.manager.images
  284.         self.sprites.add(Entity(images.player, (next(x), 40), "topleft"))
  285.  
  286.         self.sprites.add(Entity(images.items[0], (next(x), 40), "topleft"))
  287.         self.sprites.add(Entity(images.items[1], (next(x), 40), "topleft"))
  288.         self.sprites.add(Entity(images.items[2], (next(x), 40), "topleft"))
  289.  
  290.         self.sprites.add(Entity(images.box, (next(x), 40), "topleft"))
  291.  
  292.     def box_collide(self, player, sprite):
  293.         if player.rect.colliderect(sprite.rect):
  294.             pvec = pygame.Vector2(player.rect.center)
  295.             svec = pygame.Vector2(sprite.rect.center)
  296.             if pvec.distance_to(svec) < 9:
  297.                 return True
  298.  
  299.         return False
  300.  
  301.     def draw_background(self, surface):
  302.         surface.blit(self.background, (0, 0))
  303.  
  304.     def on_draw(self, surface):
  305.         self.draw_background(surface)
  306.         self.sprites.draw(surface)
  307.  
  308.     def on_update(self, delta, ticks):
  309.         self.player.update(delta)
  310.         sprites = spritecollide(self.player, self.boxes, False, self.box_collide)
  311.         for sprite in sprites:
  312.             self.manager.set_state(BoxState(self.manager, self, sprite))
  313.  
  314. class Enlarge:
  315.     def __init__(self, speed, radius, max_radius):
  316.         self.enlarge = True
  317.         self.speed = speed
  318.         self._radius = radius
  319.         self.radius = int(radius)
  320.         self.max_radius = max_radius
  321.         self.last_size = 0
  322.  
  323.     def __call__(self, delta):
  324.         if self.enlarge:
  325.             return self.update(delta)
  326.         return False
  327.  
  328.     def update(self, delta):
  329.         if self.enlarge:
  330.             self._radius += self.speed * delta
  331.             if self._radius > self.max_radius:
  332.                 self.enlarge = False
  333.                 self._radius = self.max_radius
  334.  
  335.             self.radius = int(self._radius)
  336.             if self.last_size != self.radius:
  337.                 self.last_size = self.radius
  338.                 return True
  339.  
  340.             return False
  341.  
  342. class CircleSelect(Sprite):
  343.     def __init__(self, center, color, outline, width, speed, radius, max_radius):
  344.         super().__init__()
  345.         self.enlarge = Enlarge(speed, radius, max_radius)
  346.         self.color = color
  347.         self.outline = outline
  348.         self.width = width
  349.         self.center = center
  350.         self.update(0)
  351.  
  352.     def update(self, delta):
  353.         if self.enlarge(delta):
  354.             transparent = (0, 0 ,0 ,0)
  355.             rad = self.enlarge.radius
  356.             size = rad * 2, rad * 2
  357.  
  358.             self.image = pygame.Surface(size, pygame.SRCALPHA)
  359.             self.image.fill(transparent)
  360.             self.rect = self.image.get_rect()
  361.             pygame.draw.circle(self.image, self.outline, self.rect.center, rad, self.width + 1)
  362.             pygame.draw.circle(self.image, self.color, self.rect.center, rad - self.width)
  363.             self.rect.center = self.center
  364.  
  365. class Selection(Sprite):
  366.     def __init__(self, image, position, speed, radius, max_radius):
  367.         self.enlarge = Enlarge(speed, radius, max_radius)
  368.         self.oimage = image
  369.  
  370.     def update(self, delta):
  371.         if self.enlarge(delta):
  372.             pass
  373.  
  374. class BoxState(State):
  375.     def __init__(self, manager, state, sprite):
  376.         super().__init__(manager)
  377.         self.back_state = state
  378.         self.alpha = 10
  379.         self.transparent = pygame.Color(0, 0, 0, self.alpha)
  380.         self.fade = pygame.Surface(self.manager.engine.rect.size, pygame.SRCALPHA)
  381.         self.fade.fill(self.transparent)
  382.         self.fade_timer = Timer(pygame.time.get_ticks(), 100, self.fading)
  383.         self.sprite = sprite
  384.         self.selected = False
  385.         center = sprite.rect.center
  386.         self.circle = CircleSelect(center, colora('dodgerblue', 190), colora('snow', 190), 3, 100, 6, 80)
  387.         self.sprites = Group(self.circle)
  388.  
  389.     def fading(self, timer):
  390.         self.alpha += 6
  391.         self.transparent.a = self.alpha
  392.         self.fade.fill(self.transparent)
  393.         if self.alpha > 120:
  394.             timer.active = False
  395.  
  396.     def on_draw(self, surface):
  397.         self.back_state.draw_background(surface)
  398.         surface.blit(self.fade, (0, 0))
  399.         self.back_state.sprites.draw(surface)
  400.         self.sprites.draw(surface)
  401.  
  402.     def on_event(self, event):
  403.         if event.type == pygame.MOUSEBUTTONDOWN:
  404.             if event.button == 1:
  405.                 self.selected = True
  406.  
  407.     def on_update(self, delta, ticks):
  408.         self.circle.update(delta)
  409.         if self.fade_timer.active:
  410.             self.fade_timer.tick(ticks)
  411.         else:
  412.             if self.selected:
  413.                 self.sprite.kill()
  414.                 self.manager.set_state(self.back_state)
  415.  
  416. def main():
  417.     pygame.init()
  418.     manager = Manager("Example", 800, 600)
  419.     state = Game(manager)
  420.     manager.run(state)
  421.     pygame.quit()
  422.  
  423. main()
  424.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement