2
\$\begingroup\$

Recently, I have been trying to improve my programming and coding skills in python so I have decided to make some games with the pygame module.

I have made this flappy bird clone with pygame and would love to get review of the code, what should I change/improve, any tips are welcome.

Code is here, in this github link : click here

import pygame
import random
import os
pygame.init()
SIZE = [400, 708]
FONT = pygame.font.SysFont('arialrounded', 50)
class Bird:
 def __init__(self):
 self.x = 50
 self.y = 350
 self.jump = 0
 self.jump_speed = 10
 self.gravity = 10
 self.dead = False
 self.sprite = 0
 self.bird_sprites = [pygame.image.load("images/1.png").convert_alpha(),
 pygame.image.load("images/2.png").convert_alpha(),
 pygame.image.load("images/dead.png").convert_alpha()]
 # self.img_rect =
 def move(self):
 if self.dead: # dead bird
 self.sprite = 2 # change to dead.png
 # keeps falling until it hits the ground
 if self.y < SIZE[1] - 30:
 self.y += self.gravity
 elif self.y > 0:
 # handling movement while jumping
 if self.jump:
 self.sprite = 1 # change to 2.png
 self.jump_speed -= 1
 self.y -= self.jump_speed
 else:
 # regular falling (increased gravity)
 self.gravity += 0.2
 self.y += self.gravity
 else:
 # in-case where the bird reaches the top
 # of the screen
 self.jump = 0
 self.y += 3
 def bottom_check(self):
 # bird hits the bottom = DEAD
 if self.y >= SIZE[1] - 30:
 self.dead = True
 def get_rect(self):
 # updated bird image rectangle
 img_rect = self.bird_sprites[self.sprite].get_rect()
 img_rect[0] = self.x
 img_rect[1] = self.y
 return img_rect
class Pillar:
 def __init__(self, pos):
 # pos == True is top , pos == False is bottom
 self.pos = pos
 self.img = self.get_image()
 def get_rect(self):
 # returns the pillar image rect
 return self.img.get_rect()
 def get_image(self):
 if self.pos: # image for the top pillar
 return pygame.image.load("images/top.png").convert_alpha()
 else: # image for the bottom pillar
 return pygame.image.load("images/bottom.png").convert_alpha()
class Options:
 def __init__(self):
 self.score_img = pygame.image.load("images/score.png").convert_alpha() # score board image
 self.play_img = pygame.image.load("images/play.png").convert_alpha() # play button image
 self.play_rect = self.play_img.get_rect()
 self.score_rect = self.score_img.get_rect()
 self.align_position()
 self.score = 0
 self.font = FONT
 def align_position(self):
 # aligns the "menu" in certain positions
 self.play_rect.center = (200, 330)
 self.score_rect.center = (200, 220)
 def inc(self):
 # score increased by 1
 self.score += 1
class Game:
 def __init__(self):
 self.screen = pygame.display.set_mode((SIZE[0], SIZE[1]))
 pygame.display.set_caption("Flappy Bird")
 self.background = pygame.image.load("images/background.png").convert() # background image
 self.pillar_x = 400
 self.offset = 0
 self.top_p = Pillar(1) # top pillar
 self.bot_p = Pillar(0) # bottom pillar
 self.pillar_gap = 135 # gap between pillars, (can be randomised as well)
 self.bird = Bird() # bird object
 self.score_board = Options()
 self.passed = False # allows to keep track of the score
 def pillar_move(self):
 # handling pillar movement in the background
 if self.pillar_x < -100:
 self.offset = random.randrange(-120, 120)
 self.passed = False
 self.pillar_x = 400
 self.pillar_x -= 5
 def run(self):
 clock = pygame.time.Clock()
 done = True
 while done:
 clock.tick(60)
 for event in pygame.event.get():
 if event.type == pygame.QUIT:
 pygame.quit()
 quit()
 if event.type == pygame.KEYDOWN:
 if event.key == pygame.K_SPACE:
 # bird jumps
 self.bird.jump = 17
 self.bird.gravity = 5
 self.bird.jump_speed = 10
 if event.type == pygame.MOUSEBUTTONDOWN:
 # clicking on the play button (game reset)
 if self.bird.dead and self.score_board.play_rect.collidepoint(event.pos):
 self.bird.dead = False
 self.reset()
 self.screen.blit(self.background, (0, 0))
 self.screen.blit(self.top_p.img, (self.pillar_x, 0 - self.pillar_gap - self.offset))
 self.screen.blit(self.bot_p.img, (self.pillar_x, 360 + self.pillar_gap - self.offset))
 self.screen.blit(self.bird.bird_sprites[self.bird.sprite], (self.bird.x, self.bird.y))
 self.pillar_move()
 self.bird.move()
 self.bird.bottom_check()
 if not self.bird.dead:
 self.collision()
 self.show_score()
 else:
 self.game_over()
 pygame.display.flip()
 def get_pillar_rect(self, pillar):
 # returns current pillar rectangle on display
 rect = pillar.get_image().get_rect()
 rect[0] = self.pillar_x
 if pillar.pos:
 # current rect y position for top pillar
 rect[1] = 0 - self.pillar_gap - self.offset
 else:
 # current rect y position for bottom pillar
 rect[1] = 360 + self.pillar_gap - self.offset
 return rect
 def collision(self):
 top_rect = self.get_pillar_rect(self.top_p)
 bot_rect = self.get_pillar_rect(self.bot_p)
 # collision check bird <> pillars
 if top_rect.colliderect(self.bird.get_rect()) or bot_rect.colliderect(self.bird.get_rect()):
 # print(self.bird.bird_sprites[self.bird.sprite].get_rect())
 self.bird.dead = True
 # if bird passed the pillars
 elif not self.passed and top_rect.right < self.bird.x:
 self.score_board.inc()
 self.passed = True
 def reset(self):
 # game values reset
 self.score_board.score = 0
 self.bird = Bird()
 self.top_p = Pillar(1)
 self.bot_p = Pillar(0)
 self.pillar_x = 400
 self.bird.gravity = 10
 def show_score(self):
 # score font
 score_font = FONT.render("{}".format(self.score_board.score),
 True, (255, 80, 80))
 # score font rectangle
 font_rect = score_font.get_rect()
 font_rect.center = (200, 50)
 self.screen.blit(score_font, font_rect) # show score board font
 def game_over(self):
 # score font
 score_font = FONT.render("{}".format(self.score_board.score),
 True, (255, 80, 80))
 # score font rectangle
 font_rect = score_font.get_rect()
 score_rect = self.score_board.score_rect
 play_rect = self.score_board.play_rect # play button rectangle
 font_rect.center = (200, 230)
 self.screen.blit(self.score_board.play_img, play_rect) # show play button
 self.screen.blit(self.score_board.score_img, score_rect) # show score board image
 self.screen.blit(score_font, font_rect) # show score font
os.chdir(os.path.dirname(__file__))
if __name__ == "__main__":
 game = Game()
 game.run()
mdfst13
22.4k6 gold badges34 silver badges70 bronze badges
asked Aug 24, 2017 at 18:28
\$\endgroup\$
1
  • \$\begingroup\$ Welcome to Code Review! Unfortunately your question is off-topic as of now, as the code to be reviewed must be present in the question. Please add the code you want reviewed in your question. Thanks! \$\endgroup\$ Commented Aug 24, 2017 at 18:34

1 Answer 1

2
\$\begingroup\$

Nice, not many things to improve, so only some tips.


You use many pictures. Instead of hard-coding their names spread over your code, consider using constant variables near the top of your code, something like

import os.path
FOLDER = 'images'
PICS = dict(BACKGROUND='background.png',
 SCORE='score.png',
 PLAY='play.png'
 )
PICS = {k: os.path.join(FOLDER, v) for k, v in PICS.items()}

to make your code easy maintainable.

Note the construction of dictionary with keywords to avoid put the key names in apostrophes (BTW, apostrophes are preferred over quotes in Python) and dictionary comprehension to avoid repeated use of 'images'.


It's useful to follow the DRY principle - Don't Repeat Yourself.

You repeatedly use the long construction

pygame.image.load("images/score.png").convert_alpha()

Why don't dedicate a function to it, e. g.

def image_load(name):
 return pygame.image.load(name).convert_alpha()

and then use it - together with my previous tip - instead of commands as this one

self.score_img = pygame.image.load("images/score.png").convert_alpha() # score board image

to shorten it to

self.score_img = image_load(PICS[SCORE]) # score board image

Sometimes you use so called magic numbers, e. g.

self.pillar_x = 400 # did you mean 'self-pillar_x = SIZE[0]' ?

or

(255, 80, 80) # did you mean something which may have the name FONT_COLOR?

Instead of the superfluous construction

pygame.display.set_mode((SIZE[0], SIZE[1]))

you may simply use

pygame.display.set_mode(SIZE)

as the list of coordinates is as good as a tuple of them.

answered Aug 25, 2017 at 22:35
\$\endgroup\$
1
  • \$\begingroup\$ thanks a lot for your review and your time devoting to it! I will take these tips into account when writing my next programs. \$\endgroup\$ Commented Aug 26, 2017 at 9:30

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.