diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..5192df8
--- /dev/null
+++ b/.flake8
@@ -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
diff --git a/.gitignore b/.gitignore
index 0d20b64..9f4f402 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
*.pyc
+build
+.python-version
+*.egg-info
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..ecfe79d
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -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)
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7daecd5
--- /dev/null
+++ b/Makefile
@@ -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
diff --git a/Pipfile b/Pipfile
deleted file mode 100644
index 879a373..0000000
--- a/Pipfile
+++ /dev/null
@@ -1,12 +0,0 @@
-[[source]]
-name = "pypi"
-url = "https://pypi.org/simple"
-verify_ssl = true
-
-[dev-packages]
-
-[packages]
-pygame = "*"
-
-[requires]
-python_version = "3"
diff --git a/Pipfile.lock b/Pipfile.lock
deleted file mode 100644
index b39d6ac..0000000
--- a/Pipfile.lock
+++ /dev/null
@@ -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": {}
-}
diff --git a/README.md b/README.md
index 7e0fcd0..4030cde 100644
--- a/README.md
+++ b/README.md
@@ -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 ↑ or Space key to play and Esc 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/
diff --git a/assets/audio/die-pygbag.ogg b/assets/audio/die-pygbag.ogg
new file mode 100644
index 0000000..93676a0
Binary files /dev/null and b/assets/audio/die-pygbag.ogg differ
diff --git a/assets/audio/hit-pygbag.ogg b/assets/audio/hit-pygbag.ogg
new file mode 100644
index 0000000..6c8abc6
Binary files /dev/null and b/assets/audio/hit-pygbag.ogg differ
diff --git a/assets/audio/point-pygbag.ogg b/assets/audio/point-pygbag.ogg
new file mode 100644
index 0000000..0fbc95c
Binary files /dev/null and b/assets/audio/point-pygbag.ogg differ
diff --git a/assets/audio/swoosh-pygbag.ogg b/assets/audio/swoosh-pygbag.ogg
new file mode 100644
index 0000000..5ab9a3d
Binary files /dev/null and b/assets/audio/swoosh-pygbag.ogg differ
diff --git a/assets/audio/wing-pygbag.ogg b/assets/audio/wing-pygbag.ogg
new file mode 100644
index 0000000..49fecec
Binary files /dev/null and b/assets/audio/wing-pygbag.ogg differ
diff --git a/flappy.py b/flappy.py
index 57b662a..6742797 100644
--- a/flappy.py
+++ b/flappy.py
@@ -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())
diff --git a/main.py b/main.py
new file mode 120000
index 0000000..71fe1d4
--- /dev/null
+++ b/main.py
@@ -0,0 +1 @@
+flappy.py
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..b6be597
--- /dev/null
+++ b/pyproject.toml
@@ -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 = []
diff --git a/setup.py b/setup.py
deleted file mode 100644
index a551eef..0000000
--- a/setup.py
+++ /dev/null
@@ -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,
-)