Browse Source

Add a working flappy bird with no textures and ~infinite levels~

master
Jens Pitkänen 1 year ago
commit
73ceb588ec
3 changed files with 179 additions and 0 deletions
  1. +1
    -0
      .gitignore
  2. +84
    -0
      flappy.py
  3. +94
    -0
      main.py

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
/__pycache__/

+ 84
- 0
flappy.py View File

@@ -0,0 +1,84 @@
import random


class Flappy:
# Static values, in the class so they're contained
gravity = 250
max_falling_speed = 200
scrolling_speed = 100
max_y = 480
jump_cooldown = 0.3
jump_force = 250
player_width = 32
player_height = 32
pipe_interval = 250
pipe_width = 50
pipe_gap_height = 150

# Player state
score = 0
player_x = 0
player_y = 240
player_falling_velocity = 0
current_jump_cooldown = 0
game_over = False

# Pipe state
pipe_x = 400
pipe_y = 240
pipe_scored = False

# Returns true if the game has not ended, false on game-over
def step(self, delta_time, jump):
# Return false right away if game has ended to avoid nasty post-death-continuation bugs
if self.game_over:
return False

# Jumping
if self.current_jump_cooldown > 0:
self.current_jump_cooldown -= delta_time
if jump and self.current_jump_cooldown <= 0:
self.player_falling_velocity = -self.jump_force
self.current_jump_cooldown = self.jump_cooldown
else:
self.player_falling_velocity = min(self.max_falling_speed, self.player_falling_velocity + self.gravity * delta_time)

# Moving the flapper
self.player_y += self.player_falling_velocity * delta_time
self.player_x += self.scrolling_speed * delta_time

# Checking for losing
if (self.player_x + self.player_width / 2 > self.pipe_x - self.pipe_width / 2 and \
(self.player_y - self.player_height / 2 < self.pipe_y - self.pipe_gap_height / 2 or \
self.player_y + self.player_height / 2 > self.pipe_y + self.pipe_gap_height / 2)) or \
self.player_y - self.player_height / 2 > self.max_y:
self.game_over = True
return False

# Scoring
if self.player_x - self.player_width / 2 > self.pipe_x + self.pipe_width / 2:
self.pipe_scored = False
self.pipe_x += self.pipe_interval
self.pipe_y = random.randrange(self.max_y / 2 - self.max_y / 4, self.max_y / 2 + self.max_y / 4)
elif self.player_x > self.pipe_x and not self.pipe_scored:
self.pipe_scored = True
self.score += 1

return True

# For terminal videogaming fun. Do not recommend, it is not actually fun.
def render_to_ascii(self):
size = 48
result = ""
for y in range(int(self.max_y / size)):
for x in range(20):
if x == 0 and y == int(self.player_y / size):
result += "@"
elif x == int((self.pipe_x - self.player_x) / size) and \
(y < int((self.pipe_y - self.pipe_gap_height / 2) / size) or \
y > int((self.pipe_y + self.pipe_gap_height / 2) / size)):
result += "#"
else:
result += "."
result += "\n"
return result

+ 94
- 0
main.py View File

@@ -0,0 +1,94 @@
from flappy import Flappy
import argparse

def main():
parser = argparse.ArgumentParser(description="Play flappy bird.")
parser.add_argument("--terminal", dest="terminal", action="store_true", help="run the terminal version, default is pygame")
parser.set_defaults(terminal=False)
args = parser.parse_args()
flappy = Flappy()
if args.terminal:
terminal_runtime(flappy)
else:
try:
pygame_runtime(flappy)
except ImportError:
print("Sorry, you don't have pygame, here's the terminal version.")
terminal_runtime(flappy)


# For terminal videogaming fun. Do not recommend, it is not actually fun.
def terminal_runtime(flappy):
print(flappy.render_to_ascii())
while True:
c = input("Type J to jump, Q to exit:")
should_jump = c == "j" or c == "J"
should_exit = c == "q" or c == "Q"
if should_exit or not flappy.step(0.2, should_jump):
break
print(flappy.render_to_ascii())

print("Game over :(")


# Here's the good graphicsy pygame version of the above
def pygame_runtime(flappy):
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption("Flappy Rect - Freeware Edition")

def make_surface(size, color):
surface = pygame.Surface(size)
surface.fill(color)
return surface.convert()
def write_text(text, font):
return font.render(text, True, (0, 0, 0))

background_surface = make_surface((640, 480), (100, 200, 240))
player_surface = make_surface((flappy.player_width, flappy.player_height), (255, 255, 100))
pipe_surface = make_surface((flappy.pipe_width, 480), (50, 255, 50))
normal_font = pygame.font.SysFont("Comic Sans MS", 20)
big_font = pygame.font.SysFont("Comic Sans MS", 32)
info_surface = write_text("Space to jump, R to restart", normal_font)
gameover_surface = write_text("You lost :( Press R to restart the game.", big_font)

camera_pos = (0, 0)

clock = pygame.time.Clock()
running = True
game_over = False
while running:
delta_time = clock.tick(120)

# Events
jump = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
jump = True
if event.key == pygame.K_r:
game_over = False
flappy = Flappy()

# Logic
game_over = not flappy.step(delta_time / 1000, jump)
camera_pos = (flappy.player_x - 100, 0)

# Rendering
screen.blit(background_surface, (0, 0))
if not game_over:
screen.blit(player_surface, (flappy.player_x - flappy.player_width / 2 - camera_pos[0], flappy.player_y - flappy.player_height / 2 - camera_pos[1]))
screen.blit(pipe_surface, (flappy.pipe_x - flappy.pipe_width / 2 - camera_pos[0], flappy.pipe_y - flappy.pipe_gap_height / 2 - 480 - camera_pos[1]))
screen.blit(pipe_surface, (flappy.pipe_x - flappy.pipe_width / 2 - camera_pos[0], flappy.pipe_y + flappy.pipe_gap_height / 2 - camera_pos[1]))
screen.blit(info_surface, (20, 20))
else:
screen.blit(gameover_surface, ((640 - gameover_surface.get_width()) / 2, (480 - gameover_surface.get_height()) / 2))
pygame.display.flip()
pygame.quit()


if __name__ == "__main__":
main()

Loading…
Cancel
Save