Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Created by Anthony Cook
- # fb.com/groups/pygame
- import pygame
- from pygame import gfxdraw
- import random
- # quadTree constants
- CAPACITY = 4
- # This breaks down the display area into populated rectangles. The population is based on points
- class quadTree:
- def __init__(s, rect): # boundry is a rectangle
- s.rect = rect
- s.points = []
- s.divided = False
- def insert(s, point):
- global CAPACITY # maximum capacity on each quadrant
- # if the point inserted doesn't fit into this quadrant then don't add it to
- # the points list. Exit insert()!
- if not s.rect.collidepoint(int(point.x), int(point.y)): return False
- # check if the quadrant has room for more points
- if len(s.points) < CAPACITY:
- s.points.append(point) # if there's room add it to the points list
- # return True to inform parent quadtree the point has been added
- return True
- else:
- # if the quadrant is full then divide the quadrant into
- # four parts north west, north east, south west, south east...
- if not s.divided:
- s.subDivide()
- # ...and insert the point into one of the four new quadrants
- if s.nw.insert(point):
- return True
- elif s.ne.insert(point):
- return True
- elif s.sw.insert(point):
- return True
- elif s.se.insert(point):
- return True
- def subDivide(s):
- # create four new quadtrees which belong to the parent quadtree
- size = (s.rect.width / 2, s.rect.height / 2)
- s.nw = quadTree(pygame.Rect(s.rect.topleft, size))
- s.ne = quadTree(pygame.Rect(s.rect.midtop, size))
- s.sw = quadTree(pygame.Rect(s.rect.midleft, size))
- s.se = quadTree(pygame.Rect(s.rect.center, size))
- s.divided = True # the parent quadtree can no longer be divided
- def query(s, rect, pointsArray = []):
- # if the queried rect doesn't intersect with the quadrant
- # exit query
- if not s.rect.colliderect(rect): return
- # if it does then append points to the pointsArray
- for p in s.points:
- if rect.collidepoint(p): pointsArray.append(p)
- # because the quadtree is recursive we need to check its children
- # to see if they're within the query parameters (rect)
- if s.divided:
- s.nw.query(rect, pointsArray)
- s.ne.query(rect, pointsArray)
- s.sw.query(rect, pointsArray)
- s.se.query(rect, pointsArray)
- # define a marble class
- class marble:
- def __init__(s, x, y):
- # pos is a vector (x, y)
- s.pos = pygame.math.Vector2(x, y)
- # velocity is also a vector with a starting x velocity of random (-1, 1) * 10
- s.velocity = pygame.math.Vector2(random.uniform(-1, 1), 0) * 10
- def update(s):
- global GRAVITY, FPS, STAGE
- # apply velocity to the marble's position
- s.pos += s.velocity
- # apply gravity to the marble's velocity
- s.velocity += GRAVITY / FPS
- # if a marble goes outside of the stage then it reappears
- # on the opposite side. For instance if the marble drops out of the
- # bottom of the stage then it will reappear at the top
- if s.pos.y >= STAGE.bottom:
- s.pos.y = 0
- if s.pos.x > STAGE.right:
- s.pos.x = 0
- if s.pos.x < 0:
- s.pos.x = STAGE.right
- # pygame display settings
- DR = pygame.Rect((0, 0, 1280, 720)) # Display Rectangle
- HDW, HDH = DR.center # H = half
- FPS = 60
- # set up pygame
- pygame.init()
- PD = pygame.display.set_mode(DR.size) # primary display based of the size of Display Rect (800, 600)
- CLOCK = pygame.time.Clock()
- # set strength of gravity
- GRAVITY = pygame.math.Vector2(0, 9.8)
- # set up stage
- SCALE = 10
- STAGE = pygame.Rect((0, 0, DR.w * SCALE, DR.h * SCALE))
- # create a quadtree instance and initialise it with the display rect (size of the display)
- QT = quadTree(STAGE)
- # add 1000 randomly placed Plinko pins to quadtree
- PIN_COUNT = 1000
- for index in range(PIN_COUNT):
- QT.insert(pygame.math.Vector2(random.randint(0, STAGE.w), random.randint(0, STAGE.h)))
- # create 1000 marbles
- MARBLE_COUNT = 1000
- MARBLE_SPACING = STAGE.w / MARBLE_COUNT
- MARBLES = [marble(index * MARBLE_SPACING, 0) for index in range(MARBLE_COUNT)]
- # the viewport is like a window that looks onto the stage
- # this sets the location and size of the viewport which has a minimum size set to
- # that of the primary display surface and starts in the top left of the stage
- VIEWPORT = pygame.Rect(DR)
- # exit the demo?
- exit = False
- while True:
- # process events
- for e in pygame.event.get():
- if e.type == pygame.QUIT: # window close gadget
- exit = True
- elif e.type == pygame.MOUSEBUTTONDOWN:
- if e.button == 5: # mouse wheel down
- # increase the size of the viewport
- VIEWPORT.w += 18
- VIEWPORT.h += 10
- elif e.button == 4: # mouse wheel up
- # decrease the size of the viewport
- VIEWPORT.w -= 18
- VIEWPORT.h -= 10
- # limit the mimium size of the viewport
- # to that of the primary display resolution
- if VIEWPORT.w < DR.w:
- VIEWPORT.w = DR.w
- VIEWPORT.h = DR.h
- # exit the demo if ESC pressed or exit is True (set by pressing window x gadget)
- if pygame.key.get_pressed()[pygame.K_ESCAPE] or exit: break
- # get the distance the mouse has travelled since last get_rel() call
- rx, ry = pygame.mouse.get_rel()
- # if left mouse button has been pressed then you can drag the viewport about
- if pygame.mouse.get_pressed()[0]:
- # move the viewport
- VIEWPORT.x -= rx
- VIEWPORT.y -= ry
- # limit the viewport to stage's boundry
- if VIEWPORT.right > STAGE.w:
- VIEWPORT.x = STAGE.w - VIEWPORT.w
- if VIEWPORT.x < 0:
- VIEWPORT.x = 0
- if VIEWPORT.bottom > STAGE.h:
- VIEWPORT.y = STAGE.h - VIEWPORT.h
- if VIEWPORT.y < 0:
- VIEWPORT.y = 0
- # clear the primary display (fill it with black)
- PD.fill((0, 0, 0))
- # query the quadtree for the location of all the pins within the viewport
- pinPoints = []
- # after calling query() pinPoints contains a list of pygame.math.Vector2() points
- # representing the Plinko boards pins
- QT.query(VIEWPORT, pinPoints)
- # calculate the scale of the viewport against the size of the primary display
- scale = VIEWPORT.w / DR.w
- # draw all of the Plinko board's pins if they're within the viewport
- for pin in pinPoints:
- pygame.draw.circle(PD, (255, 0, 0), (int((pin.x - VIEWPORT.x) / scale), int((pin.y - VIEWPORT.y) / scale)), int(20 / scale))
- # draw all of the marbles and update marble position
- for m in MARBLES:
- pygame.draw.circle(PD, (0, 255, 0), (int((m.pos.x - VIEWPORT.x) / scale), int((m.pos.y - VIEWPORT.y) / scale)), int(6 / scale))
- # update position of the marble (apply velocity)
- m.update()
- closestPins = []
- # query the quadtree for all the Plinko pins nearest the marble
- # and store the result in closestPins
- QT.query(pygame.Rect(m.pos.x - 30, m.pos.y - 30, 60, 60), closestPins)
- # if there are pins then determine if the marble has collided with them
- for cp in closestPins:
- # is the distance between the marble and the pin less than
- # their combined radius's?
- if cp.distance_to(m.pos) <= 26:
- # if yes then a collision has occurred and we need to calculate a new
- # trajectory for the marble. This basically the opposite direction to which
- # it was going
- angle = pygame.math.Vector2().angle_to(cp - m.pos)
- m.velocity = m.pos.rotate(angle - 180).normalize() * 10
- # update the primary display
- pygame.display.update()
- CLOCK.tick(FPS) # limit frames
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement