1

Below is the basic code for creating and managing the pipes of the game:

import pygame as pg
import sys,os,math,time,random

# colours
white = (255,255,255)
red = (255,0,0)
green = (0,255,0)

# general stuff
WIDTH = 1024
HEIGHT = 576
FPS = 60

# other
all_events = [pg.QUIT, pg.ACTIVEEVENT, pg.KEYDOWN, pg.KEYUP, pg.MOUSEMOTION,
              pg.MOUSEBUTTONUP, pg.MOUSEBUTTONDOWN, pg.VIDEORESIZE,
              pg.VIDEOEXPOSE, pg.USEREVENT]

pg.init()
screen = pg.display.set_mode((WIDTH, HEIGHT))
clock = pg.time.Clock()

# Class to manage Pipes
class Pipe_Manager:

    def __init__(self):
        self.pipe_width = 50
        self.pipes = []
        self.pipe_speed = 5
        self.max_tick = 75
        self.spawn_tick = self.max_tick

    def manage_pipes(self):
        self.spawner()
        self.manage()
        self.display()

    def make_pipe(self):

        height = random.randint(100,326)
        gap = random.randint(100,250)
        surf1 = pg.Surface((self.pipe_width, height))
        surf1.fill(green)
        surf2 = pg.Surface((self.pipe_width, HEIGHT - (height + gap)))
        surf2.fill(green)

            # surface, (x,y) and vertical height
        pipe = [surf1, [WIDTH, 0], height]
        pipe2 = [surf2, [WIDTH, height + gap], HEIGHT - (height + gap)]
        self.pipes.append(pipe)
        self.pipes.append(pipe2)

    def spawner(self):  

        if self.spawn_tick == self.max_tick:
            self.make_pipe()
            self.spawn_tick = 0

        self.spawn_tick += 1

    def manage(self):

        for pipe in self.pipes: 

            # move the pipe
            pipe[1][0] -= self.pipe_speed 

            # check if it's off screen           
            if pipe[1][0] + self.pipe_width < 0:
                self.pipes.remove(pipe)

    def display(self):
        for pipe in self.pipes:
            screen.blit(pipe[0], (pipe[1][0], pipe[1][1]))

################################################################################

pg.event.set_blocked(all_events)
pg.event.set_allowed([pg.QUIT, pg.KEYDOWN])

pipe_manager = Pipe_Manager()
loop = True
while loop:
    screen.fill(white)
    pipe_manager.manage_pipes()
    pg.display.update()
    clock.tick(FPS)

The pipes seem to shake as they move horizontally and sometimes the top pipe becomes misaligned from the bottom one.

I hope this isn't a problem specific to my computer because I have abstracted away a significant amount of my flappy-bird-clone code and the source of this pipe lag problem must lie somewhere in here.

1 Answers1

0

The problem is this piece of code:

    for pipe in self.pipes: 

        # move the pipe
        pipe[1][0] -= self.pipe_speed 

        # check if it's off screen           
        if pipe[1][0] + self.pipe_width < 0:
            self.pipes.remove(pipe)

Here you change the list you're currently iterating over. Once a pipe gets removed from the list, the next one misses one movement step.

Take a look at the following example in which you can spot the problem yourself (see how 5 is missing in the output because we removed 4):

>>> l = [1,2,3,4,5,6,7,8,9,10]
>>> for x in l:
...   if x == 4:
...     l.remove(x)
...   print(x)
...
1
2
3
4
6
7
8
9
10
>>>

(There's a reason other languages forbid changing the sequence you're currently iterating).

A simple fix is to make a copy of the list first:

    for pipe in self.pipes[:]: 

For smoother movement, try increasing your framerate and use timestepping. Here's a possible way:

import pygame as pg
import sys,os,math,time,random

# colours
white = (255,255,255)
red = (255,0,0)
green = (0,255,0)

# general stuff
WIDTH = 1024
HEIGHT = 576
FPS = 120

# other
all_events = [pg.QUIT, pg.ACTIVEEVENT, pg.KEYDOWN, pg.KEYUP, pg.MOUSEMOTION,
              pg.MOUSEBUTTONUP, pg.MOUSEBUTTONDOWN, pg.VIDEORESIZE,
              pg.VIDEOEXPOSE, pg.USEREVENT]

pg.init()
screen = pg.display.set_mode((WIDTH, HEIGHT))
clock = pg.time.Clock()


class Pipe:
    def __init__(self, img, pos):
        self.img = img
        self.pos = pos

# Class to manage Pipes
class Pipe_Manager:

    def __init__(self):
        self.pipe_width = 50
        self.pipes = []
        self.pipe_speed = 0.3
        self.max_tick = 1500
        self.spawn_tick = self.max_tick

    def manage_pipes(self, dt):
        self.spawner(dt)
        self.manage(dt)
        self.display()

    def make_pipe(self):

        height = random.randint(100,326)
        gap = random.randint(100,250)
        surf1 = pg.Surface((self.pipe_width, height))
        surf1.fill(green)
        surf2 = pg.Surface((self.pipe_width, HEIGHT - (height + gap)))
        surf2.fill(green)

        pipe = Pipe(surf1, pg.Vector2(WIDTH, 0))
        pipe2 = Pipe(surf2, pg.Vector2(WIDTH, height + gap))
        self.pipes.append(pipe)
        self.pipes.append(pipe2)

    def spawner(self, dt):

        if self.spawn_tick >= self.max_tick:
            self.make_pipe()
            self.spawn_tick = 0

        self.spawn_tick += dt

    def manage(self, dt):

        for pipe in self.pipes[:]: 

            # move the pipe
            pipe.pos.x -= self.pipe_speed * dt

            # check if it's off screen           
            if pipe.pos.x + self.pipe_width < 0:
                self.pipes.remove(pipe)

    def display(self):
        for pipe in self.pipes:
            screen.blit(pipe.img, pipe.pos)

################################################################################

pg.event.set_blocked(all_events)
pg.event.set_allowed([pg.QUIT, pg.KEYDOWN])

pipe_manager = Pipe_Manager()
loop = True
dt=0
while loop:
    for e in pg.event.get():
        if e.type == pg.QUIT:
            loop = False
    screen.fill(white)
    pipe_manager.manage_pipes(dt)
    pg.display.update()
    dt=clock.tick(FPS)
sloth
  • 99,095
  • 21
  • 171
  • 219
  • Thank you sir this has fixed the 'top pipe becoming misaligned from the bottom one' problem. I can see exactly what you are talking about thanks for the example. There still seems to be some pulsating lag running through the game however this could be specific to my computer i'm not sure... – xxsamperrinxx Dec 21 '18 at 08:53
  • thanks so much your edit made the motion far better, just one question. Where can I go to gain a full understanding of this method/technique 'timestepping'. I would like to use it in other places in my project. – xxsamperrinxx Dec 21 '18 at 11:19
  • @xxsamperrinxx take a loop [here](https://gamedev.stackexchange.com/questions/48227/smooth-movement-pygame) for example – sloth Dec 21 '18 at 12:21