0

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()
Barmar
  • 741,623
  • 53
  • 500
  • 612

1 Answers1

0

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

pipe = Pipe()
pipes.append(pipe.get_pipe())

reworked pipe class

class Pipe:
    PIPE_WIDTH = 600

    PIPE_HEIGHT = 1000
    PIPE_SPEED = 10

    def __init__(self, canvas):
        print('creating new pipe')
        self.canvas: tk.Canvas
        self.canvas = canvas
        self.x = 1200
        self.y = random.randint(100, 300)
        image = Image.open("pipes.png")
        image = image.resize((self.PIPE_WIDTH, self.PIPE_HEIGHT), Image.ANTIALIAS)
        self.photo = ImageTk.PhotoImage(image)
        self.pipe_id = self.canvas.create_image(self.x, self.y, image=self.photo, anchor="nw")

    def get_pipe(self):
        return self

    def move(self):
        self.canvas.move(self.pipe_id, -self.PIPE_SPEED, 0)
Ovski
  • 575
  • 3
  • 14