pythonpygameframe-rateterrain

How can I improve my terrain generator's performance?


My terrain generator I want to be fullscreen, but when I change block size, FPS decreases. How to fix this?

pygame.init()

scsizeX = 600
scsizeY = 400
screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
scX, scY = screen.get_size()
clock = pygame.time.Clock ()
plrX = 300
plrY = 200
speed = 3
plrOri = "Right"
cameraX = 0
cameraY = 0
genSize = 100
gen = []
fonts = [pygame.font.SysFont("Arial", 24)]
FPS = 1024
playerSize = 36
blockSize = 64
plrMoved = False
background_colour = (255,255,255)
clo = (255,255,0)

pygame.mouse.set_visible(False)

playerimg = pygame.image.load('assets/player.png')
mouseimg = pygame.image.load('assets/mouse.png')
grassimg = pygame.image.load('assets/grass.png')
stoneimg = pygame.image.load('assets/stone.png')
grassimg = pygame.transform.scale(grassimg, (blockSize, blockSize))
stoneimg = pygame.transform.scale(stoneimg, (blockSize, blockSize))
mouseimg = pygame.transform.scale(mouseimg, (48, 48))
playerimg = pygame.transform.scale(playerimg, (playerSize * 2, playerSize * 2))

screen.fill(background_colour)

player = screen.blit(playerimg, (300, 200))
mouse = screen.blit(mouseimg, (300, 200))

for x in range(genSize):
    for y in range(genSize):
        g = random.randint(1,2)
        gen.append(g)

def block(tp, posX, posY):
    if tp == "grass":
        return screen.blit(grassimg, (posX - cameraX - playerSize, posY - cameraY - playerSize))
    elif tp == "stone":
        return screen.blit(stoneimg, (posX - cameraX - playerSize, posY - cameraY - playerSize))

def render(fnt, what, color, where):
    "Renders the fonts as passed from display_fps"
    text_to_show = fnt.render(what, 0, pygame.Color(color))
    screen.blit(text_to_show, where)

game = True
while game:

    screen.fill(background_colour)
    plrMoved = False

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            game = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouseimg = pygame.transform.scale(mouseimg, (44, 44))
        if event.type == pygame.MOUSEBUTTONUP:
            mouseimg = pygame.image.load('assets/mouse.png')
            mouseimg = pygame.transform.scale(mouseimg, (48, 48))

    keys = pygame.key.get_pressed()

    if keys[pygame.K_w]:
        plrY -= speed
        plrMoved = True
    if keys[pygame.K_a]:
        plrX -= speed
        plrMoved = True
        plrOri = "Left"
    if keys[pygame.K_s]:
        plrY += speed
        plrMoved = True
    if keys[pygame.K_d]:
        plrX += speed
        plrMoved = True
        plrOri = "Right"

    cameraX = plrX - (scsizeX / 2)
    cameraY = plrY - (scsizeY / 2)

    #player = screen.blit(playerimg, (plrX, plrY))

    #if plrMoved:
    for x in range(int(scX / blockSize) + 2):
        for y in range(int(scY / blockSize) + 3):
            g = gen[(floor(cameraX / blockSize) + x) + (floor(cameraY / blockSize) + y)]

            if g == 1:
                block("grass", (floor(cameraX / blockSize) + x) * blockSize, (floor(cameraY / blockSize) + y) * blockSize)
            elif g == 2:
                block("stone", (floor(cameraX / blockSize) + x) * blockSize, (floor(cameraY / blockSize) + y) * blockSize)

    p_pos = plrX - cameraX - playerSize, plrY - cameraY - playerSize,
    if plrOri == "Right":
        player = screen.blit(playerimg, p_pos)
    elif plrOri == "Left":
        player = screen.blit(pygame.transform.flip(playerimg, True, False), p_pos)

    Mx, My = pygame.mouse.get_pos()
    msrct = mouseimg.get_rect()
    msrct = msrct.move((Mx, My))
    mouse = screen.blit(mouseimg, msrct)

    render(
        fonts[0],
        what=str(int(clock.get_fps())),
        color="white",
        where=(0, 0))

    pygame.display.flip()
    clock.tick(FPS)

pygame.quit()

I tried to only update terrain when player is moving but FPS is still low and when I stop moving the screen gets white.


Solution

  • Ensure that the background 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 background is, when the background is blit on the display, because the formats are compatible and blit does not need to perform an implicit transformation:

    playerimg = pygame.image.load('assets/player.png').convert_alpha() 
    mouseimg = pygame.image.load('assets/mouse.png').convert_alpha() 
    grassimg = pygame.image.load('assets/grass.png').convert_alpha()
    stoneimg = pygame.image.load('assets/stone.png').convert_alpha()
    

    You can buy performance by paying with memory usage. Create a large Surface with the complete map:

    game_map = pygame.Surface((genSize * 64, genSize*64))
    for x in range(genSize):
        for y in range(genSize):
            g = random.randint(1,2)
            if g == 1:
                game_map.blit(grassimg, (x * 64, y * 64))
            elif g == 2:
                game_map.blit(stoneimg, (x * 64, y * 64))
    

    blit the area which is currently visible on the screen:

    while game:
        # [...]
    
        map_sub_rect = screen.get_rect(topleft = (cameraX, cameraY))
        screen.blit(game_map, (0, 0), map_sub_rect)
    
        # [...]