Use pip instead of pipenv and add pygbag

This commit is contained in:
Sourabh Verma 2023-06-16 19:44:08 +05:30
parent 3bcc6186a6
commit a767a88be9
No known key found for this signature in database
GPG Key ID: 117177475D1726CF
17 changed files with 310 additions and 308 deletions

5
.flake8 Normal file
View File

@ -0,0 +1,5 @@
[flake8]
ignore = E203,W503,E501
exclude = .venv,.git,__pycache__,docs/source/conf.py,old,build,dist,migrations,__init__.py,conftest.py,admin.py,.tox
max-complexity = 10
max-line-length = 100

3
.gitignore vendored
View File

@ -1 +1,4 @@
*.pyc
build
.python-version
*.egg-info

22
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,22 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
exclude: ^.*\b(migrations)\b.*$
- repo: https://github.com/PyCQA/flake8
rev: 4.0.1
hooks:
- id: flake8
args: ["--config=.flake8"]
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
name: isort (python)

2
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,2 @@
{
}

25
Makefile Normal file
View File

@ -0,0 +1,25 @@
default:
@make run
run:
python flappy.py
web:
pygbag flappy.py
init:
@pip install -U pip; \
pip install -e ".[dev]"; \
pre-commit install; \
pre-commit:
pre-commit install
pre-commit-all:
pre-commit run --all-files
format:
black .
lint:
flake8 --config=../.flake8 --output-file=./coverage/flake8-report --format=default

12
Pipfile
View File

@ -1,12 +0,0 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
pygame = "*"
[requires]
python_version = "3"

51
Pipfile.lock generated
View File

@ -1,51 +0,0 @@
{
"_meta": {
"hash": {
"sha256": "e172a3e30534a9a0b74742497b40f2ab196a0890d1cb082eb481fb89e384456e"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"pygame": {
"hashes": [
"sha256:06dc92ccfea33b85f209db3d49f99a2a30c88fe9fb80fa2564cee443ece787b5",
"sha256:0919a2ec5fcb0d00518c2a5fa99858ccf22d7fbcc0e12818b317062d11386984",
"sha256:0a8c92e700e0042faefa998fa064616f330201890d6ea1c993eb3ff30ab53e99",
"sha256:220a1048ebb3d11a4d48cc4219ec8f65ca62fcafd255239478677625e8ead2e9",
"sha256:315861d2b8428f7b4d56d2c98d6c1acc18f08c77af4b129211bc036774f64be2",
"sha256:3469e87867832fe5226396626a8a6a9dac9b2e21a7819dd8cd96cf0e08bbcd41",
"sha256:54c19960180626165512d596235d75dc022d38844467cec769a8d8153fd66645",
"sha256:5ba598736ab9716f53dc943a659a9578f62acfe00c0c9c5490f3aca61d078f75",
"sha256:60ddc4f361babb30ff2d554132b1f3296490f3149d6c1c77682213563f59937a",
"sha256:6a49ab8616a9de534f1bf62c98beabf0e0bb0b6ff8917576bba22820bba3fdad",
"sha256:6d4966eeba652df2fd9a757b3fc5b29b578b47b58f991ad714471661ea2141cb",
"sha256:700d1781c999af25d11bfd1f3e158ebb660f72ebccb2040ecafe5069d0b2c0b6",
"sha256:73f4c28e894e76797b8ccaf6eb1205b433efdb803c70f489ebc3db6ac9c097e6",
"sha256:786eca2bea11abd924f3f67eb2483bcb22acff08f28dbdbf67130abe54b23797",
"sha256:7bcf586a1c51a735361ca03561979eea3180de45e6165bcdfa12878b752544af",
"sha256:82a1e93d82c1babceeb278c55012a9f5140e77665d372a6d97ec67786856d254",
"sha256:9e03589bc80a21ae951fca7659a767b7cac668289937e3756c0ab3d753cf6d24",
"sha256:aa8926a4e34fb0943abe1a8bb04a0ad82265341bf20064c0862db0a521100dfc",
"sha256:aa90689b889c417d2ac571ef2bbb5f7e735ae30c7553c60fae7508404f46c101",
"sha256:c9f8cdefee267a2e690bf17d61a8f5670b620f25a981f24781b034363a8eedc9",
"sha256:d9177afb2f46103bfc28a51fbc49ce18987a857e5c934db47b4a7030cb30fbd0",
"sha256:deb0551d4bbfb8131e2463a7fe1943bfcec5beb11acdf9c4bfa27fa5a9758d62",
"sha256:e7edfe57a5972aa9130ce9a186020a0f097e7a8e4c25e292109bdae1432b77f9",
"sha256:f0ad32efb9e26160645d62ba6cf3e5a5828dc4e82e8f41f9badfe7b685b07295"
],
"index": "pypi",
"version": "==1.9.4"
}
},
"develop": {}
}

View File

@ -6,32 +6,15 @@ A Flappy Bird Clone made using [python-pygame][pygame]
Setup (as tested on MacOS)
---------------------------
1. Install Python 3.x (recommended) 2.x from [here](https://www.python.org/download/releases/) (Or use your preffered package manager)
1. Install Python 3 from [here](https://www.python.org/download/releases/) (or use brew/apt/pyenv)
1. Install [pipenv]
1. Run `make init` (this will install pip packages, use virtualenv or something similar if you don't want to install globally)
1. _Optional_: Install PyGame 1.9.x from [here](http://www.pygame.org/download.shtml)
On MacOS, pipenv will install PyGame, please check how to install on your Linux/Windows machines
1. Clone the repository:
```bash
$ git clone https://github.com/sourabhv/FlapPyBird
```
or download as zip and extract.
1. In the root directory run
```bash
$ pipenv install
$ pipenv run python flappy.py
```
1. Run `make` to run the game.
1. Use <kbd>&uarr;</kbd> or <kbd>Space</kbd> key to play and <kbd>Esc</kbd> to close the game.
(For x64 windows, get exe [here](http://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame))
2. Optionally run `make web` to run the game in the browser (`pygbag`).
Notable forks
-------------
@ -48,8 +31,3 @@ Demo
----------
https://user-images.githubusercontent.com/2307626/130682424-9254b32d-efe0-406e-a6ea-3fb625a2df5e.mp4
[pygame]: http://www.pygame.org
[pipenv]: https://pipenv.readthedocs.io/en/latest/

BIN
assets/audio/die-pygbag.ogg Normal file

Binary file not shown.

BIN
assets/audio/hit-pygbag.ogg Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

399
flappy.py
View File

@ -1,14 +1,16 @@
from itertools import cycle
import asyncio
import random
import sys
from itertools import cycle
import pygame
from pygame.locals import *
from pygame.locals import K_ESCAPE, K_SPACE, K_UP, KEYDOWN, QUIT
FPS = 30
SCREENWIDTH = 288
SCREENWIDTH = 288
SCREENHEIGHT = 512
PIPEGAPSIZE = 100 # gap between upper and lower part of pipe
BASEY = SCREENHEIGHT * 0.79
PIPEGAPSIZE = 100 # gap between upper and lower part of pipe
BASEY = SCREENHEIGHT * 0.79
# image, sound and hitmask dicts
IMAGES, SOUNDS, HITMASKS = {}, {}, {}
@ -16,91 +18,93 @@ IMAGES, SOUNDS, HITMASKS = {}, {}, {}
PLAYERS_LIST = (
# red bird
(
'assets/sprites/redbird-upflap.png',
'assets/sprites/redbird-midflap.png',
'assets/sprites/redbird-downflap.png',
"assets/sprites/redbird-upflap.png",
"assets/sprites/redbird-midflap.png",
"assets/sprites/redbird-downflap.png",
),
# blue bird
(
'assets/sprites/bluebird-upflap.png',
'assets/sprites/bluebird-midflap.png',
'assets/sprites/bluebird-downflap.png',
"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',
"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',
"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',
"assets/sprites/pipe-green.png",
"assets/sprites/pipe-red.png",
)
try:
xrange
except NameError:
xrange = range
def main():
async def flappy():
global SCREEN, FPSCLOCK
pygame.init()
FPSCLOCK = pygame.time.Clock()
SCREEN = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))
pygame.display.set_caption('Flappy Bird')
pygame.display.set_caption("Flappy Bird")
# numbers sprites for score display
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()
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()
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()
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()
IMAGES["base"] = pygame.image.load(
"assets/sprites/base.png"
).convert_alpha()
# sounds
if 'win' in sys.platform:
soundExt = '.wav'
if "win" in sys.platform:
soundExt = ".wav"
else:
soundExt = '.ogg'
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)
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)
IMAGES['background'] = pygame.image.load(BACKGROUNDS_LIST[randBg]).convert()
IMAGES["background"] = pygame.image.load(
BACKGROUNDS_LIST[randBg]
).convert()
# select random player sprites
randPlayer = random.randint(0, len(PLAYERS_LIST) - 1)
IMAGES['player'] = (
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(),
@ -108,31 +112,34 @@ def main():
# select random pipe sprites
pipeindex = random.randint(0, len(PIPES_LIST) - 1)
IMAGES['pipe'] = (
IMAGES["pipe"] = (
pygame.transform.flip(
pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(), False, True),
pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(),
False,
True,
),
pygame.image.load(PIPES_LIST[pipeindex]).convert_alpha(),
)
# hitmask for pipes
HITMASKS['pipe'] = (
getHitmask(IMAGES['pipe'][0]),
getHitmask(IMAGES['pipe'][1]),
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]),
HITMASKS["player"] = (
getHitmask(IMAGES["player"][0]),
getHitmask(IMAGES["player"][1]),
getHitmask(IMAGES["player"][2]),
)
movementInfo = showWelcomeAnimation()
crashInfo = mainGame(movementInfo)
showGameOverScreen(crashInfo)
movementInfo = await showWelcomeAnimation()
crashInfo = await mainGame(movementInfo)
await showGameOverScreen(crashInfo)
def showWelcomeAnimation():
async def showWelcomeAnimation():
"""Shows welcome screen animation of flappy bird"""
# index of player to blit on screen
playerIndex = 0
@ -141,30 +148,32 @@ def showWelcomeAnimation():
loopIter = 0
playerx = int(SCREENWIDTH * 0.2)
playery = int((SCREENHEIGHT - IMAGES['player'][0].get_height()) / 2)
playery = int((SCREENHEIGHT - IMAGES["player"][0].get_height()) / 2)
messagex = int((SCREENWIDTH - IMAGES['message'].get_width()) / 2)
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()
baseShift = IMAGES["base"].get_width() - IMAGES["background"].get_width()
# player shm for up-down motion on welcome screen
playerShmVals = {'val': 0, 'dir': 1}
playerShmVals = {"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):
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 tap(event):
# make first flap sound and return values for mainGame
SOUNDS['wing'].play()
SOUNDS["wing"].play()
return {
'playery': playery + playerShmVals['val'],
'basex': basex,
'playerIndexGen': playerIndexGen,
"playery": playery + playerShmVals["val"],
"basex": basex,
"playerIndexGen": playerIndexGen,
}
# adjust playery, playerIndex, basex
@ -175,23 +184,26 @@ def showWelcomeAnimation():
playerShm(playerShmVals)
# draw sprites
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))
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()
await asyncio.sleep(0)
FPSCLOCK.tick(FPS)
def mainGame(movementInfo):
async def mainGame(movementInfo):
score = playerIndex = loopIter = 0
playerIndexGen = movementInfo['playerIndexGen']
playerx, playery = int(SCREENWIDTH * 0.2), movementInfo['playery']
playerIndexGen = movementInfo["playerIndexGen"]
playerx, playery = int(SCREENWIDTH * 0.2), movementInfo["playery"]
basex = movementInfo['basex']
baseShift = IMAGES['base'].get_width() - IMAGES['background'].get_width()
basex = movementInfo["basex"]
baseShift = IMAGES["base"].get_width() - IMAGES["background"].get_width()
# get 2 new pipes to add to upperPipes lowerPipes list
newPipe1 = getRandomPipe()
@ -199,64 +211,68 @@ def mainGame(movementInfo):
# list of upper pipes
upperPipes = [
{'x': SCREENWIDTH + 200, 'y': newPipe1[0]['y']},
{'x': SCREENWIDTH + 200 + (SCREENWIDTH / 2), 'y': newPipe2[0]['y']},
{"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']},
{"x": SCREENWIDTH + 200, "y": newPipe1[1]["y"]},
{"x": SCREENWIDTH + 200 + (SCREENWIDTH / 2), "y": newPipe2[1]["y"]},
]
dt = FPSCLOCK.tick(FPS)/1000
dt = FPSCLOCK.tick(FPS) / 1000
pipeVelX = -128 * dt
# player velocity, max velocity, downward acceleration, acceleration 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 acceleration
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
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 acceleration
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):
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 tap(event):
if playery > -2 * IMAGES["player"][0].get_height():
playerVelY = playerFlapAcc
playerFlapped = True
SOUNDS['wing'].play()
SOUNDS["wing"].play()
# check for crash here
crashTest = checkCrash({'x': playerx, 'y': playery, 'index': playerIndex},
upperPipes, lowerPipes)
crashTest = 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
"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
playerMidPos = playerx + IMAGES["player"][0].get_width() / 2
for pipe in upperPipes:
pipeMidPos = pipe['x'] + IMAGES['pipe'][0].get_width() / 2
pipeMidPos = pipe["x"] + IMAGES["pipe"][0].get_width() / 2
if pipeMidPos <= playerMidPos < pipeMidPos + 4:
score += 1
SOUNDS['point'].play()
SOUNDS["point"].play()
# playerIndex basex change
if (loopIter + 1) % 3 == 0:
@ -277,33 +293,36 @@ def mainGame(movementInfo):
# more rotation to cover the threshold (calculated in visible rotation)
playerRot = 45
playerHeight = IMAGES['player'][playerIndex].get_height()
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
lPipe['x'] += pipeVelX
uPipe["x"] += pipeVelX
lPipe["x"] += pipeVelX
# add new pipe when first pipe is about to touch left of screen
if 3 > len(upperPipes) > 0 and 0 < upperPipes[0]['x'] < 5:
if 3 > len(upperPipes) > 0 and 0 < upperPipes[0]["x"] < 5:
newPipe = getRandomPipe()
upperPipes.append(newPipe[0])
lowerPipes.append(newPipe[1])
# remove first pipe if its out of the screen
if len(upperPipes) > 0 and upperPipes[0]['x'] < -IMAGES['pipe'][0].get_width():
if (
len(upperPipes) > 0
and upperPipes[0]["x"] < -IMAGES["pipe"][0].get_width()
):
upperPipes.pop(0)
lowerPipes.pop(0)
# draw sprites
SCREEN.blit(IMAGES['background'], (0,0))
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["pipe"][0], (uPipe["x"], uPipe["y"]))
SCREEN.blit(IMAGES["pipe"][1], (lPipe["x"], lPipe["y"]))
SCREEN.blit(IMAGES['base'], (basex, BASEY))
SCREEN.blit(IMAGES["base"], (basex, BASEY))
# print score so player overlaps the score
showScore(score)
@ -311,40 +330,45 @@ def mainGame(movementInfo):
visibleRot = playerRotThr
if playerRot <= playerRotThr:
visibleRot = playerRot
playerSurface = pygame.transform.rotate(IMAGES['player'][playerIndex], visibleRot)
playerSurface = pygame.transform.rotate(
IMAGES["player"][playerIndex], visibleRot
)
SCREEN.blit(playerSurface, (playerx, playery))
pygame.display.update()
await asyncio.sleep(0)
FPSCLOCK.tick(FPS)
def showGameOverScreen(crashInfo):
async def showGameOverScreen(crashInfo):
"""crashes the player down and shows gameover image"""
score = crashInfo['score']
score = crashInfo["score"]
playerx = SCREENWIDTH * 0.2
playery = crashInfo['y']
playerHeight = IMAGES['player'][0].get_height()
playerVelY = crashInfo['playerVelY']
playery = crashInfo["y"]
playerHeight = IMAGES["player"][0].get_height()
playerVelY = crashInfo["playerVelY"]
playerAccY = 2
playerRot = crashInfo['playerRot']
playerRot = crashInfo["playerRot"]
playerVelRot = 7
basex = crashInfo['basex']
basex = crashInfo["basex"]
upperPipes, lowerPipes = crashInfo['upperPipes'], crashInfo['lowerPipes']
upperPipes, lowerPipes = crashInfo["upperPipes"], crashInfo["lowerPipes"]
# play hit and die sounds
SOUNDS['hit'].play()
if not crashInfo['groundCrash']:
SOUNDS['die'].play()
SOUNDS["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):
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 tap(event):
if playery + playerHeight >= BASEY - 1:
return
@ -357,40 +381,38 @@ def showGameOverScreen(crashInfo):
playerVelY += playerAccY
# rotate only when it's a pipe crash
if not crashInfo['groundCrash']:
if not crashInfo["groundCrash"]:
if playerRot > -90:
playerRot -= playerVelRot
# draw sprites
SCREEN.blit(IMAGES['background'], (0,0))
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["pipe"][0], (uPipe["x"], uPipe["y"]))
SCREEN.blit(IMAGES["pipe"][1], (lPipe["x"], lPipe["y"]))
SCREEN.blit(IMAGES['base'], (basex, BASEY))
SCREEN.blit(IMAGES["base"], (basex, BASEY))
showScore(score)
playerSurface = pygame.transform.rotate(IMAGES['player'][1], playerRot)
SCREEN.blit(playerSurface, (playerx,playery))
SCREEN.blit(IMAGES['gameover'], (50, 180))
playerSurface = pygame.transform.rotate(IMAGES["player"][1], playerRot)
SCREEN.blit(playerSurface, (playerx, playery))
SCREEN.blit(IMAGES["gameover"], (50, 180))
FPSCLOCK.tick(FPS)
pygame.display.update()
await asyncio.sleep(0)
def playerShm(playerShm):
"""oscillates the value of playerShm['val'] between 8 and -8"""
if abs(playerShm['val']) == 8:
playerShm['dir'] *= -1
if abs(playerShm["val"]) == 8:
playerShm["dir"] *= -1
if playerShm['dir'] == 1:
playerShm['val'] += 1
if playerShm["dir"] == 1:
playerShm["val"] += 1
else:
playerShm['val'] -= 1
playerShm["val"] -= 1
def getRandomPipe():
@ -398,55 +420,56 @@ def getRandomPipe():
# 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()
pipeHeight = IMAGES["pipe"][0].get_height()
pipeX = SCREENWIDTH + 10
return [
{'x': pipeX, 'y': gapY - pipeHeight}, # upper pipe
{'x': pipeX, 'y': gapY + PIPEGAPSIZE}, # lower pipe
{"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
totalWidth = 0 # total width of all numbers to be printed
for digit in scoreDigits:
totalWidth += IMAGES['numbers'][digit].get_width()
totalWidth += IMAGES["numbers"][digit].get_width()
Xoffset = (SCREENWIDTH - totalWidth) / 2
for digit in scoreDigits:
SCREEN.blit(IMAGES['numbers'][digit], (Xoffset, SCREENHEIGHT * 0.1))
Xoffset += IMAGES['numbers'][digit].get_width()
SCREEN.blit(IMAGES["numbers"][digit], (Xoffset, SCREENHEIGHT * 0.1))
Xoffset += IMAGES["numbers"][digit].get_width()
def checkCrash(player, upperPipes, lowerPipes):
"""returns True if player collides with base or pipes."""
pi = player['index']
player['w'] = IMAGES['player'][0].get_width()
player['h'] = IMAGES['player'][0].get_height()
pi = player["index"]
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:
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()
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)
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]
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)
@ -457,6 +480,7 @@ def checkCrash(player, upperPipes, lowerPipes):
return [False, False]
def pixelCollision(rect1, rect2, hitmask1, hitmask2):
"""Checks if two objects collide and not just their rects"""
rect = rect1.clip(rect2)
@ -467,20 +491,29 @@ def pixelCollision(rect1, rect2, hitmask1, hitmask2):
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]:
for x in range(rect.width):
for y in range(rect.height):
if hitmask1[x1 + x][y1 + y] and hitmask2[x2 + x][y2 + y]:
return True
return False
def getHitmask(image):
"""returns a hitmask using an image's alpha."""
mask = []
for x in xrange(image.get_width()):
for x in range(image.get_width()):
mask.append([])
for y in xrange(image.get_height()):
mask[x].append(bool(image.get_at((x,y))[3]))
for y in range(image.get_height()):
mask[x].append(bool(image.get_at((x, y))[3]))
return mask
if __name__ == '__main__':
main()
def tap(event):
left, _, _ = pygame.mouse.get_pressed()
return left or (
event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP)
)
if __name__ == "__main__":
asyncio.run(flappy())

1
main.py Symbolic link
View File

@ -0,0 +1 @@
flappy.py

32
pyproject.toml Normal file
View File

@ -0,0 +1,32 @@
[project]
name = "flappybird"
authors = [{name = "Sourabh Verma", email = "email@sourabh.dev"}]
version = "1.0.0"
description = "Flappy Bird in Pygame"
requires-python = ">=3.9,<4"
dependencies = [
"pygame == 2.4.0"
]
[project.optional-dependencies]
dev = [
"pygbag == 0.7.1",
"black >= 22.1.0",
"pre-commit >= 2.18.1",
"flake8 >= 4.0.1",
"isort >= 5.10.1"
]
[tool.black]
line-length = 80
exclude = '''
/(
| \.git
| build
)/
'''
[tool.isort]
profile = "black"
skip = []
skip_glob = []

View File

@ -1,36 +0,0 @@
import os
import sys
from distutils.core import setup
import py2exe
origIsSystemDLL = py2exe.build_exe.isSystemDLL
def isSystemDLL(pathname):
dlls = ("libfreetype-6.dll", "libogg-0.dll", "sdl_ttf.dll")
if os.path.basename(pathname).lower() in dlls:
return 0
return origIsSystemDLL(pathname)
py2exe.build_exe.isSystemDLL = isSystemDLL
sys.argv.append('py2exe')
setup(
name = 'Flappy Bird',
version = '1.0',
author = 'Sourabh Verma',
options = {
'py2exe': {
'bundle_files': 1, # doesn't work on win64
'compressed': True,
}
},
windows = [{
'script': "flappy.py",
'icon_resources': [
(1, 'flappy.ico')
]
}],
zipfile=None,
)