pythonpygamepygame-clock

Why is my pygame program gradually getting slower the longer it runs?


I am making a platformer where the player changes the terrain by swapping the background color. All of the mechanics function properly, but the rate my program runs gradually gets really, excruciatingly slow and I don't know why. I've tried tidying my code up in places but I can't seem to get it to run at a constant rate. Why would this be happening and how can I fix it? I appreciate any help or tips you guys got!

Here is my complete code:

import pygame, sys
clock = pygame.time.Clock()
from pygame.locals import *
pygame.init()

window_size = (900,600)
window = pygame.display.set_mode(window_size)
pygame.display.set_caption('Pygame Platformer')

moving_right = False
moving_left = False

unit = 30
fall = 10
jump = -10
p_speed = 3.4

black =  (  0,   0,   0)
white =  (225, 225, 225)
teal =   (  0, 100, 100)
orange = (200,  75,   0)
grey =   ( 60,  60,  60)
red =    (170,   0,   0)

current_color = black
dark_color = grey
color_1 = teal
color_2 = red

game_map = [['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
            ['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
            ['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
            ['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
            ['0','0','0','0','0','0','0','2','2','2','2','2','0','0','0','0','0','0','0'],
            ['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
            ['2','2','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','2','2'],
            ['1','1','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','1','1'],
            ['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
            ['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
            ['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
            ['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
            ['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','3'],]

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

class player():
    def __init__(self):
        self.rect = pygame.Rect(100,100,unit,unit)
        self.vertical_momentum = 0
        self.player_movement = []
        self.jump_timer = 0
    def movement(self):
        self.player_movement = [0,0]
        if moving_right == True:
            self.player_movement[0] += p_speed
        if moving_left == True:
            self.player_movement[0] -= p_speed
        self.player_movement[1] += self.vertical_momentum
        self.vertical_momentum += 0.4
        if self.vertical_momentum > fall:
            self.vertical_momentum = fall
    def draw(self, display):
        pygame.draw.rect(display, dark_color, (self.rect.x, self.rect.y, unit, unit))
        
class room_0():
    def __init__(self, block_map):
        self.block_map = game_map
        self.dark_blocks = []
        self.color_blocks = []
    def draw(self, window):
        global tile_rects
        tile_rects = []
        y = 0
        for layer in self.block_map:
            x = 0
            for tile in layer:
                if tile == '1':
                    if current_color != color_1:
                        current_room.color_blocks.append(color_block(color_1, x*unit, y*unit))
                        tile_rects.append(pygame.Rect(x*unit,y*unit,unit,unit))
                elif tile == '2':
                    if current_color != color_2:
                        current_room.color_blocks.append(color_block(color_2, x*unit, y*unit))
                        tile_rects.append(pygame.Rect(x*unit,y*unit,unit,unit))
                elif tile == '3':
                    self.dark_blocks.append(dark_block(x*unit,y*unit))
                    tile_rects.append(pygame.Rect(x*unit,y*unit,unit,unit))
                x += 1
            y += 1        
        for i in self.dark_blocks:
            i.draw(window)
        for i in self.color_blocks:
            i.draw(window)

class dark_block():
    def __init__(self, x_position, y_position):
        self.x = x_position
        self.y = y_position
    def draw(self, window):
        pygame.draw.rect(window, dark_color, (self.x, self.y, unit, unit))
        
class color_block():
    def __init__(self, color, x_position, y_position):
        self.co = color
        self.x = x_position
        self.y = y_position
    def draw(self, window):
        pygame.draw.rect(window, self.co, (self.x, self.y, unit, unit))

tile_rects = []

p = player()

def collision_test(rect,tiles):
    hit_list = []
    for tile in tiles:
        if rect.colliderect(tile):
            hit_list.append(tile)
    return hit_list

def move(rect,movement,tiles):
    collision_types = {'top':False,'bottom':False,'right':False,'left':False}
    rect.x += movement[0]
    hit_list = collision_test(rect,tiles)
    for tile in hit_list:
        if movement[0] > 0:
            rect.right = tile.left
            collision_types['right'] = True
        elif movement[0] < 0:
            rect.left = tile.right
            collision_types['left'] = True
    rect.y += movement[1]
    hit_list = collision_test(rect,tiles)
    for tile in hit_list:
        if movement[1] > 0:
            rect.bottom = tile.top
            collision_types['bottom'] = True
        elif movement[1] < 0:
            rect.top = tile.bottom
            collision_types['top'] = True
    return rect, collision_types

def object_movement():
    global tile_rects
    p.movement()
    p.rect,collisions = move(p.rect,p.player_movement,tile_rects)

    if collisions['bottom'] == True:
        p.jump_timer = 0
        p.vertical_momentum = 0
    else:
        p.jump_timer += 1
    if collisions['top'] == True:
        p.vertical_momentum = 0

def redraw_game_window():
    window.fill(current_color)
    current_room.draw(window)
    p.draw(window)
    pygame.display.update()

##############################################################################
rooms = [room_0(game_map)]
current_room = rooms[0]

t = 0

while True: # game loop
    
    t +=1
    print(t)

    for event in pygame.event.get(): # event loop
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
            if event.key == K_d:
                moving_right = True
            if event.key == K_a:
                moving_left = True
            if event.key == K_SPACE:
                if p.jump_timer < 5:
                    p.vertical_momentum = jump
            if event.key == K_LEFT:             # color swapping
                if current_color != color_1:
                    current_color = color_1
                    dark_color = black
                else:
                    current_color = black
                    dark_color = grey
            if event.key == K_RIGHT:
                if current_color != color_2:
                    current_color = color_2
                    dark_color = black
                else:
                    current_color = black
                    dark_color = grey
        if event.type == KEYUP:
            if event.key == K_d:
                moving_right = False
            if event.key == K_a:
                moving_left = False
        
    object_movement()
    redraw_game_window()
    clock.tick(60)

Solution

  • The issue is caused by the fact, that the containers (lists) self.dark_blocks and self.color_blocks are refilled in every frame, but you missed to clear them. Hence the number of elements int the containers is continuously growing. Since the content of this lists is drawn in every frame, the performance drops. You can't see this effect, because the elements of the lists are drawn on top of each other.

    Clear self.dark_blocks and self.color_blocks at the begin of room_0.draw, right after tile_rects is cleared:

    class room_0():
        # [...]
    
        def draw(self, window):
            global tile_rects
            tile_rects = []
    
            self.dark_blocks = []
            self.color_blocks = []
    
            # [...]