Jak znaleźć i naprawić błędy w strzelance
Kompendium według objawu — plus szczegółowa analiza trzech najmniej zauważalnych błędów projektu.
W trakcie tego rozdziału natknęliśmy się już na ponad dwadzieścia sposobów przypadkowego zepsucia strzelanki – od starych, technik opartych na klatkach, po subtelne błędy z podwójnym punktowaniem. Tutaj są zebrane w Jedna książka referencyjna.
| Objaw | Przyczyna | Gdzie dowiedzieć się więcej |
|---|---|---|
| Prędkość statku zależy od FPS | Ruch jest ustawiony na piksele na klatkę, a nie px/s przez dt Funkcje | 21.11 |
| Zwolnione tempo w ogóle się nie porusza | Pozycja jest przechowywana tylko w Rect (liczbach całkowitych) – część ułamkowa jest tracona | 21.11 |
| Statek diagonalny jest szybszy niż linia prosta | Wektor kierunku nie jest znormalizowany | 21.11 |
| Statek wylatuje z pola gry | Zapomniano clamp po X i/lub Y w Player.move() | 21.11 |
| Statek przepływa pod HUD | Granice ruchu to całe okno, a nie self.playfield | 21.12 |
| Spacja nagrywa każdą renderowaną klatkę | Brak sprawdzenia fire_cooldown przed strzałem | 21.13 |
| Szybkostrzelność zależy od FPS | Interwał między ujęciami mierzony jest w klatkach (-= 1), a nie sekundach (-= dt) | 21.13 |
| Po długiej pauzie debuggera — salwa z dziesiątek kul | Interwał Shot jest wprowadzany po while zamiast if | 21.13 |
| Pociski lecą wiecznie, gra zwalnia coraz bardziej z każdą minutą | Bullet nie wycofuje się z pola gry | 21.4 (usuwanie pocisków zza ekranu) |
| Pojawienie się wrogów zależy od FPS | Timer pojawiania się wrogów liczy klatki, a nie sekundy | 21.14 |
| Po osłabieniu FPS planowani przeciwnicy „znikają” | Timer pojawiania się wrogów używa if zamiast while | 21.14 |
| Wróg pojawia się w połowie krawędzi ekranu | x_poyavleniya_vraga nie uwzględnia szerokości przeciwnika | 21.14 |
| Jeden wróg jest dwukrotnie oznaczany jako zniszczony | Lista zmienia się podczas brutalnej siły (jak w sekcji 21.6) | 21.15 |
| Jednemu wrogowi nalicza się punkty dwukrotnie | Wszystkie pary pocisków-przeciwników są liczone, a nie wiele unikalnych przeciwników | 21.15 |
| Trzech jednoczesnych wrogów odbiera trzy życia | Obrażenia są przypisywane na każdą kolizję zamiast raz na klatkę | 21.16 |
| Nietykalność tyka po zatrzymaniu | Aktualizacja timera nieśmiertelności poza update_playing() bez sprawdzania state | 21.16, 21.18 |
| Na ekranie Game Over statek wciąż się porusza | handle_input() nie sprawdza self.state przed obsługą kluczy | 21.18 |
| Po restarcie pozostają stare pociski lub przeciwnicy | start_new_game() nie czyści wszystkich grup sprite’ów | 21.19 |
| Po restarcie wróg pojawia się natychmiast | spawn_timer nie zostaje zresetowany do początkowego interwału | 21.19 |
| Animacja eksplozji „połyka” | Explosion.update() używa if zamiast while | 21.20 |
| Gra zwalnia przed każdym strzałem lub eksplozją | Obraz lub dźwięk jest ładowany wewnątrz pętli gry, a nie w AssetStore | 21.10, 21.21 |
| Gra zawiesza się bez karty dźwiękowej | Sound.play() jest wywoływany bez sprawdzania None | 21.21 |
| HUD zablokowane przez wroga lub eksplozję | Kolejność renderowania nie obejmuje HUD i nakłada się na ostatnie warstwy | 21.22 |
| rect i position z czasem „rozchodzą się” | Rect zaktualizowany bezpośrednio, a nie odbudowany od position | 21.11 |
| Test kolizji przechodzi i zawiesza się bez zmian kodu | Random jest używany bezpośrednio, bez przekazywania seed | 21.24 |
Wbudowany nakładka debugowania
Naciśnij F3włączyć lub wyłączyć nakładkę debugowania w finalnej grze.
On wyznacza czerwone granice Rect statek, wrogowie i kule, i niżej
pokazuje rzeczywistą FPS. W ten sposób można zobaczyć prawdziwe hitboxy podczas parsowania kolizji i
Rozróżnić spadki liczby klatek spowodowane błędem w obliczaniu ruchu przez
dt. Nakładka zmienia tylko renderowanie: stan świata gry,
Timery i kolizje pozostają takie same.
Przyjrzyjmy się bliżej trzem najbardziej niepozornym błędom
self.rect.x += self.speed * dt – Rect przechowuje tylko liczby całkowite, a ułamkowy przyrost jest zaokrąglany do zera na każdej klatce osobno. Dlaczego: zaokrąglenie odbywa się za każdym razem, a nie raz na końcu. Poprawka: kumulować ruch w self.position (Vector2, Rozdział 21.11), oraz rect.center z niego złożyć ponownie round() każda klatka. Zasada: pozycja zawsze jest float, Rect jest jej pochodną, nigdy odwrotnie.for pulya, vragi in collisions.items(): schet += sum(v.points for v in vragi) to suma wszystkich par pocisków-przeciwników. Dlaczego: jeśli dwie kule trafią tego samego wroga w jednej aktualizacji, zostanie ona uwzględniona w obu listach punktów. Poprawka: zbierać wszystkich trafionych wrogów set przed punktacją (Rozdział 21.15). Zasada: liczyć punkty za unikalnie zniszczone obiekty, a nie za liczbę kolizji.for vrag in hit: self.lives -= 1 - Obrażenia w cyklu od zderzających się wrogów. Dlaczego: cykl odpisuje życie każdego wroga, a nie samego samego zderzenia. Poprawka: sprawdzić „czy była przynajmniej jedna kolizja” Zasada: obrażenia mierzone są przez zdarzenie „dotknięte”