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.

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.

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
# select random player sprites
randPlayer = random.randint(0, len(PLAYERS_LIST) - 1)
IMAGES['player'] = (
)
# select random pipe sprites
pipeindex = random.randint(0, len(PIPES_LIST) - 1)
IMAGES['pipe'] = (
pygame.transform.rotate(
)               #One above the pipe one below the one pipe
)
)
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 {
'playery': playery + playerShmVals['val'],
'basex': basex,
'playerIndexGen': playerIndexGen,
}
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
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

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
# if bird collided with upipe or lpipe
if uCollide or lCollide:
return [True, False]
# message sprite for welcome screen
# base (ground) sprite
# 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
# select random player sprites
randPlayer = random.randint(0, len(PLAYERS_LIST) - 1)
IMAGES['player'] = (
)
# select random pipe sprites
pipeindex = random.randint(0, len(PIPES_LIST) - 1)
IMAGES['pipe'] = (
pygame.transform.rotate(
)               #One above the pipe one below the one pipe
)
)
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 {
'playery': playery + playerShmVals['val'],
'basex': basex,
'playerIndexGen': playerIndexGen,
}
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
# if bird collided with upipe or lpipe
if uCollide or lCollide:
return [True, False]
return [False, False]
"""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):