When the game starts, a pipe should generate every few seconds and then delete itself while it is off the screen. Except... the pipes does not appear on the screen at all. I tried checking how many pipes there were present and the number fluctuated between 2 and 3 meaning that pipes were being generated and destroyed???? I would also appreciate any advice on how to make my code more efficient/readable.
This is my current code which doesn't work.
from tkinter import \*
from PIL import Image, ImageTk
import tkinter as tk
import random
class Bird:
BIRD_WIDTH = 75
BIRD_HEIGHT = 50
BIRD_GRAVITY = 0.2
def __init__(self, canvas, x, y):
self.canvas = canvas
self.image = Image.open("bird.png")
self.image = self.image.resize((self.BIRD_WIDTH, self.BIRD_HEIGHT),
Image.ANTIALIAS)
self.photo = ImageTk.PhotoImage(self.image)
self.bird_id = self.canvas.create_image(x,
y,
image=self.photo,
anchor="nw")
self.velocity = 0
def move(self):
bird_x, bird_y = self.canvas.coords(self.bird_id)
self.velocity += self.BIRD_GRAVITY # add gravity to velocity
bird_y += self.velocity # add velocity to y-coordinate
self.canvas.coords(self.bird_id, bird_x, bird_y)
class Pipe:
PIPE_WIDTH = 600
PIPE_HEIGHT = 1000
PIPE_SPEED = 5
def __init__(self, canvas, x, y, photo):
self.canvas = canvas
self.pipe_id = self.canvas.create_image(x,
y,
image=photo,
anchor="nw")
def move(self):
self.canvas.move(self.pipe_id, -self.PIPE_SPEED, 0)
@classmethod
def create_pipe(cls, canvas):
image = Image.open("pipes.png")
image = image.resize((cls.PIPE_WIDTH, cls.PIPE_HEIGHT), Image.ANTIALIAS)
photo = ImageTk.PhotoImage(image)
return cls(canvas, 1200, random.randint(-300, -100), photo)
class Gui:
CANVAS_WIDTH = 1200
CANVAS_HEIGHT = 5000
WINDOW_WIDTH = 1440
WINDOW_HEIGHT = 500
JUMP_HEIGHT = 5.5
PIPE_INTERVAL = 100
PIPE_COUNTER = 0
SCORE = 0
def __init__(self, start):
self.start = start
# Set up the main window
self.root = tk.Tk()
self.root.title("Flappy Bird")
self.root.geometry(f"{self.WINDOW_WIDTH}x{self.WINDOW_HEIGHT}")
# Set up the game canvas
self.canvas = tk.Canvas(self.root,
width=self.CANVAS_WIDTH,
height=self.CANVAS_HEIGHT)
self.canvas.pack()
# Create the bird and pipes
self.bird = Bird(self.canvas, 100, 200)
self.pipes = []
self.pipes.append(Pipe.create_pipe(self.canvas))
self.bind_keys()
# Schedule the game loop
self.game_loop()
self.start_game()
def bind_keys(self):
self.root.bind("<space>", self.flap_up)
def flap_up(self, event):
self.bird.velocity = -self.JUMP_HEIGHT
def check_collision(self):
pass
def game_over(self):
pass
def start_game(self):
self.root.mainloop()
def game_loop(self):
# Update the position of the bird
self.bird.move()
# Move the pipe to the left
for pipe in self.pipes:
pipe.move()
# Delete pipes that are off the screen
for pipe in self.pipes:
pipe_x = self.canvas.coords(pipe.pipe_id)[0]
if pipe_x < -pipe.PIPE_WIDTH:
self.canvas.delete(pipe.pipe_id)
self.pipes.remove(pipe)
self.SCORE += 1
self.PIPE_COUNTER += 1
if self.PIPE_COUNTER >= self.PIPE_INTERVAL:
self.PIPE_COUNTER = 0
new_pipe = Pipe.create_pipe(self.canvas)
self.pipes.append(new_pipe)
print(len(self.pipes))
# Check for collision
if self.check_collision():
self.game_over()
else:
# Schedule the next iteration of the game loop
self.canvas.after(10, self.game_loop)
gui = Gui(None)
gui.start_game()
Here is a previous iteration of my code that succeeds in generating the pipes. (but of course I need the pipes to generate every few seconds) I haven't found any notable difference that makes this code work but the latest version to not work. I also asked chatgpt but it also couldn't detect any notable difference.
from tkinter import \*
from PIL import Image, ImageTk
import tkinter as tk
import random
class Bird:
BIRD_WIDTH = 75
BIRD_HEIGHT = 50
BIRD_GRAVITY = 0.1
def __init__(self, canvas, x, y):
self.canvas = canvas
self.image = Image.open("bird.png")
self.image = self.image.resize((self.BIRD_WIDTH, self.BIRD_HEIGHT),
Image.ANTIALIAS)
self.photo = ImageTk.PhotoImage(self.image)
self.bird_id = self.canvas.create_image(x,
y,
image=self.photo,
anchor="nw")
self.velocity = 0
def move(self):
bird_x, bird_y = self.canvas.coords(self.bird_id)
self.velocity += self.BIRD_GRAVITY # add gravity to velocity
bird_y += self.velocity # add velocity to y-coordinate
self.canvas.coords(self.bird_id, bird_x, bird_y)
class Pipe:
PIPE_WIDTH = 600
PIPE_HEIGHT = 1000
def __init__(self, canvas, x, y):
self.canvas = canvas
self.image = Image.open("pipes.png")
self.image = self.image.resize((self.PIPE_WIDTH, self.PIPE_HEIGHT),
Image.ANTIALIAS)
self.photo = ImageTk.PhotoImage(self.image)
self.pipe_id = self.canvas.create_image(x,
y,
image=self.photo,
anchor="nw")
def move(self, dx, dy):
self.canvas.move(self.pipe_id, dx, dy)
class Gui:
CANVAS_WIDTH = 1200
CANVAS_HEIGHT = 5000
WINDOW_WIDTH = 1440
WINDOW_HEIGHT = 500
PIPE_SPEED = 4
JUMP_HEIGHT = 4
PIPE_INTERVAL = 300000
def __init__(self, start):
self.start = start
# Set up the main window
self.root = tk.Tk()
self.root.title("Flappy Bird")
self.root.geometry(f"{self.WINDOW_WIDTH}x{self.WINDOW_HEIGHT}")
# Set up the game canvas
self.canvas = tk.Canvas(self.root,
width=self.CANVAS_WIDTH,
height=self.CANVAS_HEIGHT)
self.canvas.pack()
# Create the bird and pipes
self.bird = Bird(self.canvas, 100, 200)
self.pipe = Pipe(self.canvas, 1200, random.randint(-300, -100))
self.bind_keys()
# Schedule the game loop
self.canvas.after(10, self.game_loop)
def bind_keys(self):
self.root.bind("<space>", self.flap_up)
def flap_up(self, event):
self.bird.velocity = -self.JUMP_HEIGHT
def generate_pipe(self):
self.pipe = Pipe(self.canvas, 1200, random.randint(-300, -100))
def game_loop(self):
# Update the position of the bird
self.bird.move()
# Move the pipe to the left
self.pipe.move(-self.PIPE_SPEED, 0)
# Delete pipe that is off the screen
pipe_x = self.canvas.coords(self.pipe.pipe_id)[0]
if pipe_x < -self.pipe.PIPE_WIDTH:
self.canvas.delete(self.pipe.pipe_id)
# Check for collision
if self.check_collision():
self.game_over()
else:
# Schedule the next iteration of the game loop
self.canvas.after(5, self.game_loop)
def check_collision(self):
pass
def start_game(self):
self.root.mainloop()
gui = Gui(None)
gui.start_game()
jsonharper provided a link that explains exactly what is happening. The one thing you changed in your "new" version is the way you create the pipes.
I was playing a bit with your code and remebered something about using images in funtions the way you do.
You'll have to fix some of your code, but this Pipe variant should work for you.
To create a pipe you instantiate it and then call the get_pipe() to add it to the list of pipes
instantiating a pipe
reworked pipe class