Why is such a simple game getting so heavy?

1

This is the first project I've developed using Pygame. Because it was a simple card game I expected it to be very light but not really. As I am using a very weak computer (single core 1gb ram) it is easy to see it, here it is running below 20 fps. Is it a problem in my code or is pygame a bit heavy?

This is the code for my main class that makes everything work:

import os
import random
import pygame
from button import Button
from card import Card


class Truco(object):

    def __init__(self):
        super(Truco, self).__init__()
        os.environ["SDL_VIDEO_CENTERED"] = "1"
        pygame.init()
        self.load_fonts()
        pygame.display.set_caption("Truco")
        self.screen = pygame.display.set_mode([800, 600])
        self.screen_rect = self.screen.get_rect()
        self.clock = pygame.time.Clock()
        self.play()

    def load_fonts(self):
        self.fonts = {}
        for file in os.listdir("fonts"):
            for size in [18, 24, 40, 80]:
                self.fonts["{}_{}".format(file[0:-4], size)] =\
                    pygame.font.Font(os.path.join("fonts", file), size)

    def play(self):
        self.cards_list = []
        for value in "4567qjka23":
            for suit in "diamonds spades hearts clubs".split():
                self.cards_list.append(Card(suit, value))
        self.human_score = 0
        self.robot_score = 0
        self.background = pygame.image.load("images/background.png")
        self.background_rect = self.background.get_rect()
        self.table = pygame.image.load("images/table.png")
        self.table_rect = self.table.get_rect(center=self.screen_rect.center)
        self.button_correr = Button(
            "images/button_2.png", self.fonts["titan_one_24"],
            "Correr", [255, 255, 255],
            bottomright=self.screen_rect.bottomright)
        self.button_truco = Button(
            "images/button_1.png", self.fonts["titan_one_24"], "Truco",
            [0, 0, 0], midbottom=self.button_correr.rect.midtop)
        self.card_shuffle = pygame.mixer.Sound("sounds/card_shuffle.ogg")
        self.shuffle()

    def shuffle(self):
        self.card_shuffle.play()
        random.shuffle(self.cards_list)
        self.cards_group = pygame.sprite.Group()
        for x in range(0, 40):
            self.cards_list[x].rotoflip(0, "back")
            self.cards_list[x].rect.center = [self.screen_rect.centerx - x / 2,
                                              self.screen_rect.centery - x / 2]
            self.cards_group.add(self.cards_list[x])
        self.cards_human = []
        self.cards_robot = []
        for x in range(0, 6, 2):
            self.cards_human.append(self.cards_list[x])
            self.cards_robot.append(self.cards_list[x + 1])
        self.card_vira = self.cards_list[6]
        self.index = 0
        pygame.time.set_timer(pygame.USEREVENT, 750)

    def give_cards(self, x):
        if x < 3:
            self.cards_robot[x].rect.midtop = [
                self.screen_rect.centerx + (x - 1) * 70, self.screen_rect.top]
            self.cards_human[x].flip("front")
            self.cards_human[x].rect.midbottom = [
                self.screen_rect.centerx + (x - 1) * 70,
                self.screen_rect.bottom]
        else:
            self.card_vira.rotoflip(-90, "front")
            self.card_vira.rect.center = self.cards_list[7].rect.midright
            pygame.time.set_timer(pygame.USEREVENT, 0)

    def loop(self):
        self.running = True
        while self.running:
            self.event()
            self.draw()
            pygame.display.update()
            self.clock.tick(60)

    def event(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False
            elif event.type == pygame.USEREVENT:
                self.give_cards(self.index)
                self.index += 1
            elif event.type == pygame.MOUSEBUTTONDOWN:
                self.shuffle()
            elif event.type == pygame.MOUSEMOTION:
                mouse_pos = pygame.mouse.get_pos()
                self.button_correr.hover(mouse_pos)
                self.button_truco.hover(mouse_pos)


    def draw(self):
        score = self.fonts["libre_baskerville_40"].render(
            "{} x {}".format(self.human_score, self.robot_score),
            True, [255, 255, 255], [0, 0, 0])
        fps = self.fonts["libre_baskerville_18"].render("FPS: {}".format(
            self.clock.get_fps()), True, [0, 0, 0])
        fps_rect = fps.get_rect(bottomleft=self.screen_rect.bottomleft)
        self.screen.blit(self.background, self.background_rect)
        self.screen.blit(self.table, self.table_rect)
        self.screen.blit(score, [0, 0])
        self.screen.blit(fps, fps_rect)
        self.cards_group.draw(self.screen)
        self.button_correr.draw(self.screen)
        self.button_truco.draw(self.screen)

    def quit(self):
        pygame.quit()
    
asked by anonymous 01.05.2018 / 16:52

1 answer

1

Use PyCharm's profiling mode (which is nothing more than the cProfile utility. below the cloths) indicates that most of the time (91.3%) is spent in the blit function.

Althoughitisanecessaryfunctiontodrawthingsonthescreen,wemaybeabletodecreasetheirusewhenitisnotnecessary.WhenIanalyzethecodebetter,Ifindthebiggestproblem:wealwaysdrawallthecards,eventhoseunderneatheachother.

Ichangedthe

self.cards_group.draw(self.screen)

Bythefollowing:

forcardinself.cards_human:self.screen.blit(card.image,card.rect)forcardinself.cards_robot:self.screen.blit(card.image,card.rect)self.screen.blit(self.card_vira.image,self.card_vira.rect)
Imean:insteadofdrawingallthecards,Istartedtodrawonlythosethatwereinmyhandortherobot,andtheletterturned.TheFPSthatwasat45roseto~65onmymachine,afterremovingthe60fpslimitation.

Ofcoursethishastheunwantedeffectthattherestofthecakeisnolongerdrawn.Thesolutionwouldbetohaveanownimageforthecards,ortodrawonlythetopcardandthentodrawonlythecornerofeachofthebottomcardstoavoiddrawingthesamecardseveraltimesovertheother./p>

AnotheroptimizationthatIdidbutdidnotdosomucheffect(maybe~2fps)wasnotloadingthesamefiletotheback,butusingaglobalversionloadedonlyonce.Imean,incard.py,Ididthis:

back=pygame.image.load("images/card_back_red_1.png")
back = pygame.transform.smoothscale(back, [70, 95])

class Card(pygame.sprite.Sprite):
    (...)
    self.back = back
    
01.05.2018 / 18:22