Last week, my niece came over and picked up the phone to show me the flappy bird game she played. But a few minutes later, her mother took the phone away, then she was a heartbreaking cry and rolled on the ground. When I looked at her watery eyes, as her uncle, I thought of a plan - use Python to program the "Flappy bird" game on CrowPi to save my niece. Her mother won't let her play the phone because she thinks that the phone will only affect her learning and let her indulge in the virtual world of the Internet. Therefor , CrowPi must be a wonderful choice. CrowPi is based on learning and entertainment. It allows users to learn while playing, in order to stimulate users' interest in learning, so as to maximize the user's commitment to learning. CrowPi's controller is the latest version of the Raspberry Pi 3 Model B+, thanks to the powerful features of the Raspberry Pi, which allows users to do almost anything on the CrowPi that can be done on a computer. In addition, It is not only equipped with 7 inch HD touchscreen along with a camera, but it also has a lot of cool learning modules, such as digital tube, ultrasonic sensor, lcd display, led matrix, etc. even have a breadboard on the user To build your own circuit, the most important thing is that it looks so beautiful! So I took one for myself.

122-1

Let's talk about this game first. Flappy Bird is easy to operate. Click on the screen to make Bird rise, score through the column obstacles, and hit the game. Because of the height of obstacles, controlling the rise and fall of Bird requires quick and flexible response, and it is not easy to get a higher score.

We need to analyze this game before programming. This game is roughly divided into 4 parts: 1, background 2, bird 3, pipe 4, menu, start and end screen, score, etc. In these four parts, our main analysis is birds and pipes. The bird will always fly to the right, and will drop if you don't click the screen.Every time you click on the screen, the bird will make the upward flight. When you don’t click the screen, the bird is down. Pipe will have two colors randomly appearing and one direction up and down, it will appear constantly and each time a pipe will add a point. The game will end when the bird hits the pipe or falls to the ground. Gossip less, we hurry to start our programming! Let's start with a brief modular introduction, and finally the full code will be given. First of all, I think it is also the most important to import our library files. from itertools import cycle import random import sys import pygame      #Import the pygame library into a python program from pygame.locals import *      #Need to introduce all the constants in pygame Then I will define some variables, such as the size of the screen and FPS(control how often each loop runs). And some array to store image, sound, hitmask. FPS = 30 SCREENWIDTH  = 288 #Screen width SCREENHEIGHT = 512 #Screen height IMAGES, SOUNDS, HITMASKS = {}, {}, {} In the main() function, I will add some image, sound and how to load  background and player sprites and so on. IMAGES['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()     ) 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)
The next step is to generate random, although the environment is still the environment, but the background is different, the birds are different, and the pipeline is random.
while True:
        # select random background sprites  
        randBg = random.randint(0, len(BACKGROUNDS_LIST) - 1)        #Randomly choose 0 or 1
        IMAGES['background'] =                                                                                                  pygame.image.load(BACKGROUNDS_LIST[randBg]).convert()        #Load random background
        # select random player sprites 
        randPlayer = 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 sprites 
        pipeindex = random.randint(0, len(PIPES_LIST) - 1)
        IMAGES['pipe'] = (
            pygame.transform.rotate(
                pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(), 180),   #Rotate 180 degrees
            pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(),
        )               #One above the pipe one below the one pipe
        # hismask for pipes 
        HITMASKS['pipe'] = (
            getHitmask(IMAGES['pipe'][0]),
            getHitmask(IMAGES['pipe'][1]),
        )
        # hitmask for player 
        HITMASKS['player'] = (
            getHitmask(IMAGES['player'][0]),
            getHitmask(IMAGES['player'][1]),
            getHitmask(IMAGES['player'][2]),
        )
        movementInfo = showWelcomeAnimation()     #Return 'playery' (player location), 'basex' (base image location) 'playerIndexGen' (flight posture index)
        crashInfo = mainGame(movementInfo)
        showGameOverScreen(crashInfo)
The above has already prepared the materials to be used in the game, then we have to start setting up the game screen.First, in the showWelcomeAnimation() function,I will make some decoration for the game. As everyone knows, a good looking game welcome interface is very eye-catching and important. And what I want to emphasize the pygame.event() event handler, which handles keyboard or mouse events with ease.
 for event in pygame.event.get():       #Use pygame.event.get() to handle all events
            #If quit or press esc after pressing the button, the game ends
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):         
                pygame.quit()
                sys.exit()
            #If you click or press after pressing the button
            if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP)):    
                # make first flap sound and return values for mainGame
                SOUNDS['wing'].play()       #Play the special effects sound of the fly
                return {
                    #Return to the initial position Enter maingame
                    'playery': playery + playerShmVals['val'],
                    'basex': basex,
                    'playerIndexGen': playerIndexGen,
                } 20170914161640072
The mainGame() function is arguably the most important part of this game code. In this function, you need to define the starting attitude of the flight, the pipe, the character's character speed, the maximum speed, the downward acceleration, etc., and also the effect of each click flight or the end of the game. score = playerIndex = loopIter = 0 playerIndexGen = movementInfo
        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():      #if click
                    playerVelY = playerFlapAcc          #Up
                    playerFlapped = True
                    SOUNDS['wing'].play()       #And play the flight sound
        # check for crash here         crashTest = checkCrash({'x': playerx, 'y': playery, 'index': playerIndex}, for uPipe, lPipe in zip(upperPipes, lowerPipes):             uPipe['x'] += pipeVelX       #pipe move             lPipe['x'] += pipeVelX 2.1  
Next we need to move the "tube". What we want to achieve is to add a new tube to the right when the first tube is about to touch the left side, and then remove the tube when the left tube disappears. At the same time, when you pass the pipe, you must print out the score and realize the accumulation.         for uPipe, lPipe in zip(upperPipes, lowerPipes):
            uPipe['x'] += pipeVelX           #pipe move
            lPipe['x'] += pipeVelX
        # add new pipe when first pipe is about to touch left of screen
        if 0 < upperPipes[0]['x'] < 5:         #Generate the next pipe when the first pipe moves to the left edge of the screen
            newPipe = getRandomPipe()
            upperPipes.append(newPipe[0])
            lowerPipes.append(newPipe[1])
        # remove first pipe if its out of the screen
        if upperPipes[0]['x'] < -IMAGES['pipe'][0].get_width(): 
            upperPipes.pop(0)
            lowerPipes.pop(0)
        # draw sprites
        SCREEN.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 score
        showScore(score) 
        # Player rotation has a threshold
        visibleRot = playerRotThr
        if playerRot <= playerRotThr:
            visibleRot = playerRot
        
        playerSurface = pygame.transform.rotate(IMAGES['player'][playerIndex], visibleRot)        #Rotating character
        SCREEN.blit(playerSurface, (playerx, playery))         #Show rotated characters
        pygame.display.update()        #Update window
        FPSCLOCK.tick(FPS)           #How long should the loop run
The showGameOverScreen() function is to crashes the player down and shows gameover image, also including sound effects at the end of the game, screens, and keyboard event handling.
    # play hit and die sounds
    SOUNDS['hit'].play()
    if not crashInfo['groundCrash']:       #If  haven’t hit the ground,  play the die sound.
        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

20170914161647780

And the next important thing is the checkCrash() function, which is mainly to detect whether the bird collides with the ground or the tube, and if so, returns true.
    # if player crashes into ground 
    if 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 rects
            uPipeRect = pygame.Rect(uPipe['x'], uPipe['y'], pipeW, pipeH)
            lPipeRect = pygame.Rect(lPipe['x'], lPipe['y'], pipeW, pipeH)
            # player and upper/lower pipe hitmasks
            pHitMask = HITMASKS['player'][pi]
            uHitmask = HITMASKS['pipe'][0]
            lHitmask = HITMASKS['pipe'][1]
            # if bird collided with upipe or lpipe
            uCollide = pixelCollision(playerRect, uPipeRect, pHitMask, uHitmask)
            lCollide = pixelCollision(playerRect, lPipeRect, pHitMask, lHitmask)
            if uCollide or lCollide:
                return [True, False]
The above is the main part of the game. Below is the complete game code I gave. The code comments are very detailed. Keep in mind that the image and sound information is found and added by yourself, otherwise it will not work. If you can't find pictures and sound materials, you can download the falppy bird apk file online and all the materials in the internal file are available. If you have Crowpi and you want to control the game by the sensor, please see my next blog--"Control Flappy bird by sensor on Crowpi ". Ok, let's start your game making. from itertools import cycle import random import sys import pygame         #Import the pygame library into a python program from pygame.locals import *         #Need to introduce all the constants in pygame FPS=30 SCREENWIDTH  = 288 #Screen width SCREENHEIGHT = 512 #Screen height # amount by which base can maximum shift to left PIPEGAPSIZE  = 100 # gap between upper and lower part of pipe  BASEY        = SCREENHEIGHT * 0.79 #The height of the base is the height of the point, the upper left corner is the coordinate starting point, so the height is down. IMAGES, SOUNDS, HITMASKS = {}, {}, {}      # image, sound and hitmask  dicts # 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 = range def main():     global SCREEN, FPSCLOCK     pygame.init()    #After initialization, we can use pygame to the fullest     #Before using the Pygame clock, you must first create an instance of the Clock object.     FPSCLOCK = pygame.time.Clock()      #Control how often each loop runs. It's like a timer in the control time process, pointing out "Now start the next loop"! Start the next cycle now!     SCREEN = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))     # Usually we need to create a window to facilitate our interaction with the program.    pygame.display.set_caption('Flappy Bird')      #Set window title     #Load and convert images     #In pygame you can use pygame.image.load() function to load bitmaps (support jpg, png, gif, bmp, pcx, tif, tga and other image formats).     #The convert_alpha() method will use a transparent method to draw the foreground object。     # Therefore, when loading a material with an alpha channel (such as PNG TGA), you need to use the convert_alpha() method. Of course, ordinary images can also use this method. There is no side effect.     IMAGES['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 sprite
IMAGES['gameover'] = pygame.image.load('assets/sprites/gameover.png').convert_alpha()
# message sprite for welcome screen
IMAGES['message'] = pygame.image.load('assets/sprites/message.png').convert_alpha()
# base (ground) sprite
IMAGES['base'] = pygame.image.load('assets/sprites/base.png').convert_alpha()
# sounds
# WAV OGG version refers to the audio format of the game
# WAV version belongs to the original game
# OGG is that the WAV of the audio format is changed to OGG by the converter, so that the configuration of the game is increased, and the size of the game itself is reduced to save space.
# can look at the same audio ogg version is much smaller than the wav version of the file
if 'win' in sys.platform:        #Determine the current system platform to set the sound file suffix
        soundExt = '.wav'
else:
        soundExt = '.ogg'
# Sound effects: pygame.mixer
# Sound = pygame.mixer.Sound('/home/liumin/love.wav') Loads an audio file with the specified filename and creates a Sound object. Audio files can be in wav, ogg and other formats.
# The contents of the audio file will be fully loaded into the memory.
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 sprites  
        randBg = random.randint(0, len(BACKGROUNDS_LIST) - 1)        #Randomly choose 0 or 1
        IMAGES['background'] =                                                                                                  pygame.image.load(BACKGROUNDS_LIST[randBg]).convert()        #Load random background
        # select random player sprites 
        randPlayer = 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 sprites 
        pipeindex = random.randint(0, len(PIPES_LIST) - 1)
        IMAGES['pipe'] = (
            pygame.transform.rotate(
                pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(), 180),   #Rotate 180 degrees
            pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(),
        )               #One above the pipe one below the one pipe
        # hismask for pipes 
        HITMASKS['pipe'] = (
            getHitmask(IMAGES['pipe'][0]),
            getHitmask(IMAGES['pipe'][1]),
        )
        # hitmask for player 
        HITMASKS['player'] = (
            getHitmask(IMAGES['player'][0]),
            getHitmask(IMAGES['player'][1]),
            getHitmask(IMAGES['player'][2]),
        )
        movementInfo = showWelcomeAnimation()     #Return 'playery' (player location), 'basex' (base image location) 'playerIndexGen' (flight posture index)
        crashInfo = mainGame(movementInfo)
        showGameOverScreen(crashInfo)
def showWelcomeAnimation():
    """Shows welcome screen animation of flappy bird"""
    # Index of player to blit on screen
    playerIndex = 0
    playerIndexGen = cycle([0, 1, 2, 1])
    # iterator used to change playerIndex after every 5th iteration
    loopIter = 0
    #Player location
    playerx = int(SCREENWIDTH * 0.2)
    playery = int((SCREENHEIGHT - IMAGES['player'][0].get_height()) / 2)
    #Welcome image location
    messagex = int((SCREENWIDTH - IMAGES['message'].get_width()) / 2)
    messagey = int(SCREENHEIGHT * 0.12)
    basex = 0
    # amount by which base can maximum shift to left 
    baseShift = IMAGES['base'].get_width() - IMAGES['background'].get_width()
    # player shm for up-down motion on welcome screen 
    playerShmVals = {'val': 0, 'dir': 1}
    while True:
        for event in pygame.event.get():       #Use pygame.event.get() to handle all events
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):        #If quit or press esc after pressing the button, the game ends
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP )):      #If you click or press after pressing the button
                # make first flap sound and return values for mainGame
                SOUNDS['wing'].play()       #Play the special effects sound of the fly
                return {
                    #Return to the initial position Enter maingame
                    'playery': playery + playerShmVals['val'],
                    'basex': basex,
                    'playerIndexGen': playerIndexGen,
                }
        # adjust playery, playerIndex, basex
        if (loopIter + 1) % 5 == 0:
            playerIndex = next(playerIndexGen)      #Get the sibling elements next to each element in the set of matching elements. Adjust the flight pose picture.
        loopIter = (loopIter + 1) % 30
        basex = -((-basex + 4) % baseShift)
        playerShm(playerShmVals)
        # draw sprites
        #screen.blit(space, (0,0))can draw a bitmap. The first parameter is the bitmap that is loaded and the second parameter is the starting coordinates of the drawing.
        SCREEN.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()      #Update entire window
        FPSCLOCK.tick(FPS)              #How long should the loop run
def mainGame(movementInfo):
    score = playerIndex = loopIter = 0        #The initial score and the initial player's pose and the number of iterations are 0.
    playerIndexGen = movementInfo['playerIndexGen']     #Get flight posture
    playerx, playery = int(SCREENWIDTH * 0.2), movementInfo['playery']      #player location
    basex = movementInfo['basex']          #base image location
    baseShift = IMAGES['base'].get_width() - IMAGES['background'].get_width()
    # get 2 new pipes to add to upperPipes lowerPipes list
    newPipe1 = getRandomPipe()
    newPipe2 = getRandomPipe()
    # list of upper pipes
    upperPipes = [
        {'x': SCREENWIDTH + 200, 'y': newPipe1[0]['y']},
        {'x': SCREENWIDTH + 200 + (SCREENWIDTH / 2), 'y': newPipe2[0]['y']},
    ]
    # list of lowerpipe
    lowerPipes = [
        {'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 flap 
    playerVelY    =  -9   # player's velocity along Y, default same as playerFlapped
    playerMaxVelY =  10   # max vel along Y, max descend speed
    playerMinVelY =  -8   # min vel along Y, max ascend speed
    playerAccY    =   1   # players downward accleration
    playerRot     =  45   # player's rotation
    playerVelRot  =   3   # angular speed
    playerRotThr  =  20   # rotation threshold
    playerFlapAcc =  -9   # players speed on flapping
    playerFlapped = False # True when player flaps
    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 > -2 * IMAGES['player'][0].get_height():      #if click
                    playerVelY = playerFlapAcc          #Up
                    playerFlapped = True
                    SOUNDS['wing'].play()       #And play the flight sound
        # check for crash here
        crashTest = checkCrash({'x': playerx, 'y': playery, 'index': playerIndex},
                               upperPipes, lowerPipes)
        if crashTest[0]:             #if falls to the ground or hits the pipe,then return to the end of the game
            return {
                'y': playery,
                'groundCrash': crashTest[1],
                'basex': basex,
                'upperPipes': upperPipes,
                'lowerPipes': lowerPipes,
                'score': score,
                'playerVelY': playerVelY,
                'playerRot': playerRot
            }
        # check for score
        playerMidPos = playerx + IMAGES['player'][0].get_width() / 2
        for pipe in upperPipes:
            pipeMidPos = pipe['x'] + IMAGES['pipe'][0].get_width() / 2
            if pipeMidPos <= playerMidPos < pipeMidPos + 4:     #When the character reaches the middle of the pipe gap +4, score+1, and play the score sound at this time
                score += 1
                SOUNDS['point'].play()
        # playerIndex basex change
        if (loopIter + 1) % 3 == 0:
            playerIndex = next(playerIndexGen)
        loopIter = (loopIter + 1) % 30
        basex = -((-basex + 100) % baseShift)
        # rotate the player
        if playerRot > -90:
            playerRot -= playerVelRot
        # player's movement
        if playerVelY < playerMaxVelY and not playerFlapped:
            playerVelY += playerAccY
        if playerFlapped:
            playerFlapped = False
            # more rotation to cover the threshold (calculated in visible rotation)
            playerRot = 45
        playerHeight = IMAGES['player'][playerIndex].get_height()
        playery += min(playerVelY, BASEY - playery - playerHeight)
        # move pipes to left
        for uPipe, lPipe in zip(upperPipes, lowerPipes):
            uPipe['x'] += pipeVelX           #pipe move
            lPipe['x'] += pipeVelX
        # add new pipe when first pipe is about to touch left of screen
        if 0 < upperPipes[0]['x'] < 5:         #Generate the next pipe when the first pipe moves to the left edge of the screen
            newPipe = getRandomPipe()
            upperPipes.append(newPipe[0])
            lowerPipes.append(newPipe[1])
        # remove first pipe if its out of the screen
        if upperPipes[0]['x'] < -IMAGES['pipe'][0].get_width(): 
            upperPipes.pop(0)
            lowerPipes.pop(0)
        # draw sprites
        SCREEN.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 score
        showScore(score) 
        # Player rotation has a threshold
        visibleRot = playerRotThr
        if playerRot <= playerRotThr:
            visibleRot = playerRot
        
        playerSurface = pygame.transform.rotate(IMAGES['player'][playerIndex], visibleRot)        #Rotating character
        SCREEN.blit(playerSurface, (playerx, playery))         #Show rotated characters
        pygame.display.update()        #Update window
        FPSCLOCK.tick(FPS)           #How long should the loop run
def showGameOverScreen(crashInfo):
    """crashes the player down ans shows gameover image"""
    score = crashInfo['score']       #Get the score
    playerx = SCREENWIDTH * 0.2
    playery = crashInfo['y']
    playerHeight = IMAGES['player'][0].get_height()
    playerVelY = crashInfo['playerVelY']
    playerAccY = 2
    playerRot = crashInfo['playerRot']
    playerVelRot = 7
    basex = crashInfo['basex']
    upperPipes, lowerPipes = crashInfo['upperPipes'], crashInfo['lowerPipes']
    # play hit and die sounds
    SOUNDS['hit'].play()
    if not crashInfo['groundCrash']:       #If  haven’t hit the ground,  play the die sound.
        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 shift
        if playery + playerHeight < BASEY - 1:
            playery += min(playerVelY, BASEY - playery - playerHeight)
        # player velocity change
        if playerVelY < 15:
            playerVelY += playerAccY
        # rotate only when it's a pipe crash
        if not crashInfo['groundCrash']:
            if playerRot > -90:
                playerRot -= playerVelRot
        # draw sprites
        SCREEN.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'] *= -1
    if playerShm['dir'] == 1:
         playerShm['val'] += 1
    else:
        playerShm['val'] -= 1
def getRandomPipe():      
    """returns a randomly generated pipe"""
    # y of gap between upper and lower pipe
    gapY = random.randrange(0, int(BASEY * 0.6 - PIPEGAPSIZE))
    gapY += int(BASEY * 0.2)
    pipeHeight = IMAGES['pipe'][0].get_height()
    pipeX = SCREENWIDTH + 10
    return [
        {'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 printed
    for digit in scoreDigits:
        totalWidth += IMAGES['numbers'][digit].get_width()
    Xoffset = (SCREENWIDTH - totalWidth) / 2
    for digit in scoreDigits:
        SCREEN.blit(IMAGES['numbers'][digit], (Xoffset, SCREENHEIGHT * 0.1))    #display score
        Xoffset += IMAGES['numbers'][digit].get_width()
def checkCrash(player, upperPipes, lowerPipes):
    """returns True if player collders with base or pipes."""
    pi = player['index']       #Flight posture
    player['w'] = IMAGES['player'][0].get_width()
    player['h'] = IMAGES['player'][0].get_height()
    # if player crashes into ground 
    if 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 rects
            uPipeRect = pygame.Rect(uPipe['x'], uPipe['y'], pipeW, pipeH)
            lPipeRect = pygame.Rect(lPipe['x'], lPipe['y'], pipeW, pipeH)
            # player and upper/lower pipe hitmasks
            pHitMask = HITMASKS['player'][pi]
            uHitmask = HITMASKS['pipe'][0]
            lHitmask = HITMASKS['pipe'][1]
            # if bird collided with upipe or lpipe
            uCollide = 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)        #Coincidence between roles and pipes
    if rect.width == 0 or rect.height == 0:         #Didn't coincide, it didn't hit
        return False
    x1, y1 = rect.x - rect1.x, rect.y - rect1.y
    x2, y2 = rect.x - rect2.x, rect.y - rect2.y
    for x in xrange(rect.width):
        for y in xrange(rect.height):
            if hitmask1[x1+x][y1+y] and hitmask2[x2+x][y2+y]:         #Hit it 
                return True
    return False
def getHitmask(image):
    """returns a hitmask using an image's alpha."""
    #Get the impact mask
    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 mask
if __name__ == '__main__':
    main()