How to organize the pygame code correctly?

1

I came from JavaScript which is a bit simpler than Python, so I did not have to worry about scoping (almost everything was global) or code organization. But in Python even though it is a language that has a better syntax I can not organize the code right, especially when I use pygame. Most of the time the commands will execute separate threads so it does not matter much the order that I call them, my code is getting very messy, has a variable being declared then I call a function then create a function, then instancio classes all in an arbitrary sort order, I do not know how to organize this, for example:

def main ():
    def embaralhar ():
        threading.Timer(card_shuffle.get_length() / 4, lambda: dar_cartas(39)).start()
    def dar_cartas (x):
        if x > 33: threading.Timer(card_shuffle.get_length() / 4, lambda: dar_cartas(x - 2)).start()
    os.environ["SDL_VIDEO_CENTERED"] = "1"
    pygame.init()
    open_sans_regular_18 = pygame.font.Font("fontes/open_sans/regular.ttf", 18)
    card_shuffle = pygame.mixer.Sound("sons/card_shuffle.wav")
    tela = pygame.display.set_mode([800, 600], pygame.NOFRAME)
    tela_rect = tela.get_rect()
    pontuacao = "0 x 0"
    placar = passion_one_regular_40.render(pontuacao, True, [255, 255, 255], [0, 0, 0])
    placar_rect = placar.get_rect(centerx = tela_rect.centerx)
    relogio = pygame.time.Clock()
    rodando = True
    embaralhar()
    while rodando:
        for e in pygame.event.get():
    pygame.quit()
main()

Please, an efficient tip to organize principally when the order does not interfere in the result of the program.

    
asked by anonymous 01.11.2017 / 01:29

1 answer

2

Boy

You need there is object-oriented. In the good way, not in the "mandatory" way that is Java, nor in the way "hidden under the semantics of prototyping" that is in Javascript.

You need less:

  • Nested functions within functions (with an appropriate object, all variables that small functions need can be attributes of the object)

  • Threads. Even though you say "it's all in separate threads, then I do not have to worry about the order," this is wrong. It's one thing to want to do things in parallel. Another is to want to play "all at the same time now without having control of anything".

  • With just the two examples you have of what your "thread functions" would look like, the chances of you having race-conditions that obliterate the consistency of your game look great.

    In Javascript it is simple to always put the "SetTimeout" for your function to be called after a while, and seem to run in a parallel thread. It is not - execution is always in order, the browser's mainloop is that it logs the functions registered with "setTimeout" and other events and calls everything in order.

    Pygame is a notorious library for not having a mainloop already built. It is up to the programmer to write the code that will be executed on all frames, and call the functions and methods of checking everything.

    Replace this with calls to threading.Timer almost at random is awesome - could work, but the running program can only be viewed as a paste of spaghetti in the same gravity vacuum. (Remember that asynchronous calls in javascript are not threads.) Each function called with "SetTimeout" will run from beginning to end, and can change other variables without being interrupted. With Threads, you can have an interrupt within any code expression, between any two lines, and this interrupt might change the variables you were moving in.

    Well, if you're going to continue with pygame, you'll need to create your own event system. You will learn a lot if you do this, and you will become a better dev. But - the barrier to having to create this can be great. You can choose to use a framework such as the " pyglet " which already has a mainloop, and an event system - and you will have calls actually equivalent to SetTimeout ready to work without the side effects of the threads.

    That said, some tips that can help you: use an object-oriented puck as needed.

    For the small stretch you gave, start with a class (I'm assuming you use Python 3 - if not, approve and change now to Python 3.6)

    def load_fonts():
        global open_sans_regular_18, ...
        # Esse tipo de recurso só ode ser carregado depois do pygame.init, 
        # no entanto, com o nome "comprido" e como é algo que não muda,
        # pode estar disponível como variável global tranquilamente.
    
        open_sans_regular_18 = pygame.font.Font("fontes/open_sans/regular.ttf", 18)
        ...
    
    class Jogo:
    
        def __init__(self):
            os.environ["SDL_VIDEO_CENTERED"] = "1"
            pygame.init()
            load_fonts()
            self.card_shuffle = pygame.mixer.Sound("sons/card_shuffle.wav")
            self.tela = pygame.display.set_mode([800, 600], pygame.NOFRAME)
            self.tela_rect = tela.get_rect()
    
            self.pontuacao_jogador_1 = 0
            self.pontuacao_jogador_2 = 0
    
    
            self.relogio = pygame.time.Clock()
            rodando = True
            embaralhar()
    
        def desenha_placar(self):
            pontuacao = f"{self.pontuacao_jogador_1} X {self.pontuacao_jogador_2}"
            placar = passion_one_regular_40.render(pontuacao, True, [255, 255, 255], [0, 0, 0])
            placar_rect = placar.get_rect(centerx = self.tela_rect.centerx)
            self.tela.blit(placar_rect, placar)
    
    
        def mainloop(self):
            rodando = True
            embaralhar
            while rodando:
                for e in pygame.event.get():
                    ...
                self.desenha_placar()
                    ...
                if condicao:
                    self.embaralhar()
                pygame.display.flip()
                self.relogio.tick()
    
        def agendar(self, intervalo, funcao, parametros):
            # aqui voce cria seu sistema de eventos - pode ser criando uma lista 
            # onde cada elemento é uma tupla com o "numero de tick" onde o evento deve
            # ser chamado, seguido do objeto chamável e argumentos.
            # a lista pode ser mantida em ordem com a bibliotea "heapq" do Python.
            ...
    
        def embaralhar (self):
            tempo = self.card_shuffle.get_length() / 4
            self.dar_cartas(39))
    
        def dar_cartas (x):
            if x > 33: 
                self.agendar(self.card_shuffle.get_length() / 4, 
                             self.dar_cartas, x - 2)
    
        def quit(self):
            pygame.quit()
    
    if __name__ == "__main__":
        jogo = Jogo()
        try:
            jogo.mainloop()
        finally:
            jogo.quit()
    
        
    01.11.2017 / 15:05