mirror of
https://github.com/sourabhv/FlapPyBird.git
synced 2024-11-25 16:35:52 +08:00
Use pip instead of pipenv and add pygbag
This commit is contained in:
parent
3bcc6186a6
commit
a767a88be9
5
.flake8
Normal file
5
.flake8
Normal 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
3
.gitignore
vendored
@ -1 +1,4 @@
|
||||
*.pyc
|
||||
build
|
||||
.python-version
|
||||
*.egg-info
|
||||
|
22
.pre-commit-config.yaml
Normal file
22
.pre-commit-config.yaml
Normal 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
2
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
25
Makefile
Normal file
25
Makefile
Normal 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
12
Pipfile
@ -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
51
Pipfile.lock
generated
@ -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": {}
|
||||
}
|
30
README.md
30
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 <kbd>↑</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
BIN
assets/audio/die-pygbag.ogg
Normal file
Binary file not shown.
BIN
assets/audio/hit-pygbag.ogg
Normal file
BIN
assets/audio/hit-pygbag.ogg
Normal file
Binary file not shown.
BIN
assets/audio/point-pygbag.ogg
Normal file
BIN
assets/audio/point-pygbag.ogg
Normal file
Binary file not shown.
BIN
assets/audio/swoosh-pygbag.ogg
Normal file
BIN
assets/audio/swoosh-pygbag.ogg
Normal file
Binary file not shown.
BIN
assets/audio/wing-pygbag.ogg
Normal file
BIN
assets/audio/wing-pygbag.ogg
Normal file
Binary file not shown.
399
flappy.py
399
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())
|
||||
|
32
pyproject.toml
Normal file
32
pyproject.toml
Normal 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 = []
|
36
setup.py
36
setup.py
@ -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,
|
||||
)
|
Loading…
Reference in New Issue
Block a user