Jak to w piosence Van Halen - skaczemy! 😃

Tak jak pisałem w poprzednim wpisie - musiałem zmienić myślenie o ruchu w grze. Ruch nie może odbywać się już „na zawołanie” - musi chodzić timer który będzie ogarniał bardziej zaawansowany ruch niż proste wciśnięcie przycisku :arrow_left: czy :arrow_right:.

Dodałem do aplikacji timer który tyka sobie co 50 ms. Wrzuciłem tam odpowiedzialność za odświeżenie widoku.

def tick(self):
     self.update()

I dla ruchu lewo-prawo wszystko śmiga jak trzeba. Akcja ruchu na graczu się wykonuje (czyli w uproszczeniu zmienia się jego położenie na osi x). No ale nie ruszyło to tematu skoku który składa się właściwie z serii ruchów (jeśli za ruch uznamy aktualizację pozycji gracza na osiach xi y).

I tutaj doznałem olśnienia! Skoro mam timer który tyka co jakiś czas to czemu nie zrzucić na niego podejmowania decyzji czy ruch już się zakończył czy też jeszcze trwa? Znowu dla ruchu lewo-prawo wszystko jest proste - w self.tick() trzeba tylko wykonać raz funkcję odpowiedzialną za ruch i tyle, przy skoku zaś mam informację z klasy Player mówiącą w jakim stanie jest skok użytkownika player.jump_started.

Technicznie załatwiłem to przenosząc wykonanie funkcji z metody def keyPressEvent(self, e) na ustawianie w niej aktualnej akcji. Aktualną akcję przechowuję w zmiennej self.tick_action muszę więc tylko przy każdorazowym wciśnięciu klawisza ustawiać poprawną funkcję do wykonania. W Pythonie jest to proste - wystarczy zapisać do zmiennej metodę tak, jakby ją się wykonywało - z tą różnicą, że nie dodajemy nawiasów po nazwie metody. Powoduje to zapisanie do zmiennej funkcji a nie wykonanie jej - wykonanie realizowane jest właśnie przez nawiasy.

W pewnym uproszczeniu (dla ruchu w lewo) wygląda to tak:

def keyPressEvent(self, e):
        key = e.key()

        if key == QtCore.Qt.Key_Left:
            self.tick_action = self.player_move_backward # [1]

def tick(self):
        if self.tick_action is None: # [2]
            return

        self.tick_action() # [3]
        self.update()

        self.tick_action = None # [4]

Idąc po numerach dzieją się następujące rzeczy:
[1] po wykryciu wciśnięcia strzałki w lewo zmienne tick_action ustawiana jest na self.player_move_backward (ale ta się nie wykonuje, bo nie ma nawiasów)
[2] w metodzie tick sprawdzamy czy mamy w ogóle ustawioną jakąś metodę
[3] metoda jest wykonywana
[4] akcja ustawiana jest na None aby zakończyć ruch

Żeby ograć skakanie musiałem tylko dodać przy [4] sprawdzenie czy player jest w trakcie skoku, także w tej chwili [4] wygląda tak:

if self.player.jump_started:
    self.tick_action = self.player_jump
else:
    self.tick_action = None

Na teraz to wszystko, w następnym wpisie zajmę się ogarnięciem wciskania kilku klawiszy jednocześnie aby ruch i skok mogły odbywać się jednocześnie. Stay tuned!

Na koniec małe demo skoku 😃