Why does pygame struggle with drawing 80 images at once?

Viewed 72

I started programing a "2D Minecraft" with pygame just for fun.

However right now, even drawing about 70 images on the screen, causes the FPS count drop to as low as 20.

The image loading and scaling is hapenning at the beginning of the problem. While the program runs, only the players physics (which are not the problem) and the drawing of the blocks are executed.

I published the code on Github.

2 Answers
  1. I suggest you to use .convert() every loaded image to twice the FPS

    blockTextures = {block: transform.scale(image.load('blocks/' + block + '.png').convert(), (128, 128)) for block in allBlocks}
    breakTextures = [transform.scale(image.load('blocks/destroy_' + str(i) + '.png').convert(), (128, 128)) for i in range(10)]
    

https://www.pygame.org/docs/ref/surface.html#pygame.Surface.convert

  1. Do less calculations(Physics) in every frame. Remember required frame for player direction

     class Player: # This class controls and draws the player
         def __init__(self):
             self.pos = [0, 0]
             self.velocity = [0, 0]
    
             self.imageRight = transform.scale(image.load('steve.png'), (57, 230))
             self.imageLeft = transform.flip(self.imageRight, True, False)
             self.AImage = self.imageLeft
    
         def draw(self, screen, size):
             screen.blit(self.AImage, (size[0] / 2 - 28, size[1] / 2 - 115))
    
         def physics(self, blocks): # The first part checks for input, the second part does the physics
             pressed = key.get_pressed()
             if pressed[K_LEFT]:
                 self.velocity[0] -= 0.1
                 self.AImage = self.imageLeft
    
             if pressed[K_RIGHT]:
                 self.velocity[0] += 0.1
                 self.AImage = self.imageRight
    

And speedup 10% more FPS

  1. May be FPS can be better if you calculate World once to one big-big picture at start of game and draw part of it into screen once a frame. I'm not sure about it.

Your code is calling player.Physics() every frame, and checking collisions with every block. Even if the player is not moving, it's still checking.

Aside: I didn't really follow the reason for some of the velocity code here. The player's velocity seems to be changing even without user input, and x is decreasing, y is increasing, but there's no checks to stop when it's zero/near-zero. The code seems to be using a lot of CPU for checking collisions even when velocity becomes essentially zero.

Anyway, I suspect the major issue is there's just so many blocks. A common way of optimising this sort of thing is to group your blocks into a quadtree data structure. This partitions your blocks into quarters based on their position. Then each quarter is again partitioned into quarters, and so forth.

This means that you only need to collide once to rule-out 75% of the non-colliding blocks. Then you collide again, and remove a further 75% of non-colliding blocks. Eventually you get down to the collisions you really need to check. Decide the depth of the quadtree yourself, but even a couple of levels will save you lots and lots of unnecessary checks.

Related