I've been working on learning Pygame and I made a Breakout clone. I'm looking for stylistic advice and overall suggestions for improvement.Controls are simple:  -  and  to move.# Breakout/Arkanoid cloneimport pygame, sysfrom pygame.locals import *import math# General game attributesFPS = 100WINDOWWIDTH = 480WINDOWHEIGHT = 600MAXLEVEL = 6# Ball attributesBALLSTARTINGX = 0BALLSTARTINGY = 250BALLSPEED = 6 # pixels per tickSTARTINGANGLE = 60# Paddle attributesPADDLEWIDTH = 100PADDLEHEIGHT = 10PADDLESTARTINGX = (WINDOWWIDTH-PADDLEWIDTH)/2PADDLEY = WINDOWHEIGHT-PADDLEHEIGHTPADDLESPEED = 6# Brick attributesBRICKWIDTH = 50BRICKHEIGHT = 25SIDEBUFFER = 15TOPBUFFER = 220# ColorsWHITE       = (255, 255, 255)NAVYBLUE    = ( 60,  60, 100)RED         = (200,   0,   0)DARKRED     = (100,   0,   0)ORANGE      = (200, 100,   0)DARKORANGE  = (100,  50,   0)YELLOW      = (200, 200,   0)DARKYELLOW  = (100, 100,   0)GREEN       = (  0, 200,   0)DARKGREEN   = (  0, 100,   0)BLUE        = (  0,   0, 200)DARKBLUE    = (  0,   0, 100)PURPLE      = (200,   0, 200)DARKPURPLE  = (100,   0, 100)RAINBOW = [(RED, DARKRED),(ORANGE,DARKORANGE),(YELLOW,DARKYELLOW),           (GREEN,DARKGREEN),(BLUE,DARKBLUE),(PURPLE,DARKPURPLE)]def main():    global FPSCLOCK, DISPLAYSURF, FONT    pygame.init()    # Globals    FPSCLOCK = pygame.time.Clock()    DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))    # Caption    pygame.display.set_caption(Bricky)    # Game objects    ball = Ball(DISPLAYSURF)    paddle = Paddle(DISPLAYSURF)    game_board = Game_board(DISPLAYSURF)    new_level = True    # Font    FONT = pygame.font.Font('freesansbold.ttf', 18)    # Text objects    text_newlevel_surf = FONT.render(New level!, True, WHITE, NAVYBLUE)    text_newlevel = text_newlevel_surf.get_rect()    text_newlevel.center = (WINDOWWIDTH/2, WINDOWHEIGHT/2)    text_youwin_surf = FONT.render(YOU WIN!, True, WHITE, NAVYBLUE)    text_youwin = text_newlevel_surf.get_rect()    text_youwin.center = (WINDOWWIDTH/2, WINDOWHEIGHT/2)    text_gamestart_surf = FONT.render(Game start!, True, WHITE, NAVYBLUE)    text_gamestart = text_newlevel_surf.get_rect()    text_gamestart.center = (WINDOWWIDTH/2, WINDOWHEIGHT/2)    text_youlose_surf = FONT.render(YOU LOSE!, True, WHITE, NAVYBLUE)    text_youlose = text_newlevel_surf.get_rect()    text_youlose.center = (WINDOWWIDTH/2, WINDOWHEIGHT/2)    # Main loop    while True:        DISPLAYSURF.fill(NAVYBLUE)        for event in pygame.event.get():            if event.type == QUIT:                pygame.quit()                sys.exit()            elif event.type == KEYDOWN:                if(event.key == K_LEFT):                    paddle.direction -= paddle.speed                elif(event.key == K_RIGHT):                    paddle.direction += paddle.speed            elif event.type == KEYUP:                if(event.key == K_LEFT):                    paddle.direction += paddle.speed                elif(event.key == K_RIGHT):                    paddle.direction -= paddle.speed        if new_level:            game_board.new_board()            ball.posx, ball.posy = BALLSTARTINGX, BALLSTARTINGY            ball.angle = STARTINGANGLE            new_level = False            paddle.posx = PADDLESTARTINGX        bricks_cleared = len(game_board.brickarray) == 0        if bricks_cleared:            if game_board.roundnum == MAXLEVEL:                DISPLAYSURF.fill(NAVYBLUE)                DISPLAYSURF.blit(text_youwin_surf, text_youwin)                pygame.display.update()                pygame.time.wait(1000)            else:                new_level = True                DISPLAYSURF.fill(NAVYBLUE)                if game_board.roundnum == 0:                    DISPLAYSURF.blit(text_gamestart_surf, text_gamestart)                else:                    DISPLAYSURF.blit(text_newlevel_surf, text_newlevel)                pygame.display.update()                pygame.time.wait(1000)                game_board.roundnum += 1        elif ball.ballsremaining == -1:            DISPLAYSURF.fill(NAVYBLUE)            DISPLAYSURF.blit(text_youlose_surf, text_youlose)            pygame.display.update()            pygame.time.wait(2000)            new_level = True            game_board.roundnum = 0            ball.ballsremaining = 3        else:            paddle.update()            ball.update(paddle.posx, game_board.brickarray) # Handle ball collisions            game_board.update(ball.brickarray) # Destroy bricks colliding with ball        pygame.display.update()        FPSCLOCK.tick(FPS)class Game_board(object):    def __init__(self, surface):        self.surf = surface        self.roundnum = 6    def draw_brick(self, x, y, color):        pygame.draw.rect(self.surf, color[0], (x,y,BRICKWIDTH, BRICKHEIGHT))        pygame.draw.rect(self.surf, color[1], (x,y,BRICKWIDTH,BRICKHEIGHT), 3)    def draw_board(self):        for brick in self.brickarray:            self.draw_brick(brick[1][0], brick[1][1], RAINBOW[brick[0][1]])    def new_board(self):        self.brick_indexes = zip(range(9)*self.roundnum, [i for i in range(self.roundnum) for _ in range(9)])        self.brick_positions = [(SIDEBUFFER + x * BRICKWIDTH, (TOPBUFFER-(y*BRICKHEIGHT*1.5))) for x,y in self.brick_indexes]        self.brickarray = zip(self.brick_indexes, self.brick_positions)    def update(self, brickarray):        self.brickarray = brickarray        self.draw_board()class Paddle(Game_board):    def __init__(self, surf):                Initialize paddle. Paddle starts in the middle. Speed is constant.                self.surf = surf        self.posx, self.posy = PADDLESTARTINGX, PADDLEY        self.speed = PADDLESPEED        self.direction = 0        pygame.draw.rect(self.surf, WHITE,(self.posx, self.posy, PADDLEWIDTH, PADDLEHEIGHT))    def update(self):                Move paddle if the left or right key is held down. Do not allow        paddle to move out of bounds.                #############################        # Handling movement of paddle        #############################        self.posx += self.direction        ##############################################        # Making sure paddle does not go out of bounds        ##############################################        if self.posx < 0:            self.posx = 0        if self.posx > WINDOWWIDTH-PADDLEWIDTH:            self.posx = WINDOWWIDTH-PADDLEWIDTH        # Drawing paddle        pygame.draw.rect(self.surf, WHITE,(self.posx, self.posy, PADDLEWIDTH, PADDLEHEIGHT))class Ball(object):        Ball object for Bricky game. Handles collision detection and bouncing.        def __init__(self, surf):                Initialize ball. Ball always starts in same position with same        angle. Speed is constant throughout the game.                self.posx,self.posy = BALLSTARTINGX,BALLSTARTINGY        self.speed = BALLSPEED        self.angle = STARTINGANGLE        self.surf = surf        self.ballsremaining = 3        pygame.draw.circle(self.surf, WHITE, (self.posx,self.posy), 5, 0)    def update(self, paddlex, brickarray):        self.brickarray = brickarray                Calculate position of ball at next tick. If no collision occurs,        ball's position will change based on current angle and speed.        If ball bounces off of a brick or a wall, position will not        change this tick but angle will update to reflect the direction        the ball is facing, based on its angle before the collision.        If ball bounces off of the paddle, again position will not        change but angle will update based off of the location on the        paddle that the ball hit: the closer to the center the ball hits,        the closer to a vertical angle the ball will have. Hitting towards        the edges will give the ball a steeper angle closer to        0 or 180.                ############################        # Displaying remaining balls        ############################        text_ballsremaining_surf = FONT.render(Balls left: {}.format(self.ballsremaining), True, WHITE, NAVYBLUE)        text_ballsremaining = text_ballsremaining_surf.get_rect()        text_ballsremaining.center = (55, 10)        self.surf.blit(text_ballsremaining_surf, text_ballsremaining)        ######################################        # Next position if no collision occurs        ######################################        # Next position depends on current angle and speed        xdif = int(math.cos(self.angle*(math.pi/180)) * self.speed)        ydif = int(math.sin(self.angle*(math.pi/180)) * self.speed)        ########################        # Bouncing off of bricks        ########################        ycollission = [ y1 < (self.posy + ydif) < y2 for y1, y2 in [ (y, y+BRICKHEIGHT) for x,y in [ b[1] for b in self.brickarray ] ]]        xcollission = [ x1 < (self.posx + xdif) < x2 for x1, x2 in [ (x, x+BRICKWIDTH) for x,y in [ b[1] for b in self.brickarray ] ]]        collissions = [ xcollission[i] and ycollission[i] for i in range(len([ b[1] for b in self.brickarray ]))]        if any(collissions):            collided = self.brickarray.pop([ i for i in range(len(collissions)) if collissions[i] ][0])[1]            if (self.posx <= collided[0]) and (collided[0] <= (self.posx + xdif) <= collided[0]+BRICKWIDTH):                if self.angle < 90:                    adj = (90 - (self.angle%90))*2 # Change angle clockwise                else:                    adj = -(self.angle%90)*2 # change angle counter-clockwise            elif (self.posx >= (collided[0]+BRICKWIDTH)) and (collided[0] <= (self.posx + xdif) <= collided[0]+BRICKWIDTH):                if self.angle < 180:                    adj = -(self.angle%90)*2 # Change angle counter-clockwise                else:                    adj = (90 - (self.angle%90))*2 # Change angle clockwise            elif (self.posy <= collided[1]) and (collided[1] <= (self.posy + ydif) <= collided[1]+BRICKHEIGHT):                if self.angle < 90:                    adj = -(self.angle%90)*2 # Change angle counter-clockwise                else:                    adj = (90 - (self.angle%90))*2 # Change angle clockwise            elif (self.posy >= collided[1]+BRICKHEIGHT) and (collided[1] <= (self.posy + ydif) <= collided[1]+BRICKHEIGHT):                if self.angle < 270:                    adj = -(self.angle%90)*2 # Change angle counter-clockwise                else:                    adj = (90 - (self.angle%90))*2 # Change angle clockwise            self.angle = (self.angle + adj) % 360        ##########################################        # Bouncing against window borders        ##########################################        if not (0 < (self.posx + xdif) < WINDOWWIDTH): #Bounce off wall            if (90 <= self.angle <= 180) or (270 <= self.angle <= 360):                adj = -(self.angle%90)*2 # Change angle counterclockwise            else:                adj = (90 - (self.angle%90))*2 # Change angle clockwise            self.angle  = (self.angle + adj) % 360        elif self.posy + ydif < 0: # Bounce off ceiling            if (90 <= self.angle <= 180) or (270 <= self.angle <= 360):                adj = (90 - (self.angle%90))*2 # Change angle clockwise            else:                adj = -(self.angle%90)*2 # Change angle counterclockwise            self.angle = (self.angle + adj) % 360        ##############################################        # Bouncing off paddle or falling out of bounds        ##############################################        elif WINDOWHEIGHT > self.posy + ydif > WINDOWHEIGHT-PADDLEHEIGHT: # Bounce off paddle or fall out of bounds            if paddlex < self.posx < paddlex+PADDLEWIDTH:# Bounce off paddle                if (270 <= self.angle <= 360):                    adj = (90 - (self.angle%90))*2 # Change angle clockwise                else:                    adj = -(self.angle%90)*2 # Change angle counterclockwise                #####                # Angle changes based off of how far from the middle the ball has bounced                #####                paddleangle = (paddlex - self.posx) + PADDLEWIDTH/2                paddleangle *= 130.0/PADDLEWIDTH                self.angle = 270 - paddleangle            else: # Ball falls out of bounds                self.posx += xdif                self.posy += ydif                pygame.draw.circle(self.surf, WHITE, (self.posx,self.posy), 5, 0)        ########################################        # Movement if ball does not hit anything        ########################################        else:            self.posx += xdif            self.posy += ydif            pygame.draw.circle(self.surf, WHITE, (self.posx,self.posy), 5, 0)            if self.posy > WINDOWHEIGHT + 300:                self.posx,self.posy = BALLSTARTINGX, BALLSTARTINGY                self.angle = STARTINGANGLE                self.ballsremaining -= 1if __name__ == __main__:    main()
Bricky - A Breakout Clone in Pygame
python;pygame