PyBreakout - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: General (https://python-forum.io/forum-1.html) +--- Forum: Code sharing (https://python-forum.io/forum-5.html) +--- Thread: PyBreakout (/thread-3016.html) |
PyBreakout - TerryRitchie - Apr-24-2017 A simple little breakout program I wrote using PyGame. ############################################################################################################# # ________ ___ ___ ________ ________ _______ ________ ___ __ ________ ___ ___ __________ # # |\ __ \|\ \ / /|\ __ \|\ __ \|\ ___ \ |\ __ \|\ \|\ \ |\ __ \|\ \|\ \|\___ ___\ # # \ \ \|\ \ \ \/ / | \ \|\ /\ \ \|\ \ \ __/|\ \ \|\ \ \ \/ /|\ \ \|\ \ \ \\\ \|___ \ \_| # # \ \ ____\ \ / / \ \ __ \ \ _ _\ \ \_|/_\ \ __ \ \ ___ \ \ \\\ \ \ \\\ \ \ \ \ # # \ \ \___|\/ / / \ \ \|\ \ \ \\ \\ \ \_|\ \ \ \ \ \ \ \\ \ \ \ \\\ \ \ \\\ \ \ \ \ # # \ \__\ __/ / / \ \_______\ \__\\ _\\ \_______\ \__\ \__\ \__\\ \__\ \_______\ \_______\ \ \__\ # # \|__||\___/ / \|_______|\|__|\|__|\|_______|\|__|\|__|\|__| \|__|\|_______|\|_______| \|__| # # \|___|/ # # v1.0 # ############################################################################################################# # PyBreakout 1.0 # Terry Ritchie # 04/24/17 # ----------------------------------------------------------------------------------------------------------- # - IMPORT LIBRARIES ---------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------------------- import pygame # pygame graphics engine import random # random number library import os # operating system library import winsound # windows sound library import sys # system functions from pygame.locals import * # pygame constants # ----------------------------------------------------------------------------------------------------------- # - DEFINE CONSTANTS ---------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------------------- FPS = 240 # frames per second of game play BLACK = ( 0, 0, 0) # define bright colors BLUE = ( 0, 0, 255) GREEN = ( 0, 255, 0) CYAN = ( 0, 255, 255) RED = (255, 0, 0) PURPLE = (255, 0, 255) YELLOW = (255, 255, 0) WHITE = (255, 255, 255) DBLUE = ( 0, 0, 127) # define dark colors DGREEN = ( 0, 127, 0) DCYAN = ( 0, 127, 127) DRED = (127, 0, 0) DPURPLE = (127, 0, 127) DYELLOW = (127, 127, 0) GRAY = (127, 127, 127) SILVER = (191, 191, 191) COLOR = (BLUE, GREEN, CYAN, RED, PURPLE, YELLOW, DBLUE, DGREEN, DCYAN, DRED, DPURPLE, DYELLOW) STARS = 100 # number of stars in starfield STARX = 0 # star x index within star list STARY = 1 # star y index within star list STARSPEED = 2 # star speed index within star list STARCOLOR = 3 # star color index within star list STARCLDIR = 4 # star color direction index within star list INPLAY = 4 # brick in play index within brick list BRICKHIT = 5 # number of times brick hit index within brick list # ----------------------------------------------------------------------------------------------------------- # - DEFINE VARIABLES ---------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------------------- brick = [] # master brick list star = [] # master star list saying = [] # rendered font surface images of each level text scoretext = [] # custom number font launch = False # ball has been launched newgame = False # new game in progress levelshown = False # has level saying been shown extra = False # extra paddle awarded spacebar = 0 # rendered font surface image p1txt = 0 # rendered font surface image hitxt = 0 # rendered font surface image welcome = 0 # rendered font surface image screen = 0 # window surface px = 0 # player paddle x coordinate py = 516 # player paddle y coordinate prect = 0 # paddle collision rectangle paddles = 3 # number of player paddles bx = 0.0 # ball x coordinate by = 0.0 # ball y coordinate brect = 0 # ball collision rectangle bxdir = 0.0 # ball x direction bydir = 0.0 # ball y direction bspeed = 0.0 # ball speed maxbspeed = 0.0 # ball maximum speed minxspeed = 0.0 # ball minimum horizontal speed bspeedinc = 0.0 # ball speed increment trajectory = 0.0 # ball trajectory when hit paddle score = 0 # player score hiscore = 0 # high score level = 0 # current level of play bhit = 0 # number of times brick must be hit bricks = 0 # total number of bricks clock = 0 # FPS timer countdown = 0 # count down timer frame = 0 # frame counter # ----------------------------------------------------------------------------------------------------------- # - DEFINE FUNCTIONS ---------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------------------- def Setup(): # creates the assets needed to play the game Setup() # ----------------------------------------------------------------------------------------------------------- global screen, clock, star, brick, spacebar, p1txt, hitxt, saying, welcome os.environ["SDL_VIDEO_CENTERED"] = "1" # center the surface on desktop pygame.init() # start pygame engine screen = pygame.display.set_mode((768, 576)) # create the surface window pygame.display.set_caption("PyBreakout!") # give window a caption pygame.mouse.set_visible(False) # hide mouse pointer clock = pygame.time.Clock() # start clock routines font = pygame.font.SysFont("Calibri", 40, True, False) # create font and rendered surfaces spacebar = font.render("PRESS SPACEBAR TO LAUNCH", True, WHITE) p1txt = font.render("PLAYER 1", True, YELLOW) hitxt = font.render("HIGH", True, YELLOW) welcome = font.render("WELCOME TO PYBREAKOUT!", True, WHITE) saying.append(font.render("Try Again", True, CYAN)) saying.append(font.render("Level 1 - Easy Peasy", True, CYAN)) saying.append(font.render("Level 2 - Here We Go!", True, CYAN)) saying.append(font.render("Level 3 - Twice As Fun", True, CYAN)) saying.append(font.render("Level 4 - Even Faster", True, CYAN)) saying.append(font.render("Level 5 - Outstanding!", True, CYAN)) saying.append(font.render("Level 6 - Double Whammy", True, CYAN)) saying.append(font.render("Level 7 - Super Human", True, CYAN)) saying.append(font.render("Level 8 - Unstoppable!", True, CYAN)) saying.append(font.render("Level 9 - Here Be Dragons", True, CYAN)) saying.append(font.render("Breakout Master!", True, CYAN)) scoretext.append(["0000 00 00 0000"]) # create custom number strings list scoretext.append([" 1 1 1 1 1"]) scoretext.append(["222 22222 222"]) scoretext.append(["333 3333 3333"]) scoretext.append(["4 44 4444 4 4"]) scoretext.append(["5555 555 5555"]) scoretext.append(["6666 6666 6666"]) scoretext.append(["777 7 7 7 7"]) scoretext.append(["8888 88888 8888"]) scoretext.append(["9999 9999 9999"]) for i in range(STARS): # create starfield sx = random.randrange(0, 767) sy = random.randrange(0, 575) cd = 0 while not(cd): cd = random.randrange(-1, 2) ss = 0 while ss < .25: ss = random.random() star.append([sx, sy, ss, random.randrange(1, 255), cd]) for i in range(6): # create bricks b = [] for j in range(16): b.append([j * 48 + 1, 96 + (i * 24) + 1, 47, 23, True, 0]) brick.append(b) # ----------------------------------------------------------------------------------------------------------- def Draw_Digit(dx, dy, d, c, s): # draw a large digit to the screen Draw_Digit() # ----------------------------------------------------------------------------------------------------------- # dx = digit top left x coordinate # dy = digit top left y coordinate # d = digit (0 - 9) # c = digit color # s = size of digit squares # The large digits are drawn as follows: # # x # --------- # 0 1 2 # +---+---+---+ +---+---+---+ If there is a character (non-space) in the text a # | 0 | * | * | * | | * | * | * | square is drawn in that position. As the nested for # | +---+---+---+ +---+---+---+ statements progress a number is drawn from the text # | 1 | * | | * | | | | * | that describes where a square should be placed in # | +---+---+---+ +---+---+---+ each row. # y | 2 | * | | * | | * | * | * | # | +---+---+---+ +---+---+---+ # | 3 | * | | * | | | | * | # | +---+---+---+ +---+---+---+ # | 4 | * | * | * | | * | * | * | # | +---+---+---+ +---+---+---+ # # "0000 00 00 0000" "333 3333 3333" # |||---|||---||| |||---|||---||| # Row-> 0 1 2 3 4 0 1 2 3 4 p = -1 # current position in text string for y in range(5): for x in range(3): p += 1 # increment text position if scoretext[d][0][p] != " ": # if character in position draw square pygame.draw.rect(screen, c, [dx + x * s, dy + y * s, s, s], 0) # ----------------------------------------------------------------------------------------------------------- def New_Game(): # starts a new game of pybreakout New_Game() # ----------------------------------------------------------------------------------------------------------- global level, launch, newgame, levelshown, score, paddles, extra launch = False # reset booleans newgame = True levelshown = False while newgame: # loop while in new game mode Play_Round() score = 0 # reset player score level = 0 # reset level of play paddles = 3 # reset number of player paddles extra = False # reset extra paddle # ----------------------------------------------------------------------------------------------------------- def Next_Level(): # sets up the next level of the game Next_Level() # ----------------------------------------------------------------------------------------------------------- global level, bspeed, bxdir, bydir, brick, bhit, maxbspeed, bspeedinc, bricks global launch, trajectory, levelshown, countdown, minxspeed, frame level += 1 # increment level of play bspeed = 1 # reset ball characteristics bxdir = .5 bydir = -1 if not(level % 3): # bricks hit twice every third level bhit = 2 else: bhit = 1 for i in range(6): # reset brick characteristics for j in range(16): brick[i][j][INPLAY] = True brick[i][j][BRICKHIT] = 0 bricks = 96 maxbspeed = 2 + (level / 8) # calculate maximum ball speed if maxbspeed > 4: maxbspeed = 4 minxspeed = maxbspeed / 4 * (1 + (maxbspeed / 4)) # calculate minimum ball horizontal speed bspeedinc = level / 96 # calculate ball speed increments trajectory = (100 / (level * 100)) * 200 # calculate level trajectory launch = True # reset booleans levelshown = False countdown = 9 # reset countdown frame = 0 # ----------------------------------------------------------------------------------------------------------- def Draw_Frame(): # draws a frame of the game Draw_Frame() # ----------------------------------------------------------------------------------------------------------- global countdown, frame, launch screen.fill(BLACK) Draw_Stars() # draw starfield Draw_Bricks() # draw bricks pygame.draw.circle(screen, RED, [px - 40, 526], 10, 0) # draw paddle pygame.draw.circle(screen, RED, [px + 40, 526], 10, 0) pygame.draw.rect(screen, WHITE, [px - 40, 516, 80 , 20], 0) for i in range(paddles): # draw paddles remaining x = (i + 1) * 60 pygame.draw.circle(screen, RED, [x - 20, 560], 5, 0) pygame.draw.circle(screen, RED, [x + 20, 560], 5, 0) pygame.draw.rect(screen, WHITE, [x - 19, 555, 38, 10], 0) screen.blit(hitxt, [525, 0]) # display text screen.blit(p1txt, [155, 0]) scoretxt = ((6 - len(str(score))) * "0") + str(score) # format scores hiscoretxt = ((6 - len(str(hiscore))) * "0") + str(hiscore) for i in range(6): # draw scores Draw_Digit(326 + (i * 20), 5, int(scoretxt[i]), WHITE, 5) Draw_Digit(628 + (i * 20), 5, int(hiscoretxt[i]), WHITE, 5) leveltxt = ((2 - len(str(level))) * "0") + str(level) for i in range(2): Draw_Digit(740 + (i * 8), 555, int(leveltxt[i]), WHITE, 2) if launch: # display level text lev = 0 if not(levelshown): lev = level if lev > 10: lev = 10 if paddles > 0: screen.blit(saying[lev], [(768 - pygame.Surface.get_width(saying[lev])) // 2, 265]) screen.blit(spacebar, [140, 400]) if countdown > 6: # display count down timer clr = GREEN elif countdown > 3: clr = YELLOW else: clr = RED Draw_Digit(369, 325, countdown, clr, 10) frame += 1 if frame == FPS: frame = 0 countdown -= 1 winsound.Beep(2200, 5) if countdown == 0: launch = False if newgame: screen.blit(welcome, [145,300]) else: pygame.draw.circle(screen, CYAN, [int(bx), int(by)], 7, 0) # draw ball pygame.display.flip() # ----------------------------------------------------------------------------------------------------------- def Draw_Stars(): # draws the moving starfield Draw_Stars() # ----------------------------------------------------------------------------------------------------------- global star for i in range(STARS): # cycle through all stars star[i][STARY] += star[i][STARSPEED] # update speed star[i][STARCOLOR] += star[i][STARCLDIR] # update color if star[i][STARCOLOR] == 0 or star[i][STARCOLOR] == 255: # keep color in limits star[i][STARCLDIR] *= -1 if star[i][STARY] >= 767: # star went off bottom of screen star[i][STARY] = random.randrange(-30, -10) star[i][STARX] = random.randrange(0, 768) c = star[i][STARCOLOR] pygame.draw.circle(screen, [c, c, c], [star[i][STARX], int(star[i][STARY])], 0, 0) # ----------------------------------------------------------------------------------------------------------- def Draw_Bricks(): # check for ball/brick collisions while bricks being drawn Draw_Bricks() # ----------------------------------------------------------------------------------------------------------- global bxdir, bydir, score, hiscore, bspeed, bricks, brick, extra, paddles alreadyhit = False for i in range(6): # cycle through all bricks for j in range(16): if brick[i][j][INPLAY]: # draw brick if in play brick_rect = pygame.Rect(brick[i][j][0:4]) pygame.draw.rect(screen, COLOR[i + brick[i][j][BRICKHIT] * 6], brick_rect, 0) if not(newgame): if brect.colliderect(brick_rect) and not(alreadyhit): # first brick hit? brick[i][j][BRICKHIT] += 1 # update brick hits if brick[i][j][BRICKHIT] == bhit: # max number of hits? brick[i][j][INPLAY] = False # brick out of play bricks -= 1 score += (6 - i) * 10 winsound.Beep((6 - i) * 200, 10) else: # brick still in play score += (6 - i) * 5 winsound.Beep(2200, 10) if score >= 10000 and not(extra): # award extra ship paddles += 1 extra = True for i in range(5): winsound.Beep((i + 1) * 1000, 5) alreadyhit = True # no more collision checks this loop bspeed += bspeedinc # increase ball speed if bspeed > maxbspeed: # keep ball speed in limits bspeed = maxbspeed if score > hiscore: # update high score hiscore = score if sgn(bxdir) == 1: # ball traveling right bdx = int(brick_rect[0] - bx) # ball distance from left side of brick else: # ball traveling left bdx = int(bx - brick_rect[0]) - 46 # ball distance from right side of brick if sgn(bydir) == 1: # ball traveling down bdy = int(brick_rect[1] - by) # ball distance from top of brick else: # ball traveling up bdy = int(by - brick_rect[1]) - 22 # ball distance from bottom of brick if bdx == bdy: # hit corner of brick bxdir = -bxdir bydir = -bydir elif bdx > bdy: # hit left/right side of brick bxdir = -bxdir else: # hit top/bottom of brick bydir = -bydir # ----------------------------------------------------------------------------------------------------------- def Get_Input(): # gets user input and processes Get_Input() # ----------------------------------------------------------------------------------------------------------- global px, launch, levelshown, newgame pygame.event.pump() # update keyboard buffer key = pygame.key.get_pressed() # get all keys pressed if key[K_SPACE] and launch: # launch ball with space bar? launch = False levelshown = True for event in pygame.event.get(): # cycle through events if event.type == QUIT or key[K_ESCAPE]: # leave game if closed or ESC pressed pygame.quit() sys.exit() elif (event.type == KEYUP or event.type == MOUSEBUTTONUP) and newgame: # start a new game? newgame = False launch = True elif event.type == MOUSEBUTTONUP and launch: # launch ball with mouse button? launch = False levelshown = True elif event.type == MOUSEMOTION: # mouse moved? px = event.pos[0] # set player paddle x to mouse x if px < 50: # keep paddle on screen px = 50 elif px > 718: px = 718 pygame.mouse.set_pos(px, py) # bind mouse to paddle # ----------------------------------------------------------------------------------------------------------- def Play_Round(): # plays a round of pybreakout Play_Round() # ----------------------------------------------------------------------------------------------------------- global bx, by, bxdir, bydir, px, brect, launch, paddles, bspeed, countdown Get_Input() # get user input and process if launch or newgame: # keep ball on paddle bx = px by = 511 else: # update ball position bx += bxdir by += bydir * bspeed if bx <= 7: # hit left side of screen? bx = 7 bxdir = -bxdir elif bx >= 760: # hit right side of screen? bx = 760 bxdir = -bxdir if by <= 7: # hit top of screen? bydir = -bydir elif by >= 568: # player missed ball? paddles -= 1 launch = True bspeed = 1 bxdir = .5 bydir = -1 countdown = 9 brect = pygame.Rect(bx - 5, by - 5, 10, 10) # create ball rectangle object prect = pygame.Rect(px - 50, 516, 100, 1) # create paddle rectangle object if brect.colliderect(prect): # did the objects collide? bydir = -bydir by = 511 bxdir += (bx - px) / trajectory # add some trajectory if abs(bxdir) > maxbspeed: # keep horizontal ball speed in limits bxdir = sgn(bxdir) * maxbspeed elif abs(bxdir) < minxspeed: bxdir = sgn(bxdir) * minxspeed winsound.Beep(440, 10) Draw_Frame() # draw the next frame of game clock.tick(FPS) # limit FPS # ----------------------------------------------------------------------------------------------------------- def sgn(n): # returns the sign of a number (-1, 0, or 1) sgn() # ----------------------------------------------------------------------------------------------------------- # n = any numeric value if n > 0: # positive return 1 elif n < 0: # negative return -1 else: # must be zero return 0 # ----------------------------------------------------------------------------------------------------------- # - MAIN PROGRAM LOOP --------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------------------- Setup() # generate game assets while True: # main loop New_Game() # initiate a new game while paddles > 0: # loop until player loses all paddles Next_Level() # set up next level of play while paddles > 0 and bricks: # loop until no paddles or bricks cleared Play_Round() # play the round RE: PyBreakout - RandoomDude - Apr-26-2017 This games works. Excellent work. I it. Good Skills RE: PyBreakout - TerryRitchie - Apr-26-2017 Thank you :) RE: PyBreakout - Mekire - Apr-26-2017 global very, good, jobBut, in all seriousness, it is great that you are making some working complete programs but there are a lot of things you need to change if you are going to write more complex code. My guess is you learned pygame from the Invent book (correct me if I'm wrong). Your main issue is use of globals. You should consider the global keyword completely off limits until you become more experienced. The one use case for it where it is acceptable is for declaring a global CONSTANT inside a function. I do this in pygame for instance when I want to load a graphic that I want to be available globally but can't be loaded until pygame and the display is set up. Rather than having all this setup code in the global namespace I do it in a function but declare the resource loads global. But, and this is the important part, they are constants. After they are created they are never changed. Also more trivial, but none of those color definitions (or at least the vast majority) are necessary. Nearly every color you could need (all the ones defined with names in html) are predefined for you so instead of writing (255, 0, 0) you just write pg.Color("red") and achieve much more readable code.Here is a template for a very simple pygame game: https://gist.github.com/Mekire/f67d830875bb99ded6f1450fb2939796 Even the simplest pygame programs should really adhere to that. With the complexity you will soon be approaching though you are going to need to look into some form of scene manager. Here is my scene manager template: https://github.com/Mekire/pygame-mutiscene-template-with-movie And for a slightly softer intro to the concept, check here: https://www.reddit.com/r/pygame/comments/3kghhj/simple_state_machine_example/ Also for a number of basic pygame examples written in a sane style check here: https://github.com/Mekire/pygame-samples RE: PyBreakout - TerryRitchie - Apr-27-2017 Thank you very much for all of the constructive help you have provided. Yes, Invent was one of the resources I have used to learn Python as you have pointed out. My main problem is that I started coding in 1980 using the many different dialects of BASIC offered on the many different systems of the time (TRS-80, TI99/4A, Commodore, etc..) I eventually moved into Pascal, then QuickBASIC, VBDOS/VBWin, and the like. I realize I have many old-school habits that I need to get rid of when using Python. OOP was never something I delved into but I realize with Python this is going to be required. I really appreciate the examples and templates you have provided me. I want to learn the Python way and helpful posts such as yours will help me get there. Thank you :) RE: PyBreakout - RandoomDude - Apr-27-2017 (Apr-26-2017, 08:14 PM)TerryRitchie Wrote: Thank you :)No.Thank you. |