Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- """
- A Breakout implementation in pygame
- Version: 0.2.1.0
- Current known bugs:
- Collision detection needs work
- Ball can get caught in endless horizontal movement
- """
- import pygame, os, sys, random
- from pygame.locals import *
- if not pygame.font: print('Warning: fonts not available.')
- if not pygame.mixer: print('Warning: sounds not available.')
- class Ball():
- def __init__(self, game, x, y, xd, yd):
- self.rad = 5
- self.sprite = pygame.sprite.Sprite()
- self.sprite.image = pygame.Surface([self.rad*2, self.rad*2])
- pygame.draw.ellipse(self.sprite.image,(255,255,255),self.sprite.image.get_rect())
- self.sprite.rect = self.sprite.image.get_rect()
- self.sprite.rect.x = x - self.rad
- self.sprite.rect.y = y - self.rad
- self.vect = pygame.math.Vector2(xd,yd)
- self.game = game
- self.upright = pygame.math.Vector2(0,1)
- def move(self):
- """move one tick's worth
- Returns true on an out
- """
- scrn = self.game.screen.get_rect()
- self.sprite.rect.move_ip(int(self.vect.x), int(self.vect.y))
- self.sprite.rect.clamp_ip(scrn)
- x, y = self.sprite.rect.center
- # screen-edge bounce
- if x+self.rad >= scrn.width: # right edge
- print("Right edge bounce")
- self.bounce(pygame.math.Vector2(1,0))
- elif x-self.rad <=0: #left edge
- print("Left edge bounce")
- self.bounce(pygame.math.Vector2(-1,0))
- if y+self.rad >= scrn.height: #lower edge
- print("Ball out")
- return True
- elif y-self.rad <= 0: #upper edge
- print("Top edge bounce")
- self.bounce(pygame.math.Vector2(0,-1))
- return False
- def bounce(self, normal):
- self.vect.reflect_ip(normal)
- print("New angle: "+str(round(self.vect.angle_to(self.upright),2)))
- class Bat():
- def __init__(self, game, color, x, y, width, height):
- self.sprite = pygame.sprite.Sprite()
- self.sprite.image = pygame.Surface([width, height])
- self.sprite.image.fill(color)
- self.sprite.rect = self.sprite.image.get_rect()
- self.sprite.rect.x = x
- self.sprite.rect.y = y
- self.xdelta = 0
- self.game = game
- def move(self):
- """move one tick's worth"""
- self.sprite.rect.move_ip(self.xdelta, 0)
- self.sprite.rect.clamp_ip(self.game.screen.get_rect())
- def set_motion(self, delta):
- """change movement delta"""
- self.xdelta = delta
- class Block():
- def __init__(self, color, x, y, width, height):
- self.sprite = pygame.sprite.Sprite()
- self.sprite.image = pygame.Surface([width, height])
- self.sprite.image.fill(color)
- self.sprite.rect = self.sprite.image.get_rect()
- self.sprite.rect.x = x
- self.sprite.rect.y = y
- self.killed = False
- def die(self):
- self.killed = True
- class GameMain():
- def __init__(self, width=600, height=300):
- pygame.init()
- random.seed(None)
- self.width = width
- self.height = height
- self.screen = pygame.display.set_mode((self.width,self.height))
- self.bg = pygame.Surface(self.screen.get_size())
- self.bg = self.bg.convert()
- self.bg.fill((0,0,0))
- pygame.display.set_caption("Breakout")
- self.clock = pygame.time.Clock()
- self.sprites = pygame.sprite.Group()
- self.keystates = Keystates()
- self.colors = [(127,0,0),(255,0,0),(0,127,0),(0,255,0),(0,0,127),(0,0,255),
- (127,127,0),(255,255,0),(127,0,127),(255,0,255),(0,127,127),(0,255,255),
- (127,127,127),(255,255,255),(127,255,0),(255,127,0),(127,0,255),
- (255,0,127),(0,127,255),(0,255,127),(127,127,255),(127,255,127),
- (255,127,127)]
- self.blocks = []
- self.top_rows = 3
- self.rows = 5
- self.batspeed = 3
- self.balldelta = (1,2)
- self.block_width = 20
- self.block_height = 10
- self.tilt_scale = 1.0 # adjusts amount of tilting based on hit location
- self.bat = None
- self.ball = None
- self.font_gameover = pygame.font.Font(None, 64)
- self.font_gameover2 = pygame.font.Font(None, 32)
- self.font_score = pygame.font.Font(None, 16)
- self.score = 0
- self.gamestate = "Initial" # possible states: Initial, Running, Game over
- def run(self):
- self.start_game()
- while True:
- self.clock.tick(60)
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- sys.exit()
- elif event.type == pygame.KEYDOWN:
- if self.gamestate == "Initial":
- self.gamestate = "Running"
- self.keystates.set_key(event.key)
- elif event.key == K_r and not self.gamestate == "Running":
- print("Restarting!")
- self.start_game()
- self.gamestate = "Running"
- elif event.key == K_p:
- if self.gamestate == "Running":
- self.gamestate = "Paused"
- print("Paused")
- elif self.gamestate == "Paused":
- self.gamestate = "Running"
- print("Unpaused")
- else:
- pass
- else:
- self.keystates.set_key(event.key)
- elif event.type == pygame.KEYUP:
- self.keystates.unset_key(event.key)
- if self.gamestate == "Running":
- self.control_bat()
- self.bat.move()
- out = self.ball.move()
- ball_center = self.ball.sprite.rect.center
- bat_center = self.bat.sprite.rect.center
- #collision detection with bat
- if self.bat.sprite.rect.colliderect(self.ball.sprite.rect):
- print("Bat collision")
- #simplest case for now: reflection from upper edge
- self.ball.sprite.rect.bottom = self.bat.sprite.rect.top
- norm = pygame.math.Vector2(0,1)
- #adjust angle based on spot hit and speed
- xs = self.bat.xdelta #bat motion
- xl = ball_center[0] - bat_center[0] #position difference
- degs = xl*self.tilt_scale
- norm.rotate_ip(degs)
- print("Bat angle adjustment: "+str(degs)+" degrees")
- self.ball.bounce(norm)
- #block collisions
- for b in self.blocks:
- if self.ball.sprite.rect.colliderect(b.sprite.rect) and not b.killed:
- print("Block collision")
- b.die()
- self.sprites.remove(b.sprite)
- #bounce ball based on direction of collision
- rct = b.sprite.rect
- if ball_center[0] >= rct.left and ball_center[0] <= rct.right: #bottom or top collision
- self.ball.bounce(pygame.math.Vector2(0,1))
- elif ball_center[1] >= rct.top and ball_center[1] <= rct.bottom: #side collision
- self.ball.bounce(pygame.math.Vector2(1,0))
- #TODO: corner cases
- self.blocks.remove(b)
- self.score += 1
- break #do not ricochet off multiple blocks in one frame
- if out:
- self.gamestate = "Game over"
- self.update_screen()
- def control_bat(self):
- if self.keystates.left and not self.keystates.right:
- self.bat.set_motion(-self.batspeed)
- elif self.keystates.right and not self.keystates.left:
- self.bat.set_motion(self.batspeed)
- else:
- self.bat.set_motion(0)
- def start_game(self):
- print("Starting!")
- self.score = 0
- width = self.block_width
- height = self.block_height
- row = 1
- x = 0
- y = self.top_rows * height
- current_width = 0
- self.game_is_over = False
- self.blocks = []
- while True:
- if x >= self.width:
- row += 1
- if row > self.rows:
- break
- x = 0
- y += height
- if row%2 == 0 and (x == 0 or x + width > self.width):
- current_width = width/2
- else:
- current_width = width
- self.blocks.append(Block(random.choice(self.colors),
- x,y,current_width,height))
- x += current_width
- self.sprites.empty()
- for b in self.blocks:
- self.sprites.add(b.sprite)
- self.bat = Bat(self, random.choice(self.colors),
- self.width/2-width, self.height-height*2, width*2, height)
- self.sprites.add(self.bat.sprite)
- self.ball = Ball(self, self.width/2, self.height/2,
- self.balldelta[0], self.balldelta[1])
- self.sprites.add(self.ball.sprite)
- def update_screen(self):
- self.screen.blit(self.bg, (0,0))
- self.sprites.draw(self.screen)
- scr = self.screen.get_rect()
- score_surface = self.font_score.render("SCORE: " + str(self.score),
- False, pygame.Color(255,255,255))
- self.screen.blit(score_surface,
- score_surface.get_rect(right=scr.right, top=scr.top))
- if self.gamestate == "Initial":
- gameover2_surface = self.font_gameover2.render("Press any key to start",
- False, pygame.Color(255,255,255))
- self.screen.blit(gameover2_surface,
- gameover2_surface.get_rect(top=scr.centery, centerx=scr.centerx))
- if self.gamestate == "Game over":
- gameover_surface = self.font_gameover.render("GAME OVER",
- False, pygame.Color(255,255,255))
- gameover2_surface = self.font_gameover2.render("Press \'r\' to retry",
- False, pygame.Color(255,255,255))
- self.screen.blit(gameover_surface,
- gameover_surface.get_rect(bottom=scr.centery - 5,
- centerx=scr.centerx))
- self.screen.blit(gameover2_surface,
- gameover2_surface.get_rect(top=scr.centery + 5,
- centerx=scr.centerx))
- pygame.display.flip()
- class Keystates:
- """keeps track of key states. True = pressed, False = not pressed"""
- def __init__(self):
- self.left = False
- self.right = False
- def set_key(self, key):
- """mark key as currently pressed
- returns True if a key recognized by game interface"""
- ignore_key = False
- if key == K_LEFT:
- self.left = True
- elif key == K_RIGHT:
- self.right = True
- else: ignore_key = True
- return ignore_key
- def unset_key(self, key):
- """mark key as currently free
- returns True if a key recognized by game interface"""
- ignore_key = False
- if key == K_LEFT:
- self.left = False
- elif key == K_RIGHT:
- self.right = False
- else: ignore_key = True
- return ignore_key
- if __name__ == '__main__':
- game = GameMain()
- game.run()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement