0

I'm creating a flappy bird clone in pygame, and I'm having some weird graphical bugs that I have no idea how to solve.

In order to keep track of the pipes in the game, I have a list called pipes. This list starts out empty when the game starts, and lists containing x and y coordinates are added as the current pipes move to the left of the screen:

def create_new_pipe(): # A function that adds a new pipe to the pipe list when called
    bottom_pipe_position = random.randrange(24+pipe_vertical_spacing, screenh-168)
    pipes.extend([
    [screenw+pipe_horizontal_spacing, bottom_pipe_position], 
    [screenw+pipe_horizontal_spacing, bottom_pipe_position-320-pipe_vertical_spacing]])

Every frame the game checks to see if the list is empty (aka there aren't any pipes) or none of the pipes are off the right side of the screen, and if either conditions are met, it generates a new set of randomly positioned pipes:

if not pipes or max([pipe[0] for pipe in pipes]) < screenw-52:
    # Creating a new pipe if no pipes are present or none of the pipes are off the right side of the screen
    create_new_pipe()

Then, every frame every pipe is looped through and rendered accordingly, and moved to the left to progress the game. If any of the pipes are completely off the left side of the screen, they are removed from the list and are not rendered:

for pipe in pipes:
    if pipe[0] < -52: # Removes any pipes that are completely off the left side of the screen
        pipes.remove(pipe)
    else:
        pipe[0] -= 2 # Moving the pipes 2 pixels every frame
        if pipes.index(pipe) % 2: # Rendering every odd pipe normally, and every even pipe flipped
            screen.blit(pipe_image_flipped, pipe)
        else:
            screen.blit(pipe_image, pipe)

While this technically works, whenever I run the game some weird graphical glitches occur. Every time a new set of pipes is added, all the pipes flicker and the bottom pipes shift slightly to the left:

enter image description here

Is this a mistake done on my part or is it a limitation with the engine itself?

  • 2
    try `for pipe in pipes[:]:`. [You shouldn't remove elements from a list while iterating through it](https://stackoverflow.com/questions/1207406/how-to-remove-items-from-a-list-while-iterating). Notice it flashes exactly when `if pipe[0] < -52:` returns true and the pipes are removed. –  Sep 12 '21 at 02:17
  • Are ```pipe_image_flipped``` and ```pipe_image``` strings that you're loading each time? I don't use pygame, but according to the docs it looks like it might be best to use a Surface. https://pygame-zero.readthedocs.io/en/stable/builtins.html – Chaos_Is_Harmony Sep 12 '21 at 02:19
  • 1
    @user16038533 It worked, thanks so much! I'll keep that in mind next time! – gavinskycastle Sep 12 '21 at 02:27
  • 1
    @GavinTheCrafter Happy to help. See the answer bellow, that solution is certainly faster than the solution I have mentioned since it doesn't have to make a copy of the entire list every frame. It's a little more work though. –  Sep 12 '21 at 02:32
  • @user16038533 I don't think it'll be too much of a problem, since the list is fairly short and only ever gets to a maximum length of about 8-10, but you're right in that it's something to keep in mind. – gavinskycastle Sep 12 '21 at 02:39

1 Answers1

0

I think what's happening is that when you are in a frame that updates the pipes list by removing pipes, your loop skips over rendering some pipes. So you have to spend a few frames finding the skipped pipes and updating properly, which causes a pipe rendering lag.

Here's a dumbed down example of an attempt at removing even elements.

def demo():
    myList = [0,2,3,4,7,9,10]
    print("start: ", myList)
    for i, element in enumerate(myList):
        if element % 2 == 0: # 0 or even
            myList.remove(element)
        print(i, ': ', element, myList)

demo()

The printout demonstrates that as the list deletes an element, it shifts the existing elements over to fill the spot. However, the iterating index isn't shifted back to compensate, so it skips over elements.

start:  [0, 2, 3, 4, 7, 9, 10]
0 :  0 [2, 3, 4, 7, 9, 10]
1 :  3 [2, 3, 4, 7, 9, 10]
2 :  4 [2, 3, 7, 9, 10]
3 :  9 [2, 3, 7, 9, 10]
4 :  10 [2, 3, 7, 9]

Since the problem is that the iteration index keeps going when it shouldn't, you can control these factors in a simple while loop.

def fixeddemo():
    myList = [0,2,3,4,7,9,10]
    print("start: ", myList)
    
    i = 0
    length = len(myList)
    while i < length:
        element = myList[i]
        if element % 2 == 0: # 0 or even
            myList.remove(element)
            # keep index i in same spot
            length -= 1
        else:
            i += 1
            # length has not changed
        print(i, ': ', element, myList)

fixeddemo()

No elements are skipped and all the even elements are removed in one go:

start:  [0, 2, 3, 4, 7, 9, 10]
0 :  0 [2, 3, 4, 7, 9, 10]
0 :  2 [3, 4, 7, 9, 10]
1 :  3 [3, 4, 7, 9, 10]
1 :  4 [3, 7, 9, 10]
2 :  7 [3, 7, 9, 10]
3 :  9 [3, 7, 9, 10]
3 :  10 [3, 7, 9]
BatWannaBe
  • 4,330
  • 1
  • 14
  • 23