0

To create an abstract, scrolling city skyline for a prototype, I created a class that generates random rectangles. These rects are added to a list and items are pulled from that list to be drawn on the screen. The rects begin off screen to the right and scroll across to the left until they leave the view plane and are trashed. The movement of the buildings is oddly jerky and they also shift to the right a few pixels at a specific point on the screen.

This video of the prototype is fairly accurate with very little video capture lag. Pay attention to the gaps between the buildings, as they get within the right most 3rd of the display area, the gap will suddenly shrink as if the building to the left of the gap is suddenly shifting right a few pixels. The smaller the gap, the more noticeable it is. The other anomalies in the video are from the recorder and are not present in the app. Here's a very short video that clearly shows this phenomenon: https://www.youtube.com/watch?v=0cdhrezjcY8

At about 1 second in you'll notice a very narrow gap between buildings in the rear layer. At :04 seconds in the gap is even with the blue player object, the left rectangle shifts and the gap vanishes. There's a second, larger gap to the right of that one that does the same thing but since the gap is larger it doesn't completely vanish. I've looked over the code numerous times but can't see what could be causing this anomaly. I'm hoping someone can tell me if it's something I did or a limitation I'm encountering.

I originally wrote this program linearly, without classes or functions. I rewrote it using a class that generates layer objects and handles all the generating and scrolling. In both cases the problem exists. It's driving me crazy trying to figure out why the buildings do not move smoothly. I've even written a version of this using png images instead of randomly generated rectangles. In that version the pngs scroll smoothly and seamlessly: https://www.youtube.com/watch?v=Uiw_giAvbOo (The video is a bit jerky, but the actual program plays smooth) So the issue is limited to these random rectangles.

Here's the code for the program: https://www.refheap.com/73079

Here's the class code by itself:

class Scroller():
    def __init__(self, speed, color, heightMax):
        # Speed of the layer scroll, the color of the layer and the maximum height for buildings
        # set up the building parameters
        self.buildingHeightMax = heightMax
        self.buildingHeightMin = 100
        self.buildingWidthMax = 125
        self.buildingWidthMin = 75
        self.buildings = []
        self.layerspeed = speed
        self.buildTime = True
        self.buildCountdown = 10
        self.color = color

    def update(self):
        # Check if it's time to build. If not, decrement counter
        if self.buildTime == False:
            self.buildCountdown -= 1
            # If time is 0, time to build, reset counter to a new random time
            if self.buildCountdown <= 0:
                self.buildTime = True
                self.buildCountdown = random.randint(3, self.layerspeed)

        # create building if it's time
        if self.buildTime:
            # generate random width and height of building
            buildingHeight = random.randint(self.buildingHeightMin, self.buildingHeightMax)
            buildingWidth = random.randint(self.buildingWidthMin, self.buildingWidthMax)
            buildingTop = WINDOWHEIGHT - buildingHeight
            # This generates the building object from the above parameters
            building = pygame.Rect(WINDOWWIDTH, buildingTop, buildingWidth, WINDOWHEIGHT)
            self.buildTime = False
            self.buildCountdown = random.randint(3, self.layerspeed * 5)
            # add building to buildings list
            self.buildings.append(building)

        # move all buildings on layer at set speed
        for building in self.buildings:
            # if the building is off the screen, trash it. If not, move it to the
            # right at the objects speed.
            if building.right < 0:
                self.buildings.remove(building)
            else:
                building.left -= self.layerspeed

        # draw the Front buildings
        for i in range(len(self.buildings)):
            pygame.draw.rect(windowSurface, self.color, self.buildings[i])
Vin Breau
  • 269
  • 3
  • 17

1 Answers1

1

Your problem most likely lies in:

    # move all buildings on layer at set speed
    for building in self.buildings:
        # if the building is off the screen, trash it. If not, move it to the
        # right at the objects speed.
        if building.right < 0:
            self.buildings.remove(building)
        else:
            building.left -= self.layerspeed

You're using remove on the same list you're iterating from, and this will make it skip the next building. So it's not the building to the right that's moving faster, it's the one to the left that has skipped moving.

You can see it yourself with this simple example:

a = [2, 3, 4, 1.5, 6, 8, 3.2]

for element in a:
    if element == 4:
        a.remove(element)
    else:
        print element

Try it and you'll see that not only 4 won't be printed, but also 1.5 will be skipped.

Possibly a good way to do it is to first iterate through all the buildings to see which ones need to be removed, then remove then all, and finally move all the ones that are left.
You might want to check this link for some good suggestions.

You're also updating the countdown twice, first on line 47 and then on line 58. Is there any reason for this?

Community
  • 1
  • 1
Roberto
  • 2,696
  • 18
  • 31
  • 1
    That's fantastic! I did not know this about list manipulation. You are correct, I moved `building.left -= self.layerspeed` out of the loop that removes dead buildings and put it in a successive separate loop and now everything runs smoothly. Thank you for that bit of education. – Vin Breau Apr 07 '14 at 20:47
  • You asked why I was setting the counter twice, that's a mistake. Thanks. It doesn't really affect anything except generate a new random number, but it's certainly not necessary. – Vin Breau Apr 07 '14 at 23:56