Por que meu player sprite se move mais rápido quando movo o mouse?

17

Estou tentando desenvolver um jogo simples feito com Pygame (biblioteca Python).

Eu tenho um spriteobjeto que é o playere movo-o usando as setas. Se eu não mover o mouse, o sprite se moverá normalmente, mas quando movo o mouse, o sprite se moverá mais rápido (como x2 ou x3). O playerobjeto está dentro da charsGroupvar.

Eu executei o jogo no W7 e no Ubuntu. A mesma coisa acontece nos dois sistemas operacionais.

Eu tenho mais entidades que se movem como NPCs e balas, mas elas não são afetadas, apenas o jogador. Diante disso, acho que o problema talvez tenha uma conexão direta com o sistema de movimentação do player (teclas de seta).

Aqui está o update()método do playerobjeto:

def update(self):

    for event in pygame.event.get():
        key = pygame.key.get_pressed()
        mouseX, mouseY = pygame.mouse.get_pos()
        if event.type == pygame.MOUSEBUTTONDOWN:
            self.bulletsGroup.add(Bullet(pygame.image.load("bullet.png"),
                                          self.rect.x + (self.image.get_width()/2),
                                           self.rect.y + (self.image.get_height()/2),
                                            mouseX, mouseY, 50, 50))

        if key[pygame.K_RIGHT]:
            if not self.checkCollision():
                self.rect.x += 10
            else:
                self.rect.x -= 10
        if key[pygame.K_LEFT]:
            if not self.checkCollision():
                self.rect.x -= 10
            else:
                self.rect.x += 10
        if key[pygame.K_UP]:
            if not self.checkCollision():
                self.rect.y -= 10
            else:
                self.rect.y += 10
        if key[pygame.K_DOWN]:
            if not self.checkCollision():
                self.rect.y += 10
            else:
                self.rect.y -= 10

E aqui está o loop while:

while True:

    if PLAYER.healthBase <= 0:
        GAMEOVER = True

    if not GAMEOVER:
        mapTilesGroup.draw(SCREEN)
        charsGroup.update()
        charsGroup.draw(SCREEN)
        npcsGroup.update()
        npcsGroup.draw(SCREEN)
        drawBullets()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

    if GAMEOVER:
        myfont = pygame.font.SysFont("monospace", 30)
        label = myfont.render("GAME OVER!", 1, (255, 255, 0))
        SCREEN.blit(label, (400, 300))

    freq.tick(0)

    pygame.display.flip() 

Não sei o que mais você pode precisar para me ajudar, mas qualquer coisa que você precise (mais informações ou código) é só pedir!

Drumnbass
fonte
5
Seu bug exato realmente existe em muitos aplicativos. Tente arrastar a seleção em um documento grande e mover o cursor para fora da borda. Geralmente, a rolagem da borda do programa é ativada e lentamente seleciona mais do documento. Se você mover o mouse de um lado para o outro, ele normalmente rolará muito mais rápido, pois a velocidade de rolagem está ligada ao loop de eventos e os movimentos X ativam o loop de eventos repetidamente.
Ben Jackson
2
@ BenJackson Acho que é um bug útil quando a rolagem é terrivelmente lenta para começar.
User253751 22/05
1
Isso não está relacionado ao seu bug, mas eu recomendaria carregar a imagem uma vez e armazená-la em um objeto. BULLET_IMAGE = pygame.image.load("bullet.png")e depoisself.bulletsGroup.add(Bullet(BULLET_IMAGE...
DJMcMayhem
@DJMcMayhem Você está totalmente certo, eu já fiz isso com o resto das imagens, mas senti falta disso ... obrigado! :)
Drumnbass

Respostas:

43

tl; dr não misture seu loop de eventos com seu loop de jogo .

Quando você move o mouse, o jogo recebe uma carga de pygame.MOUSEMOTIONeventos. Na verdade, você não usa esses eventos para atualizar a posição do mouse, mas está obtendo o estado atual do mouse pygame.mouse.get_pos(). Isso é ineficiente, mas não é o problema.

O problema é que você está atualizando a posição do jogador dentro do loop de eventos !

Isto é o que deveria acontecer:

game loop:
    event loop # get key presses, mouse moves etc.)
    if key pressed in the event loop:
        move the player

Isto é o que seu código faz:

game loop:
    event loop:
        if key pressed:
            move the player

Quando você move o mouse, o loop de eventos é executado várias vezes por quadro. Mas quando você verifica com quais teclas é pressionada pygame.key.get_pressed(), elas permanecem pressionadas até você soltar, algum tempo depois. Portanto, à medida que o loop de eventos estiver agitando os eventos de movimentação do mouse, ele reaplicará os movimentos da reprodutora repetidamente.

A solução é simples: mova o player para fora do loop de eventos.

congusbongus
fonte
1
Obrigado! Funciona perfeitamente agora e provavelmente eu nunca tinha percebido o que estava acontecendo! Btw, por que você diz que pygame.mouse.get_pos()é ineficiente? Que alternativas eu tenho?
Drumnbass
Olá, @congusbongus, você poderia me explicar isso? Obrigado.
Drumnbass
O @Drumnbass pygame.mouse.get_pos()obtém a posição mais recente do mouse, independentemente da fila de eventos, portanto, não há necessidade de colocá-lo dentro do loop de eventos. A alternativa seria processar pygame.MOUSEMOTIONvocê mesmo, mas, a menos que você precise de todos os eventos (por exemplo, você esteja escrevendo um programa de pintura), a última posição será necessária.
26615 congusbongus
3

Aqui estão mais algumas reflexões para complementar a resposta existente .

O Gaffer On Games tem um ótimo artigo sobre loops de jogos que foi mencionado em todos os lugares.

O loop do seu jogo deve ter diferentes estágios independentes: Entrada, Atualização, Render.

Você pode, por exemplo, ler entradas 30 vezes por segundo (ou em tempo real para obter melhor capacidade de resposta), fazer 30 atualizações por segundo e renderizar 60 quadros por segundo, ou quaisquer valores que funcionem bem para o seu jogo.

HgMerk
fonte