cookertron

Python Pygame Bouncing Balls with Particles

Oct 22nd, 2021
731
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import pygame
  2. from pygame import Rect
  3. from pygame import Vector2
  4. from random import randint, uniform
  5.  
  6. # particle
  7. class particle:
  8.     def __init__(s, xy, vel): # xy = tuple(x, y)
  9.         # position of the particle
  10.         s.xy = Vector2(xy)
  11.  
  12.         # particle velocity
  13.         s.vel = vel #Vector2(uniform(-1, 1), uniform(-4.5, -5)) # x vel is betweem -1 & 1 and y vel is between -4.5 and -5
  14.  
  15.         # particle radius
  16.         s.radius = randint(4, 8) # random integer between 5 & 15
  17.  
  18.     # draw particle and update position/size
  19.     def update(s):
  20.         global PDS, WHITE # PDS = primary display surface
  21.        
  22.         # draw particle
  23.         pygame.draw.circle(PDS, (128, 128, 128), s.xy, s.radius)
  24.  
  25.         # draw glow
  26.         s.glow()
  27.  
  28.         # add velocity to particle
  29.         s.xy += s.vel
  30.  
  31.         # add gravity to velocity. negative velocity will slowly become positive
  32.         s.vel.y += GRAVITY
  33.  
  34.         # decrease radius to give a fizzling out effect
  35.         s.radius -= 0.1
  36.  
  37.     def glow(s):
  38.         global PDS
  39.         # get rectangular size of the particle. radius * 2
  40.         r = Vector2(s.radius) * 2 # r = (radius * 2, radius * 2)
  41.        
  42.         # create a surface twice the size of the particle
  43.         gs = pygame.Surface(r * 2)
  44.        
  45.         # draw a circle with a radius twice the size of the particle in the center of the new surface
  46.         pygame.draw.circle(gs, (60, 20, 60), r, r.x)
  47.  
  48.         # blit the new larger particle on the primary display with the center of the smaller particle
  49.         # use BLEND_RGB_ADD to increase luminosity if particles overlap
  50.         PDS.blit(gs, s.xy - r, special_flags=pygame.BLEND_RGB_ADD)
  51.  
  52. class ball:
  53.     def __init__(s, xy, v):
  54.         s.xy = xy
  55.         s.v = v
  56.  
  57.     def update(s):
  58.         global OBSTICLES, BALL_RADIUS, BALL_RADIUS_V, BALL_RADIUS2_V
  59.  
  60.         r = Rect(s.xy - BALL_RADIUS_V, BALL_RADIUS2_V)
  61.         nr = Rect(s.xy + s.v - (10, 10), (20, 20))
  62.  
  63.         for o in OBSTICLES:
  64.             if not o.colliderect(nr): continue
  65.            
  66.             if nr.right >= o.left and r.right <= o.left:
  67.                 r.right = o.left
  68.                 s.v.x = -s.v.x
  69.                 burst(r.midright, 1, 0)
  70.             if nr.left <= o.right and r.left >= o.right:
  71.                 r.left = o.right
  72.                 s.v.x = -s.v.x
  73.                 burst(r.midleft, 1, 1)
  74.             if nr.bottom >= o.top and r.bottom <= o.top:
  75.                 r.bottom = o.top
  76.                 s.v.y = -s.v.y
  77.                 burst(r.midbottom, 0, 0)
  78.             if nr.top <= o.bottom and r.top >= o.bottom:
  79.                 r.top = o.bottom
  80.                 s.v.y = -s.v.y
  81.                 burst(r.midbottom, 0, 1)
  82.             s.xy = Vector2(r.center)
  83.         pygame.draw.circle(PDS, WHITE, s.xy, BALL_RADIUS)
  84.         s.xy += s.v
  85.  
  86. def burst(xy, verticies, ricochet_direction):
  87.     global PARTICLES
  88.  
  89.     for i in range(20):
  90.         if verticies == 0: # horizontal particle burst
  91.             v = Vector2(uniform(-4, -2) if i // 10 else uniform(2, 4), uniform(1, 2) if ricochet_direction else uniform(-2, -1))
  92.         else:
  93.             v = Vector2(uniform(1, 2) if ricochet_direction else uniform(-2, -1), uniform(-4, -2) if i // 10 else uniform(2, 4))
  94.         PARTICLES += [particle(xy, v)]
  95.  
  96.  
  97. # initialise pygame and create primary display surface PDS
  98. pygame.init()
  99. PDR = Rect(0, 0, 1280, 720)
  100. PDS = pygame.display.set_mode(PDR.size)
  101.  
  102. # define some colors
  103. BLACK = (0, 0, 0)
  104. WHITE = (255, 255, 255)
  105.  
  106. # strength of gravity
  107. GRAVITY = 0.1
  108.  
  109. # particle container
  110. PARTICLES = []
  111.  
  112. # obsticles
  113. OBSTICLES = [
  114.     Rect(0, 0, PDR.w, 20),
  115.     Rect(0, 20, 20, PDR.h),
  116.     Rect(20, PDR.h - 20, PDR.w - 20, 20),
  117.     Rect(PDR.w - 20, 20, 20, PDR.h - 40),
  118.     Rect(PDR.w / 3 - 10, 140, 20, PDR.h - 280),
  119.     Rect(-10 + PDR.w - PDR.w / 3, 140, 20, PDR.h - 280),
  120.     Rect(140, PDR.centery - 10, PDR.w / 3 - 270, 20),
  121.     Rect(PDR.w - 300, PDR.centery - 10, PDR.w / 3 - 270, 20)
  122. ]
  123.  
  124. BALL_RADIUS = 10
  125. BALL_RADIUS_V = Vector2(BALL_RADIUS)
  126. BALL_RADIUS2_V = BALL_RADIUS_V * 2
  127. BALLS = [ball(Vector2(PDR.center), Vector2(5).rotate(d)) for d in range(0, 360, 20)]
  128.  
  129. exitDemo = False
  130. pause = False # press p to pause
  131. while not exitDemo:
  132.  
  133.     # handle the events
  134.     events = pygame.event.get()
  135.     for event in events:
  136.         if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):    
  137.             exitDemo = True
  138.         if event.type == pygame.KEYDOWN and event.key == pygame.K_p:
  139.             pause = 1 - pause
  140.    
  141.     if pause: continue
  142.  
  143.     # clear the PDS to black
  144.     PDS.fill(BLACK)
  145.  
  146.     for o in OBSTICLES:
  147.         pygame.draw.rect(PDS, (60, 20, 60), o, 2)
  148.  
  149.     for b in BALLS:
  150.         b.update()
  151.  
  152.     # create a container to hold particles that need deleting
  153.     killParticles = []
  154.     for p in PARTICLES:
  155.         p.update() # draw and update postion/size of the particle
  156.        
  157.         # if the particle is outside of the PDS or the radius is less than zero ie no longer visible
  158.         # then add it the killparticles pile
  159.         if p.xy.y >= PDR.bottom or p.radius <= 0:
  160.             killParticles += [p]
  161.    
  162.     # delete particles from particle container
  163.     for p in killParticles:
  164.         PARTICLES.remove(p)
  165.  
  166.     # update the PDS
  167.     pygame.display.update()
  168.     pygame.time.Clock().tick(120) # limit FPS to 120
RAW Paste Data