1

I have a problem with massive lag in my game. My game has a map that is quite large. The map is 250x250 tiles. Each tile is 32x32. Note that I'm using an older laptop. When I open a task manager it says that my CPU usage is up to 50%, but the memory usage is only 30-40 MB. I'm a beginner so I would appreciate any help. I'm using the code that I got from some YouTube tutorial. I did some modifying for it to work with my project.

    import pygame as pg, time, random
    from pygame.locals import *
    from pytmx.util_pygame import load_pygame


    def blit_all_tiles(screen, tmxdata, world_offset):
    for layer in tmxdata:
        for tile in layer.tiles():
            # tile[0] = x coordinate
            # tile[1] = y coordinate
            # tile[2] = image data
            x_pixel = tile[0] * 32 + world_offset[0]
            y_pixel = tile[1] * 32 + world_offset[1]
            screen.blit(tile[2], (x_pixel, y_pixel))


    # Game Variables
    def main():
    tmxdata = load_pygame("assets/Chris' adventure.tmx")
    # Standing
    player_stand = pg.image.load("CharAssets/Cris 01.png")
    player_stand = pg.transform.scale(player_stand, (32, 32))
    # Moving Right
    player_left = [
        pg.image.load("CharAssets/Cris 04.png"),
        pg.image.load("CharAssets/Cris 05.png"),
        pg.image.load("CharAssets/Cris 06.png"),
    ]
    player_up = [
        pg.image.load("CharAssets/Cris 07.png"),
        pg.image.load("CharAssets/Cris 08.png"),
        pg.image.load("CharAssets/Cris 09.png"),
    ]
    player_down = [
        pg.image.load("CharAssets/Cris 01.png"),
        pg.image.load("CharAssets/Cris 02.png"),
        pg.image.load("CharAssets/Cris 03.png"),
    ]
# Resize
player_left = [pg.transform.scale(image, (32, 32)) for image in player_left]
player_left_f = 0
player_up = [pg.transform.scale(image, (32, 32)) for image in player_up]
player_up_f = 0
player_down = [pg.transform.scale(image, (32, 32)) for image in player_down]
player_down_f = 0
# Flipping
player_right = [pg.transform.flip(image, True, False) for image in player_left]
player_right_f = 0
direction = "stand"
world_offset = [0, 0]

quit = False
x = 400
y = 200 + 128
# Game Loop
while not quit:
    screen.fill((0, 0, 0))
    blit_all_tiles(screen, tmxdata, world_offset)
    # Events
    keypressed = pg.key.get_pressed()
    for event in pg.event.get():
        # print(event)
        if event.type == QUIT:
            quit = True
    if keypressed[ord("a")]:
        x = x - 20
        world_offset[0] += 40
        direction = "left"
    if sum(keypressed) == 0:
        direction = "stand"
    if keypressed[ord("d")]:
        x = x + 20
        world_offset[0] -= 40
        direction = "right"
    if keypressed[ord("w")]:
        y = y - 20
        world_offset[1] += 40
        direction = "up"
    if keypressed[ord("s")]:
        y = y + 20
        world_offset[1] -= 40
        direction = "down"
    if y < 204:
        y = 204
    if y >= screen.get_height() - 204 - 32:
        y = screen.get_height() - 204 - 32
    if x < 204:
        x = 204
        # world_offset[0] += 10
    if x >= screen.get_width() - 204 - 32:
        x = screen.get_width() - 204 - 32

    # Game Logic
    # player = Rect(x, y, 32, 32)
    # pg.draw.rect(screen, (225, 0, 120), player)
    if direction == "stand":
        screen.blit(player_stand, (x, y))
    elif direction == "left":
        screen.blit(player_left[player_left_f], (x, y))
        player_left_f = (player_left_f + 1) % len(player_left)
    elif direction == "right":
        screen.blit(player_right[player_right_f], (x, y))
        player_right_f = (player_right_f + 1) % len(player_right)
    elif direction == "up":
        screen.blit(player_up[player_up_f], (x, y))
        player_up_f = (player_up_f + 1) % len(player_up)
    elif direction == "down":
        screen.blit(player_down[player_down_f], (x, y))
        player_down_f = (player_down_f + 1) % len(player_down)

    # Screen Update
    pg.display.update()
    clock.tick(60)


    # Game Initialization
    if __name__ == "__main__":
    width, height = 800, 600
    pg.init()
    pg.mixer.init()
    screen = pg.display.set_mode((width, height))
    pg.display.set_caption("Chris' quest")
    clock = pg.time.Clock()
    main()
    pg.quit()

I successfully imported the map, and it works for now but the lag is extreme. I'm confused and i don't know how to solve this problem. I would appreciate help. Thanks in advance.

  • 1
    One thing you can try is to not render each tile each frame. At the start of the game, create one big Surface for the entire map, render the tiles to that surface, and blit that surface to the screen each frame. Another thing to try is to skip blitting tiles that are not on the screen anyway. This way, you can skip most of the blit calls. – sloth Mar 24 '21 at 14:46
  • @sloth This second idea sounds good. Do you know how I should do it? Thank you – Vladimir Avramović Mar 24 '21 at 15:29

1 Answers1

3

Ensure that the image Surface has the same format as the display Surface. Use convert() (or convert_alpha()) to create a Surface that has the same pixel format. This improves performance when the image is blit on the display, because the formats are compatible and blit does not need to perform an implicit transformation.

For example:

player_stand = pg.image.load("CharAssets/Cris 01.png")

player_stand = pg.image.load("CharAssets/Cris 01.png").convert_alpha()

Another option is to create a single large map of the game.

Create a pygame.Surface the size of the entire map by drawing all the tiles on it:

def create_map():
    game_map = pygame.Surface((250 * 32, 250 * 32))
    for layer in tmxdata:
        for tile in layer.tiles():
            # tile[0] = x coordinate
            # tile[1] = y coordinate
            # tile[2] = image data
            x_pixel = tile[0] * 32
            y_pixel = tile[1] * 32
            game_map.blit(tile[2], (x_pixel, y_pixel))
    return game_map  
game_map = create_map()

In the application, you draw only a subsection of the map on the screen, using the optional area argument of the blit method:

while not quit:
    # [...]

    sub_area = screen.get_rect(topleft = world_offset)
    screen.blit(game_map, (0, 0), sub_area) 

    # [...]

This solution improves performance. However, you pay with memory usage.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • I tried implementing this code as it says, but I'm getting an error "Unresolved reference 'game_map" and it says that the name is not defined although I did everything you said – Vladimir Avramović Mar 24 '21 at 16:52
  • My code left unchanged. I only added convert_alpha() after the every image. I tried to implement your code now but tmxdata is not defined – Vladimir Avramović Mar 24 '21 at 17:28
  • I'm sorry to hear that you think like that. I think it's not working because my map is .tmx file. Thank you – Vladimir Avramović Mar 24 '21 at 17:54
  • @VladimirAvramović You have drawn the tiles before. Why can't you draw them now? Before you have drawn the tiles to `screen` now you draw them to `game_map`. – Rabbid76 Mar 24 '21 at 17:54