Rozdział 19 · Funkcje gry

Pause / Resume

Pauza zatrzymuje model w miejscu – konsumuje tę samą grę, nie rozpoczyna nowej.

Pauza zatrzymuje tykanie, a nie rysuje na wierzchu gry biegowej

Dwa prawdziwe okna obok siebie: po lewej gra działa, po prawej napis PAUSE i podpowiedź Space kontynuować w tej samej klatce
Prawdziwe okno: toggle_pause() nie przesuwa węża dalej — ramka po prawej stronie jest taka sama jak po lewej, tylko z nakładką.

Klawisz Space przełącza grę między RUNNING oraz PAUSED:

toggle_pause.py
def toggle_pause(self):
    if self.state.status is GameStatus.RUNNING:
        self._generation += 1   # natychmiast unieruchamia już zaplanowany tik
        self.state.status = GameStatus.PAUSED
        self._show_overlay(„PAUZA”, „Space kontynuować”)
    elif self.state.status is GameStatus.PAUSED:
        self._generation += 1   # Nowe pokolenie – dokładnie jeden świeży łańcuch odkleń
        self.state.status = GameStatus.RUNNING
        self._clear_overlay()
        self._schedule_next_tick()
Zatrzymanie nie odtwarza węża
toggle_pause() nie dotyka state.snake, state.score lub state.food — tylko zmiany status. Restart kontynuuje tę samą grę z tego samego punktu zamiast zaczynać nową.

Dlaczego jedna kontrola status nie wystarcza

screen.ontimer(callback, delay_ms) tylko PLANUJE przyszłe wywołanie — sam fakt, że status zmienione na PAUSEDnie usuwa już zaplanowanych callback z kolejki Tkinter: wciąż zostanie wywołany, po prostu nic pożytecznego nie zrobi, jeśli wewnątrz sprawdzić status. Prawie działa — ale nie do końca, a różnica ujawnia się tam, gdzie jest najtrudniej ją zauważyć: co jeśli wznowienie (Resume) stanie się PRZED wywołaniem połączenia Tik zaplanowany przed przerwą?

Prawdziwy błąd: Pause → Resume zanim stary tik się uruchomił
Bez niepełnosprawności pokolenia okazuje się, że to właśnie w momencie przerwy okazuje się: stary kleszcz nadal jest zaplanowany → Resume planuje SEKUNDĘ, jego własny tik → oba kiedyś zadziałają, każdy planuje kolejny dla siebie, → DWA niezależne łańcuchy tykają w grze jednocześnie, a wąż zaczyna poruszać się dwa razy częściej, niż powinien. Weryfikacja status is RUNNING w środku tyka nie łapie tego wyścigu: zanim stary tik w końcu działa, gra jest już RUNNING – test przechodzi, a tik planuje kontynuować, jakby nic się nie stało.

Dlatego toggle_pause() zwiększa self._generation przy KAŻDEJ zmianie — i przy ustawianiu na Pauza, a przy wznowieniu, nie tylko podczas restartu. Pauza jest paraliżująca zaplanowane tiknięcie natychmiast, w momencie naciskania Space, nie Polega na tym, że rozpozna siebie, gdy w końcu zacznie pracować. Odnowienie otrzymuje posiadanie nowej generacji i planowanie dokładnie jednego nowego odszycia – niezależnie od sieci istniał wcześniej, już nie żyje.

TIMER A zaplanowane
generation = 4
Space
PAUSE
generation: 4 → 5
Space, zanim TIMER A zadziałała
RESUME
generation: 5 → 6
TIMER B planowane (gen=6)
późno
TIMER A dzieła
jego generation = 4
4 ≠ 6 → ignorowany, nic nie planuje
punktualnie
TIMER B dzieła
jego generation = 6
6 = 6 → prawdziwy tik
planuje kolejny tick (gen=6)
Pauza i wznowienie rozpoczynają nowe pokolenie od razu, w momencie naciśnięcia Space — przeterminowane TIMER A dowiaduje się o tym przy pierwszym sprawdzeniu i nie planuje niczego więcej.
Praktyka: pause nie przesuwa tego stanu
Automatyczne sprawdzanie – game_tick(): funkcja tykania podczas PAUSED nie zmienia węża
Otwórz praktykę →