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.
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,
}
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
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()