A Flappy Bird Clone using python-pygame
转自:https://github.com/sourabhv/FlapPyBird
很好的学习案例
from itertools import cycle
import random
import sysimport pygame
from pygame.locals import *FPS = 30
SCREENWIDTH = 288
SCREENHEIGHT = 512
# amount by which base can maximum shift to left
PIPEGAPSIZE = 100 # gap between upper and lower part of pipe
BASEY = SCREENHEIGHT * 0.79
# image, sound and hitmask dicts
IMAGES, SOUNDS, HITMASKS = {}, {}, {}# list of all possible players (tuple of 3 positions of flap)
PLAYERS_LIST = (# red bird('assets/sprites/redbird-upflap.png','assets/sprites/redbird-midflap.png','assets/sprites/redbird-downflap.png',),# blue bird(# amount by which base can maximum shift to left'assets/sprites/bluebird-upflap.png','assets/sprites/bluebird-midflap.png','assets/sprites/bluebird-downflap.png',),# yellow bird('assets/sprites/yellowbird-upflap.png','assets/sprites/yellowbird-midflap.png','assets/sprites/yellowbird-downflap.png',),
)# list of backgrounds
BACKGROUNDS_LIST = ('assets/sprites/background-day.png','assets/sprites/background-night.png',
)# list of pipes
PIPES_LIST = ('assets/sprites/pipe-green.png','assets/sprites/pipe-red.png',
)try:xrange
except NameError:xrange = rangedef main():global SCREEN, FPSCLOCKpygame.init()FPSCLOCK = pygame.time.Clock()SCREEN = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))pygame.display.set_caption('Flappy Bird')# numbers sprites for score displayIMAGES['numbers'] = (pygame.image.load('assets/sprites/0.png').convert_alpha(),pygame.image.load('assets/sprites/1.png').convert_alpha(),pygame.image.load('assets/sprites/2.png').convert_alpha(),pygame.image.load('assets/sprites/3.png').convert_alpha(),pygame.image.load('assets/sprites/4.png').convert_alpha(),pygame.image.load('assets/sprites/5.png').convert_alpha(),pygame.image.load('assets/sprites/6.png').convert_alpha(),pygame.image.load('assets/sprites/7.png').convert_alpha(),pygame.image.load('assets/sprites/8.png').convert_alpha(),pygame.image.load('assets/sprites/9.png').convert_alpha())# game over spriteIMAGES['gameover'] = pygame.image.load('assets/sprites/gameover.png').convert_alpha()# message sprite for welcome screenIMAGES['message'] = pygame.image.load('assets/sprites/message.png').convert_alpha()# base (ground) spriteIMAGES['base'] = pygame.image.load('assets/sprites/base.png').convert_alpha()# soundsif 'win' in sys.platform:soundExt = '.wav'else:soundExt = '.ogg'SOUNDS['die'] = pygame.mixer.Sound('assets/audio/die' + soundExt)SOUNDS['hit'] = pygame.mixer.Sound('assets/audio/hit' + soundExt)SOUNDS['point'] = pygame.mixer.Sound('assets/audio/point' + soundExt)SOUNDS['swoosh'] = pygame.mixer.Sound('assets/audio/swoosh' + soundExt)SOUNDS['wing'] = pygame.mixer.Sound('assets/audio/wing' + soundExt)while True:# select random background spritesrandBg = random.randint(0, len(BACKGROUNDS_LIST) - 1)IMAGES['background'] = pygame.image.load(BACKGROUNDS_LIST[randBg]).convert()# select random player spritesrandPlayer = random.randint(0, len(PLAYERS_LIST) - 1)IMAGES['player'] = (pygame.image.load(PLAYERS_LIST[randPlayer][0]).convert_alpha(),pygame.image.load(PLAYERS_LIST[randPlayer][1]).convert_alpha(),pygame.image.load(PLAYERS_LIST[randPlayer][2]).convert_alpha(),)# select random pipe spritespipeindex = random.randint(0, len(PIPES_LIST) - 1)IMAGES['pipe'] = (pygame.transform.rotate(pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(), 180),pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(),)# hismask for pipesHITMASKS['pipe'] = (getHitmask(IMAGES['pipe'][0]),getHitmask(IMAGES['pipe'][1]),)# hitmask for playerHITMASKS['player'] = (getHitmask(IMAGES['player'][0]),getHitmask(IMAGES['player'][1]),getHitmask(IMAGES['player'][2]),)movementInfo = showWelcomeAnimation()crashInfo = mainGame(movementInfo)showGameOverScreen(crashInfo)def showWelcomeAnimation():"""Shows welcome screen animation of flappy bird"""# index of player to blit on screenplayerIndex = 0playerIndexGen = cycle([0, 1, 2, 1])# iterator used to change playerIndex after every 5th iterationloopIter = 0playerx = int(SCREENWIDTH * 0.2)playery = int((SCREENHEIGHT - IMAGES['player'][0].get_height()) / 2)messagex = int((SCREENWIDTH - IMAGES['message'].get_width()) / 2)messagey = int(SCREENHEIGHT * 0.12)basex = 0# amount by which base can maximum shift to leftbaseShift = IMAGES['base'].get_width() - IMAGES['background'].get_width()# player shm for up-down motion on welcome screenplayerShmVals = {'val': 0, 'dir': 1}while True:for event in pygame.event.get():if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):pygame.quit()sys.exit()if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):# make first flap sound and return values for mainGameSOUNDS['wing'].play()return {'playery': playery + playerShmVals['val'],'basex': basex,'playerIndexGen': playerIndexGen,}# adjust playery, playerIndex, basexif (loopIter + 1) % 5 == 0:playerIndex = next(playerIndexGen)loopIter = (loopIter + 1) % 30basex = -((-basex + 4) % baseShift)playerShm(playerShmVals)# draw spritesSCREEN.blit(IMAGES['background'], (0,0))SCREEN.blit(IMAGES['player'][playerIndex],(playerx, playery + playerShmVals['val']))SCREEN.blit(IMAGES['message'], (messagex, messagey))SCREEN.blit(IMAGES['base'], (basex, BASEY))pygame.display.update()FPSCLOCK.tick(FPS)def mainGame(movementInfo):score = playerIndex = loopIter = 0playerIndexGen = movementInfo['playerIndexGen']playerx, playery = int(SCREENWIDTH * 0.2), movementInfo['playery']basex = movementInfo['basex']baseShift = IMAGES['base'].get_width() - IMAGES['background'].get_width()# get 2 new pipes to add to upperPipes lowerPipes listnewPipe1 = getRandomPipe()newPipe2 = getRandomPipe()# list of upper pipesupperPipes = [{'x': SCREENWIDTH + 200, 'y': newPipe1[0]['y']},{'x': SCREENWIDTH + 200 + (SCREENWIDTH / 2), 'y': newPipe2[0]['y']},]# list of lowerpipelowerPipes = [{'x': SCREENWIDTH + 200, 'y': newPipe1[1]['y']},{'x': SCREENWIDTH + 200 + (SCREENWIDTH / 2), 'y': newPipe2[1]['y']},]pipeVelX = -4# player velocity, max velocity, downward accleration, accleration on flapplayerVelY = -9 # player's velocity along Y, default same as playerFlappedplayerMaxVelY = 10 # max vel along Y, max descend speedplayerMinVelY = -8 # min vel along Y, max ascend speedplayerAccY = 1 # players downward acclerationplayerRot = 45 # player's rotationplayerVelRot = 3 # angular speedplayerRotThr = 20 # rotation thresholdplayerFlapAcc = -9 # players speed on flappingplayerFlapped = False # True when player flapswhile True:for event in pygame.event.get():if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):pygame.quit()sys.exit()if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):if playery > -2 * IMAGES['player'][0].get_height():playerVelY = playerFlapAccplayerFlapped = TrueSOUNDS['wing'].play()# check for crash herecrashTest = checkCrash({'x': playerx, 'y': playery, 'index': playerIndex},upperPipes, lowerPipes)if crashTest[0]:return {'y': playery,'groundCrash': crashTest[1],'basex': basex,'upperPipes': upperPipes,'lowerPipes': lowerPipes,'score': score,'playerVelY': playerVelY,'playerRot': playerRot}# check for scoreplayerMidPos = playerx + IMAGES['player'][0].get_width() / 2for pipe in upperPipes:pipeMidPos = pipe['x'] + IMAGES['pipe'][0].get_width() / 2if pipeMidPos <= playerMidPos < pipeMidPos + 4:score += 1SOUNDS['point'].play()# playerIndex basex changeif (loopIter + 1) % 3 == 0:playerIndex = next(playerIndexGen)loopIter = (loopIter + 1) % 30basex = -((-basex + 100) % baseShift)# rotate the playerif playerRot > -90:playerRot -= playerVelRot# player's movementif playerVelY < playerMaxVelY and not playerFlapped:playerVelY += playerAccYif playerFlapped:playerFlapped = False# more rotation to cover the threshold (calculated in visible rotation)playerRot = 45playerHeight = IMAGES['player'][playerIndex].get_height()playery += min(playerVelY, BASEY - playery - playerHeight)# move pipes to leftfor uPipe, lPipe in zip(upperPipes, lowerPipes):uPipe['x'] += pipeVelXlPipe['x'] += pipeVelX# add new pipe when first pipe is about to touch left of screenif 0 < upperPipes[0]['x'] < 5:newPipe = getRandomPipe()upperPipes.append(newPipe[0])lowerPipes.append(newPipe[1])# remove first pipe if its out of the screenif upperPipes[0]['x'] < -IMAGES['pipe'][0].get_width():upperPipes.pop(0)lowerPipes.pop(0)# draw spritesSCREEN.blit(IMAGES['background'], (0,0))for uPipe, lPipe in zip(upperPipes, lowerPipes):SCREEN.blit(IMAGES['pipe'][0], (uPipe['x'], uPipe['y']))SCREEN.blit(IMAGES['pipe'][1], (lPipe['x'], lPipe['y']))SCREEN.blit(IMAGES['base'], (basex, BASEY))# print score so player overlaps the scoreshowScore(score)# Player rotation has a thresholdvisibleRot = playerRotThrif playerRot <= playerRotThr:visibleRot = playerRotplayerSurface = pygame.transform.rotate(IMAGES['player'][playerIndex], visibleRot)SCREEN.blit(playerSurface, (playerx, playery))pygame.display.update()FPSCLOCK.tick(FPS)def showGameOverScreen(crashInfo):"""crashes the player down ans shows gameover image"""score = crashInfo['score']playerx = SCREENWIDTH * 0.2playery = crashInfo['y']playerHeight = IMAGES['player'][0].get_height()playerVelY = crashInfo['playerVelY']playerAccY = 2playerRot = crashInfo['playerRot']playerVelRot = 7basex = crashInfo['basex']upperPipes, lowerPipes = crashInfo['upperPipes'], crashInfo['lowerPipes']# play hit and die soundsSOUNDS['hit'].play()if not crashInfo['groundCrash']:SOUNDS['die'].play()while True:for event in pygame.event.get():if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):pygame.quit()sys.exit()if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):if playery + playerHeight >= BASEY - 1:return# player y shiftif playery + playerHeight < BASEY - 1:playery += min(playerVelY, BASEY - playery - playerHeight)# player velocity changeif playerVelY < 15:playerVelY += playerAccY# rotate only when it's a pipe crashif not crashInfo['groundCrash']:if playerRot > -90:playerRot -= playerVelRot# draw spritesSCREEN.blit(IMAGES['background'], (0,0))for uPipe, lPipe in zip(upperPipes, lowerPipes):SCREEN.blit(IMAGES['pipe'][0], (uPipe['x'], uPipe['y']))SCREEN.blit(IMAGES['pipe'][1], (lPipe['x'], lPipe['y']))SCREEN.blit(IMAGES['base'], (basex, BASEY))showScore(score)playerSurface = pygame.transform.rotate(IMAGES['player'][1], playerRot)SCREEN.blit(playerSurface, (playerx,playery))FPSCLOCK.tick(FPS)pygame.display.update()def playerShm(playerShm):"""oscillates the value of playerShm['val'] between 8 and -8"""if abs(playerShm['val']) == 8:playerShm['dir'] *= -1if playerShm['dir'] == 1:playerShm['val'] += 1else:playerShm['val'] -= 1def getRandomPipe():"""returns a randomly generated pipe"""# y of gap between upper and lower pipegapY = random.randrange(0, int(BASEY * 0.6 - PIPEGAPSIZE))gapY += int(BASEY * 0.2)pipeHeight = IMAGES['pipe'][0].get_height()pipeX = SCREENWIDTH + 10return [{'x': pipeX, 'y': gapY - pipeHeight}, # upper pipe{'x': pipeX, 'y': gapY + PIPEGAPSIZE}, # lower pipe]def showScore(score):"""displays score in center of screen"""scoreDigits = [int(x) for x in list(str(score))]totalWidth = 0 # total width of all numbers to be printedfor digit in scoreDigits:totalWidth += IMAGES['numbers'][digit].get_width()Xoffset = (SCREENWIDTH - totalWidth) / 2for digit in scoreDigits:SCREEN.blit(IMAGES['numbers'][digit], (Xoffset, SCREENHEIGHT * 0.1))Xoffset += IMAGES['numbers'][digit].get_width()def checkCrash(player, upperPipes, lowerPipes):"""returns True if player collders with base or pipes."""pi = player['index']player['w'] = IMAGES['player'][0].get_width()player['h'] = IMAGES['player'][0].get_height()# if player crashes into groundif player['y'] + player['h'] >= BASEY - 1:return [True, True]else:playerRect = pygame.Rect(player['x'], player['y'],player['w'], player['h'])pipeW = IMAGES['pipe'][0].get_width()pipeH = IMAGES['pipe'][0].get_height()for uPipe, lPipe in zip(upperPipes, lowerPipes):# upper and lower pipe rectsuPipeRect = pygame.Rect(uPipe['x'], uPipe['y'], pipeW, pipeH)lPipeRect = pygame.Rect(lPipe['x'], lPipe['y'], pipeW, pipeH)# player and upper/lower pipe hitmaskspHitMask = HITMASKS['player'][pi]uHitmask = HITMASKS['pipe'][0]lHitmask = HITMASKS['pipe'][1]# if bird collided with upipe or lpipeuCollide = pixelCollision(playerRect, uPipeRect, pHitMask, uHitmask)lCollide = pixelCollision(playerRect, lPipeRect, pHitMask, lHitmask)if uCollide or lCollide:return [True, False]return [False, False]def pixelCollision(rect1, rect2, hitmask1, hitmask2):"""Checks if two objects collide and not just their rects"""rect = rect1.clip(rect2)if rect.width == 0 or rect.height == 0:return Falsex1, y1 = rect.x - rect1.x, rect.y - rect1.yx2, y2 = rect.x - rect2.x, rect.y - rect2.yfor x in xrange(rect.width):for y in xrange(rect.height):if hitmask1[x1+x][y1+y] and hitmask2[x2+x][y2+y]:return Truereturn Falsedef getHitmask(image):"""returns a hitmask using an image's alpha."""mask = []for x in xrange(image.get_width()):mask.append([])for y in xrange(image.get_height()):mask[x].append(bool(image.get_at((x,y))[3]))return maskif __name__ == '__main__':main()
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
