Dlaczego mój odtwarzacz sprite porusza się szybciej, gdy poruszam myszą?

17

Próbuję opracować prostą grę stworzoną za pomocą Pygame (biblioteka Python).

Mam spriteobiekt, który jest playeri poruszam go za pomocą klawiszy strzałek. Jeśli nie poruszę myszą, duszek porusza się normalnie, ale kiedy poruszam myszą, duszek porusza się szybciej (jak x2 lub x3). playerObiekt znajduje się wewnątrz charsGroupvar.

Uruchomiłem grę w W7 i Ubuntu. To samo dzieje się w obu systemach operacyjnych.

Mam więcej bytów, które poruszają się jak NPC i pociski, ale nie mają na nie wpływu, tylko gracz. Biorąc to pod uwagę, myślę, że problem może mieć bezpośredni związek z systemem poruszania się gracza (klawisze strzałek).

Oto update()metoda playerobiektu:

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

A oto pętla 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() 

Nie wiem, co więcej możesz mi pomóc, ale o wszystko, czego potrzebujesz (więcej informacji lub kod), poproś o to!

Drumnbass
źródło
5
Twój dokładny błąd faktycznie istnieje w wielu aplikacjach. Spróbuj przeciągnąć zaznaczenie w dużym dokumencie i przesunąć kursor poza krawędź. Zwykle przewijanie krawędzi programu uruchamia się i powoli wybiera większą część dokumentu. Jeśli poruszasz myszą z boku na bok, zwykle przewija się ona znacznie szybciej, ponieważ ich prędkość przewijania jest powiązana z pętlą zdarzeń, a ruchy X budzą pętlę zdarzeń wielokrotnie.
Ben Jackson,
2
@BenJackson Uważam, że jest to przydatny błąd, gdy przewijanie jest bardzo powolne.
user253751
1
Nie ma to związku z twoim błędem, ale zalecam jednorazowe załadowanie obrazu i przechowywanie go w obiekcie. BULLET_IMAGE = pygame.image.load("bullet.png")a późniejself.bulletsGroup.add(Bullet(BULLET_IMAGE...
DJMcMayhem
@DJMcMayhem Masz całkowitą rację, zrobiłem to z resztą zdjęć, ale przegapiłem to z tym .. dzięki! :)
Drumnbass

Odpowiedzi:

42

tl; dr nie mieszaj pętli zdarzeń z pętlą gry .

Gdy poruszasz myszą, gra otrzymuje mnóstwo pygame.MOUSEMOTIONwydarzeń. Tak naprawdę nie używasz tych zdarzeń do aktualizacji pozycji myszy, otrzymujesz bieżący stan myszy pygame.mouse.get_pos(). To nieefektywne, ale to nie problem.

Problem polega na tym, że aktualizujesz pozycję gracza w pętli zdarzeń !

Oto co powinno się wydarzyć:

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

Oto, co robi Twój kod:

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

Kiedy poruszasz myszą, pętla zdarzeń będzie wykonywana wiele razy na klatkę. Ale kiedy sprawdzasz, które klawisze są naciskane pygame.key.get_pressed(), pozostają one wciśnięte, dopóki nie puścisz, jakiś czas później. W związku z tym, że pętla zdarzeń przesuwa się przez zdarzenia ruchu myszy, ponownie zastosuje ruchy gracza.

Rozwiązanie jest proste: przenieś odtwarzacz poza pętlę zdarzeń.

congusbongus
źródło
1
Dzięki! Działa idealnie teraz i prawdopodobnie nigdy nie zdawałem sobie sprawy z tego, co się dzieje! Przy okazji, dlaczego uważasz, że pygame.mouse.get_pos()to jest nieefektywne? Jakie mam alternatywy?
Drumnbass
Cześć @Congusbongus, czy mógłbyś mi to wyjaśnić? Dzięki.
Drumnbass
@Drumnbass pygame.mouse.get_pos()pobiera najnowszą pozycję myszy, niezależnie od kolejki zdarzeń, więc nie ma potrzeby umieszczania jej w pętli zdarzeń. Alternatywą byłoby przetworzenie każdego pygame.MOUSEMOTIONosobiście, ale chyba że potrzebujesz każdego wydarzenia (np. Piszesz program malarski), zrobi to najnowsza pozycja.
congusbongus
3

Oto kilka pomysłów na uzupełnienie istniejącej odpowiedzi .

Gaffer On Games ma świetny artykuł na temat pętli gier , do którego odwołuje się wszędzie.

Twoja pętla gry powinna mieć różne niezależne etapy: Wejście, Aktualizacja, Renderowanie.

Możesz na przykład czytać dane wejściowe 30 razy na sekundę (lub w czasie rzeczywistym dla lepszej reakcji), robić 30 aktualizacji na sekundę i renderować 60 klatek na sekundę lub dowolne wartości, które działają dobrze w twojej grze.

HgMerk
źródło