Skip to content

Commit fa4cbf6

Browse files
committed
add flappy bird game tutorial
1 parent 501cd4b commit fa4cbf6

File tree

15 files changed

+271
-0
lines changed

15 files changed

+271
-0
lines changed

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ This is a repository of all the tutorials of [The Python Code](https://www.thepy
299299
- [How to Create a Slide Puzzle Game in Python](https://www.thepythoncode.com/article/slide-puzzle-game-in-python). ([code](gui-programming/slide-puzzle))
300300
- [How to Make a Maze Game in Python](https://www.thepythoncode.com/article/build-a-maze-game-in-python). ([code](gui-programming/maze-game))
301301
- [How to Create a Platformer Game in Python](https://www.thepythoncode.com/article/platformer-game-with-pygame-in-python). ([code](gui-programming/platformer-game))
302+
- [How to Make a Flappy Bird Game in Python](https://thepythoncode.com/article/make-a-flappy-bird-game-python). ([code](gui-programming/flappy-bird-game))
302303

303304

304305
For any feedback, please consider pulling requests.

Diff for: gui-programming/flappy-bird-game/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# [How to Make a Flappy Bird Game in Python](https://thepythoncode.com/article/make-a-flappy-bird-game-python)

Diff for: gui-programming/flappy-bird-game/assets/bird/0.png

12.1 KB
Loading

Diff for: gui-programming/flappy-bird-game/assets/bird/1.png

11.3 KB
Loading

Diff for: gui-programming/flappy-bird-game/assets/bird/2.png

11.4 KB
Loading
39.1 KB
Loading
1.3 KB
Loading
4.35 KB
Loading

Diff for: gui-programming/flappy-bird-game/bird.py

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import pygame
2+
from settings import import_sprite
3+
4+
class Bird(pygame.sprite.Sprite):
5+
def __init__(self, pos, size):
6+
super().__init__()
7+
# bird basic info
8+
self.frame_index = 0
9+
self.animation_delay = 3
10+
self.jump_move = -9
11+
12+
# bird animation
13+
self.bird_img = import_sprite("assets/bird")
14+
self.image = self.bird_img[self.frame_index]
15+
self.image = pygame.transform.scale(self.image, (size, size))
16+
self.rect = self.image.get_rect(topleft = pos)
17+
self.mask = pygame.mask.from_surface(self.image)
18+
19+
# bird status
20+
self.direction = pygame.math.Vector2(0, 0)
21+
self.score = 0
22+
23+
# for bird's flying animation
24+
def _animate(self):
25+
sprites = self.bird_img
26+
sprite_index = (self.frame_index // self.animation_delay) % len(sprites)
27+
self.image = sprites[sprite_index]
28+
self.frame_index += 1
29+
self.rect = self.image.get_rect(topleft=(self.rect.x, self.rect.y))
30+
self.mask = pygame.mask.from_surface(self.image)
31+
if self.frame_index // self.animation_delay > len(sprites):
32+
self.frame_index = 0
33+
34+
# to make the bird fly higher
35+
def _jump(self):
36+
self.direction.y = self.jump_move
37+
38+
# updates the bird's overall state
39+
def update(self, is_jump):
40+
if is_jump:
41+
self._jump()
42+
self._animate()

Diff for: gui-programming/flappy-bird-game/game.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import pygame
2+
from settings import WIDTH, HEIGHT
3+
4+
pygame.font.init()
5+
6+
class GameIndicator:
7+
def __init__(self, screen):
8+
self.screen = screen
9+
self.font = pygame.font.SysFont('Bauhaus 93', 60)
10+
self.inst_font = pygame.font.SysFont('Bauhaus 93', 30)
11+
self.color = pygame.Color("white")
12+
self.inst_color = pygame.Color("black")
13+
14+
def show_score(self, int_score):
15+
bird_score = str(int_score)
16+
score = self.font.render(bird_score, True, self.color)
17+
self.screen.blit(score, (WIDTH // 2, 50))
18+
19+
def instructions(self):
20+
inst_text1 = "Press SPACE button to Jump,"
21+
inst_text2 = "Press \"R\" Button to Restart Game."
22+
ins1 = self.inst_font.render(inst_text1, True, self.inst_color)
23+
ins2 = self.inst_font.render(inst_text2, True, self.inst_color)
24+
self.screen.blit(ins1, (95, 400))
25+
self.screen.blit(ins2, (70, 450))

Diff for: gui-programming/flappy-bird-game/main.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import pygame, sys
2+
from settings import WIDTH, HEIGHT, ground_space
3+
from world import World
4+
5+
pygame.init()
6+
7+
screen = pygame.display.set_mode((WIDTH, HEIGHT + ground_space))
8+
pygame.display.set_caption("Flappy Bird")
9+
10+
class Main:
11+
def __init__(self, screen):
12+
self.screen = screen
13+
self.bg_img = pygame.image.load('assets/terrain/bg.png')
14+
self.bg_img = pygame.transform.scale(self.bg_img, (WIDTH, HEIGHT))
15+
self.ground_img = pygame.image.load('assets/terrain/ground.png')
16+
self.ground_scroll = 0
17+
self.scroll_speed = -6
18+
self.FPS = pygame.time.Clock()
19+
self.stop_ground_scroll = False
20+
21+
def main(self):
22+
world = World(screen)
23+
while True:
24+
self.stop_ground_scroll = world.game_over
25+
self.screen.blit(self.bg_img, (0, 0))
26+
27+
for event in pygame.event.get():
28+
if event.type == pygame.QUIT:
29+
pygame.quit()
30+
sys.exit()
31+
32+
elif event.type == pygame.KEYDOWN:
33+
if not world.playing and not world.game_over:
34+
world.playing = True
35+
if event.key == pygame.K_SPACE:
36+
world.update("jump")
37+
if event.key == pygame.K_r:
38+
world.update("restart")
39+
40+
world.update()
41+
42+
self.screen.blit(self.ground_img, (self.ground_scroll, HEIGHT))
43+
if not self.stop_ground_scroll:
44+
self.ground_scroll += self.scroll_speed
45+
if abs(self.ground_scroll) > 35:
46+
self.ground_scroll = 0
47+
48+
pygame.display.update()
49+
self.FPS.tick(60)
50+
51+
if __name__ == "__main__":
52+
play = Main(screen)
53+
play.main()

Diff for: gui-programming/flappy-bird-game/pipe.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import pygame
2+
3+
class Pipe(pygame.sprite.Sprite):
4+
def __init__(self, pos, width, height, flip):
5+
super().__init__()
6+
self.width = width
7+
img_path = 'assets/terrain/pipe.png'
8+
self.image = pygame.image.load(img_path)
9+
self.image = pygame.transform.scale(self.image, (width, height))
10+
if flip:
11+
flipped_image = pygame.transform.flip(self.image, False, True)
12+
self.image = flipped_image
13+
self.rect = self.image.get_rect(topleft = pos)
14+
15+
# update object position due to world scroll
16+
def update(self, x_shift):
17+
self.rect.x += x_shift
18+
19+
# removes the pipe in the game screen once it is not shown in the screen anymore
20+
if self.rect.right < (-self.width):
21+
self.kill()

Diff for: gui-programming/flappy-bird-game/requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pygame

Diff for: gui-programming/flappy-bird-game/settings.py

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from os import walk
2+
import pygame
3+
4+
WIDTH, HEIGHT = 600, 650
5+
6+
pipe_pair_sizes = [
7+
(1, 7),
8+
(2, 6),
9+
(3, 5),
10+
(4, 4),
11+
(5, 3),
12+
(6, 2),
13+
(7, 1)
14+
]
15+
pipe_size = HEIGHT // 10
16+
pipe_gap = (pipe_size * 2) + (pipe_size // 2)
17+
ground_space = 50
18+
19+
def import_sprite(path):
20+
surface_list = []
21+
for _, __, img_file in walk(path):
22+
for image in img_file:
23+
full_path = f"{path}/{image}"
24+
img_surface = pygame.image.load(full_path).convert_alpha()
25+
surface_list.append(img_surface)
26+
return surface_list

Diff for: gui-programming/flappy-bird-game/world.py

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import pygame
2+
from pipe import Pipe
3+
from bird import Bird
4+
from game import GameIndicator
5+
from settings import WIDTH, HEIGHT, pipe_size, pipe_gap, pipe_pair_sizes
6+
import random
7+
8+
class World:
9+
def __init__(self, screen):
10+
self.screen = screen
11+
self.world_shift = 0
12+
self.current_x = 0
13+
self.gravity = 0.5
14+
self.current_pipe = None
15+
self.pipes = pygame.sprite.Group()
16+
self.player = pygame.sprite.GroupSingle()
17+
self._generate_world()
18+
self.playing = False
19+
self.game_over = False
20+
self.passed = True
21+
self.game = GameIndicator(screen)
22+
23+
# creates the player and the obstacle
24+
def _generate_world(self):
25+
self._add_pipe()
26+
bird = Bird((WIDTH//2 - pipe_size, HEIGHT//2 - pipe_size), 30)
27+
self.player.add(bird)
28+
29+
# adds pipe once the last pipe added reached the desired pipe horizontal spaces
30+
def _add_pipe(self):
31+
pipe_pair_size = random.choice(pipe_pair_sizes)
32+
top_pipe_height, bottom_pipe_height = pipe_pair_size[0] * pipe_size, pipe_pair_size[1] * pipe_size
33+
34+
pipe_top = Pipe((WIDTH, 0 - (bottom_pipe_height + pipe_gap)), pipe_size, HEIGHT, True)
35+
pipe_bottom = Pipe((WIDTH, top_pipe_height + pipe_gap), pipe_size, HEIGHT, False)
36+
self.pipes.add(pipe_top)
37+
self.pipes.add(pipe_bottom)
38+
self.current_pipe = pipe_top
39+
40+
# for moving background/obstacle
41+
def _scroll_x(self):
42+
if self.playing:
43+
self.world_shift = -6
44+
else:
45+
self.world_shift = 0
46+
47+
# add gravity to bird for falling
48+
def _apply_gravity(self, player):
49+
if self.playing or self.game_over:
50+
player.direction.y += self.gravity
51+
player.rect.y += player.direction.y
52+
53+
# handles scoring and collision
54+
def _handle_collisions(self):
55+
bird = self.player.sprite
56+
# for collision checking
57+
if pygame.sprite.groupcollide(self.player, self.pipes, False, False) or bird.rect.bottom >= HEIGHT or bird.rect.top <= 0:
58+
self.playing = False
59+
self.game_over = True
60+
else:
61+
# if player pass through the pipe gaps
62+
bird = self.player.sprite
63+
if bird.rect.x >= self.current_pipe.rect.centerx:
64+
bird.score += 1
65+
self.passed = True
66+
67+
# updates the bird's overall state
68+
def update(self, player_event = None):
69+
# new pipe adder
70+
if self.current_pipe.rect.centerx <= (WIDTH // 2) - pipe_size:
71+
self._add_pipe()
72+
73+
# updates, draws pipes
74+
self.pipes.update(self.world_shift)
75+
self.pipes.draw(self.screen)
76+
77+
# applying game physics
78+
self._apply_gravity(self.player.sprite)
79+
self._scroll_x()
80+
self._handle_collisions()
81+
82+
# configuring player actions
83+
if player_event == "jump" and not self.game_over:
84+
player_event = True
85+
elif player_event == "restart":
86+
self.game_over = False
87+
self.pipes.empty()
88+
self.player.empty()
89+
self.player.score = 0
90+
self._generate_world()
91+
else:
92+
player_event = False
93+
94+
if not self.playing:
95+
self.game.instructions()
96+
97+
# updates, draws pipes
98+
self.player.update(player_event)
99+
self.player.draw(self.screen)
100+
101+
self.game.show_score(self.player.sprite.score)

0 commit comments

Comments
 (0)