Advertisement
Bicorn

Python Breakout (in progress)

Apr 6th, 2014
143
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.66 KB | None | 0 0
  1. """
  2. A Breakout implementation in pygame
  3. Version: 0.2.1.0
  4. Current known bugs:
  5.    Collision detection needs work
  6.    Ball can get caught in endless horizontal movement
  7. """
  8. import pygame, os, sys, random
  9. from pygame.locals import *
  10.  
  11. if not pygame.font: print('Warning: fonts not available.')
  12. if not pygame.mixer: print('Warning: sounds not available.')
  13.  
  14.  
  15. class Ball():
  16.     def __init__(self, game, x, y, xd, yd):
  17.         self.rad = 5
  18.         self.sprite = pygame.sprite.Sprite()
  19.         self.sprite.image = pygame.Surface([self.rad*2, self.rad*2])
  20.         pygame.draw.ellipse(self.sprite.image,(255,255,255),self.sprite.image.get_rect())
  21.         self.sprite.rect = self.sprite.image.get_rect()
  22.         self.sprite.rect.x = x - self.rad
  23.         self.sprite.rect.y = y - self.rad
  24.         self.vect = pygame.math.Vector2(xd,yd)
  25.         self.game = game
  26.         self.upright = pygame.math.Vector2(0,1)
  27.  
  28.     def move(self):
  29.         """move one tick's worth
  30.        Returns true on an out
  31.        """
  32.         scrn = self.game.screen.get_rect()
  33.         self.sprite.rect.move_ip(int(self.vect.x), int(self.vect.y))
  34.         self.sprite.rect.clamp_ip(scrn)
  35.         x, y = self.sprite.rect.center
  36.         # screen-edge bounce
  37.         if x+self.rad >= scrn.width:  # right edge
  38.             print("Right edge bounce")
  39.             self.bounce(pygame.math.Vector2(1,0))
  40.         elif x-self.rad <=0:   #left edge
  41.             print("Left edge bounce")
  42.             self.bounce(pygame.math.Vector2(-1,0))
  43.         if y+self.rad >= scrn.height:   #lower edge
  44.             print("Ball out")
  45.             return True
  46.         elif y-self.rad <= 0:  #upper edge
  47.             print("Top edge bounce")
  48.             self.bounce(pygame.math.Vector2(0,-1))
  49.         return False
  50.  
  51.     def bounce(self, normal):
  52.         self.vect.reflect_ip(normal)
  53.         print("New angle: "+str(round(self.vect.angle_to(self.upright),2)))
  54.  
  55.        
  56. class Bat():
  57.     def __init__(self, game, color, x, y, width, height):
  58.         self.sprite = pygame.sprite.Sprite()
  59.         self.sprite.image = pygame.Surface([width, height])
  60.         self.sprite.image.fill(color)
  61.         self.sprite.rect = self.sprite.image.get_rect()
  62.         self.sprite.rect.x = x
  63.         self.sprite.rect.y = y
  64.         self.xdelta = 0
  65.         self.game = game
  66.  
  67.     def move(self):
  68.         """move one tick's worth"""
  69.         self.sprite.rect.move_ip(self.xdelta, 0)
  70.         self.sprite.rect.clamp_ip(self.game.screen.get_rect())
  71.    
  72.     def set_motion(self, delta):
  73.         """change movement delta"""
  74.         self.xdelta = delta
  75.  
  76.    
  77. class Block():
  78.     def __init__(self, color, x, y, width, height):
  79.         self.sprite = pygame.sprite.Sprite()
  80.         self.sprite.image = pygame.Surface([width, height])
  81.         self.sprite.image.fill(color)
  82.         self.sprite.rect = self.sprite.image.get_rect()
  83.         self.sprite.rect.x = x
  84.         self.sprite.rect.y = y
  85.         self.killed = False
  86.    
  87.     def die(self):
  88.         self.killed = True
  89.        
  90. class GameMain():
  91.     def __init__(self, width=600, height=300):
  92.         pygame.init()
  93.         random.seed(None)
  94.         self.width = width
  95.         self.height = height
  96.         self.screen = pygame.display.set_mode((self.width,self.height))
  97.         self.bg = pygame.Surface(self.screen.get_size())
  98.         self.bg = self.bg.convert()
  99.         self.bg.fill((0,0,0))
  100.         pygame.display.set_caption("Breakout")
  101.         self.clock = pygame.time.Clock()
  102.         self.sprites = pygame.sprite.Group()
  103.         self.keystates = Keystates()
  104.         self.colors = [(127,0,0),(255,0,0),(0,127,0),(0,255,0),(0,0,127),(0,0,255),
  105.                         (127,127,0),(255,255,0),(127,0,127),(255,0,255),(0,127,127),(0,255,255),
  106.                         (127,127,127),(255,255,255),(127,255,0),(255,127,0),(127,0,255),
  107.                         (255,0,127),(0,127,255),(0,255,127),(127,127,255),(127,255,127),
  108.                         (255,127,127)]
  109.         self.blocks = []
  110.         self.top_rows = 3
  111.         self.rows = 5
  112.         self.batspeed = 3
  113.         self.balldelta = (1,2)
  114.         self.block_width = 20
  115.         self.block_height = 10
  116.         self.tilt_scale = 1.0     # adjusts amount of tilting based on hit location
  117.         self.bat = None
  118.         self.ball = None
  119.         self.font_gameover = pygame.font.Font(None, 64)
  120.         self.font_gameover2 = pygame.font.Font(None, 32)
  121.         self.font_score = pygame.font.Font(None, 16)
  122.         self.score = 0
  123.         self.gamestate = "Initial"      # possible states: Initial, Running, Game over
  124.  
  125.     def run(self):
  126.         self.start_game()
  127.         while True:
  128.             self.clock.tick(60)
  129.             for event in pygame.event.get():
  130.                 if event.type == pygame.QUIT:
  131.                     sys.exit()
  132.                 elif event.type == pygame.KEYDOWN:
  133.                     if self.gamestate == "Initial":
  134.                         self.gamestate = "Running"
  135.                         self.keystates.set_key(event.key)
  136.                     elif event.key == K_r and not self.gamestate == "Running":
  137.                         print("Restarting!")
  138.                         self.start_game()
  139.                         self.gamestate = "Running"
  140.                     elif event.key == K_p:
  141.                         if self.gamestate == "Running":
  142.                             self.gamestate = "Paused"
  143.                             print("Paused")
  144.                         elif self.gamestate == "Paused":
  145.                             self.gamestate = "Running"
  146.                             print("Unpaused")
  147.                         else:
  148.                             pass
  149.                     else:
  150.                         self.keystates.set_key(event.key)
  151.                 elif event.type == pygame.KEYUP:
  152.                     self.keystates.unset_key(event.key)
  153.             if self.gamestate == "Running":
  154.                 self.control_bat()
  155.                 self.bat.move()
  156.                 out = self.ball.move()
  157.                 ball_center = self.ball.sprite.rect.center
  158.                 bat_center = self.bat.sprite.rect.center
  159.                 #collision detection with bat
  160.                 if self.bat.sprite.rect.colliderect(self.ball.sprite.rect):
  161.                     print("Bat collision")
  162.                     #simplest case for now: reflection from upper edge
  163.                     self.ball.sprite.rect.bottom = self.bat.sprite.rect.top
  164.                     norm = pygame.math.Vector2(0,1)
  165.                     #adjust angle based on spot hit and speed
  166.                     xs = self.bat.xdelta    #bat motion
  167.                     xl = ball_center[0] - bat_center[0]   #position difference
  168.                     degs = xl*self.tilt_scale
  169.                     norm.rotate_ip(degs)
  170.                     print("Bat angle adjustment: "+str(degs)+" degrees")
  171.                     self.ball.bounce(norm)
  172.                 #block collisions
  173.                 for b in self.blocks:
  174.                     if self.ball.sprite.rect.colliderect(b.sprite.rect) and not b.killed:
  175.                         print("Block collision")
  176.                         b.die()
  177.                         self.sprites.remove(b.sprite)
  178.                         #bounce ball based on direction of collision
  179.                         rct = b.sprite.rect
  180.                         if ball_center[0] >= rct.left and ball_center[0] <= rct.right:  #bottom or top collision
  181.                             self.ball.bounce(pygame.math.Vector2(0,1))
  182.                         elif ball_center[1] >= rct.top and ball_center[1] <= rct.bottom:  #side collision
  183.                             self.ball.bounce(pygame.math.Vector2(1,0))
  184.                         #TODO: corner cases
  185.                         self.blocks.remove(b)
  186.                         self.score += 1
  187.                         break   #do not ricochet off multiple blocks in one frame
  188.                            
  189.                 if out:
  190.                     self.gamestate = "Game over"
  191.             self.update_screen()
  192.  
  193.     def control_bat(self):
  194.         if self.keystates.left and not self.keystates.right:
  195.             self.bat.set_motion(-self.batspeed)
  196.         elif self.keystates.right and not self.keystates.left:
  197.             self.bat.set_motion(self.batspeed)
  198.         else:
  199.             self.bat.set_motion(0)
  200.  
  201.     def start_game(self):
  202.         print("Starting!")
  203.         self.score = 0
  204.         width = self.block_width
  205.         height = self.block_height
  206.         row = 1
  207.         x = 0
  208.         y = self.top_rows * height
  209.         current_width = 0
  210.         self.game_is_over = False
  211.         self.blocks = []
  212.         while True:
  213.             if x >= self.width:
  214.                 row += 1
  215.                 if row > self.rows:
  216.                     break
  217.                 x = 0
  218.                 y += height
  219.             if row%2 == 0 and (x == 0 or x + width > self.width):
  220.                 current_width = width/2
  221.             else:
  222.                 current_width = width
  223.             self.blocks.append(Block(random.choice(self.colors),
  224.                 x,y,current_width,height))
  225.             x += current_width
  226.        
  227.         self.sprites.empty()
  228.         for b in self.blocks:
  229.             self.sprites.add(b.sprite)
  230.         self.bat = Bat(self, random.choice(self.colors),
  231.                 self.width/2-width, self.height-height*2, width*2, height)
  232.         self.sprites.add(self.bat.sprite)
  233.         self.ball = Ball(self, self.width/2, self.height/2,
  234.             self.balldelta[0], self.balldelta[1])
  235.         self.sprites.add(self.ball.sprite)
  236.    
  237.     def update_screen(self):
  238.         self.screen.blit(self.bg, (0,0))
  239.         self.sprites.draw(self.screen)
  240.         scr = self.screen.get_rect()
  241.         score_surface = self.font_score.render("SCORE: " + str(self.score),
  242.             False, pygame.Color(255,255,255))
  243.         self.screen.blit(score_surface,
  244.             score_surface.get_rect(right=scr.right, top=scr.top))
  245.         if self.gamestate == "Initial":
  246.             gameover2_surface = self.font_gameover2.render("Press any key to start",
  247.                 False, pygame.Color(255,255,255))
  248.             self.screen.blit(gameover2_surface,
  249.                 gameover2_surface.get_rect(top=scr.centery, centerx=scr.centerx))
  250.         if self.gamestate == "Game over":
  251.             gameover_surface = self.font_gameover.render("GAME OVER",
  252.                 False, pygame.Color(255,255,255))
  253.             gameover2_surface = self.font_gameover2.render("Press \'r\' to retry",
  254.                 False, pygame.Color(255,255,255))
  255.             self.screen.blit(gameover_surface,
  256.                 gameover_surface.get_rect(bottom=scr.centery - 5,
  257.                     centerx=scr.centerx))
  258.             self.screen.blit(gameover2_surface,
  259.                 gameover2_surface.get_rect(top=scr.centery + 5,
  260.                     centerx=scr.centerx))
  261.         pygame.display.flip()
  262.    
  263.  
  264. class Keystates:
  265.     """keeps track of key states. True = pressed, False = not pressed"""
  266.     def __init__(self):
  267.         self.left = False
  268.         self.right = False
  269.  
  270.     def set_key(self, key):
  271.         """mark key as currently pressed
  272.            returns True if a key recognized by game interface"""
  273.         ignore_key = False
  274.         if key == K_LEFT:
  275.             self.left = True
  276.         elif key == K_RIGHT:
  277.             self.right = True
  278.         else: ignore_key = True
  279.         return ignore_key
  280.  
  281.     def unset_key(self, key):
  282.         """mark key as currently free
  283.          returns True if a key recognized by game interface"""
  284.         ignore_key = False
  285.         if key == K_LEFT:
  286.             self.left = False
  287.         elif key == K_RIGHT:
  288.             self.right = False
  289.         else: ignore_key = True
  290.         return ignore_key
  291.  
  292.  
  293. if __name__ == '__main__':
  294.     game = GameMain()
  295.     game.run()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement