Gra Szybkie Palce! Wyłonienie zwycięzcy

Tworzenie gry Szybkie Palce! wchodzi w decydującą fazę. W poprzednich postach zaprojektowaliśmy ją, stworzyliśmy logikę rozpoczynania gry i pojedynku graczy. W dzisiejszym poście zaimplementujemy funkcje, w których wyłonimy zwycięzcę.

Dla przypomnienia, nasz pomysł na grę wygląda następująco: dwóch graczy siada przy jednej klawiaturze i wchodzą na stronę naszej gry. Gracze będą musieli wykazać się refleksem i w odpowiednim momencie nacisnąć klawisz na klawiaturze komputera. Ten który zrobi to pierwszy zdobywa punkt. Ten który zdobędzie 3 punkty wygrywa grę. Moment naciśnięcia klawisza będzie wybierany losowo oraz dodatkowo zabezpieczymy się przed nieuczciwymi graczami, którzy mogliby chcieć bezmyślnie naciskać klawisz cały czas.

Gra będzie napisana głównie w języku JavaScript i zamieszczona na stronie HTML a do jej graficznego uatrakcyjnienia skorzystamy z języka styli CSS. Kod gry jest dostępny na stronie github.com. Cała gra znajduje się w jednym pliku index.html co ułatwi jej tworzenie.

Zachęcam do zapoznania się z poprzednimi postami w cyklu Szybkie Palce!

W grę można już pograć. Jest dostępna na stronie: Szybkie Palce!

Zmieniamy funkcję: KoniecRundy

Zgodnie z naszym projektem (link: Piszemy grę), gra polega na tym, aby gracze nacisnęli klawisz na klawiaturze komputera w odpowiednim momencie. Gracz, który zrobi to pierwszy zyskuje punkt. Gracz, który jako pierwszy zdobędzie 3 punkty wygrywa grę. Jeżeli gracz oszukuje (czyli szybko naciska swój klawisz licząc że będzie szybszy) zostaje zdyskwalifikowany i grę wygrywa przeciwnik.

W funkcji KoniecRundy zaimplementujemy prostą logikę sprawdzającą warunki kończące naszą grę. Jest ona wykonywana po każdej rundzie (a tych jest aż 3).

Pierwszym krokiem jest sprawdzenie, czy żaden z graczy nie oszukiwał. Jeżeli tak,  uruchamiamy funkcję Zakonczenie, która wyświetla komunikat końcowy. Instrukcja return powoduje wyjście z funkcji KoniecRundy bez uruchomienia ostatniej linijki w funkcji czyli RozpocznijRunde.

Podobnie w drugim kroku, w którym sprawdzamy warunek na zakończenie gry. Oba te sprawdzenia możemy rozbić na dwie oddzielne funkcje. Dzięki temu kod funkcji KoniecRundy jest bardziej przejrzysty a także posiadamy ładny podział odpowiedzialności za poszczególne rzeczy.

Funkcje CzyKtosOszukiwal oraz CzyKoniecGry

Poszczególne warunki kończące zaimplementowane są w dwóch funkcjach. Obie sprawdzają liczniki naciśnięć klawiszy, punkty graczy oraz limity. Obie też są funkcjami które zwracają wartość logiczną typu PRAWDA/FAŁSZ (ang. true/false).

Jeżeli jakiś gracz oszukiwał, funkcja CzyKtosOszukiwal zwraca true. Jeżeli nikt nie oszukiwał, zostanie zwrócona wartość false.  Podobnie dzieje się w funkcji CzyKoniecGry gdzie sprawdzamy kto faktycznie wygrał.

W przypadku gdy zostanie spełniony jeden z warunków w instrukcjach if (link: Instrukcje sterujące), zostaną uruchomione funkcje Oszust albo Zwyciezca. Wyświetlają one graczom odpowiednie komunikaty.

Testy i poprawa błędów

Po zaimplementowaniu logiki w funkcjach KoniecRundy, CzyKtosOszukiwal oraz CzyKoniecGry możemy już zagrać w grę Szybkie Palce! Jest to świetny moment, aby wyzwać kogoś na pojedynek, sprawdzić swój refleks i testować grę.

Programista tworząc wszystkie swoje programy musi (a przynajmniej powinien) testować ich działanie. Często okazuje się, że kod który napisaliśmy posiada pewne wady. Wynikające z błędnych założeń, błędów w samej implementacji czy innych czasami nieoczekiwanych zdarzeń. Testowanie aplikacji to złożone zagadnienie i kiedyś zajmiemy się nim dużo dokładniej.

Teraz skupmy się tylko na tak zwanym “przeklikaniu” się przez nasz program. Pora na uruchomienie gry.

Zachęcam Cię do wejścia na ten link Szybkie Palce! (wersja z błędami) i pogrania w wersję gry na tym etapie.

Po pewnym czasie okazuje się, że gracze, którzy mają podobny refleks i na przemian zdobywają punkty, dochodząc do ostatniego pojedynku i naciskając w praktycznie tym samym czasie na klawisze dostają komunikat nie o wygranej ale o oszustwie. Mimo, że nie oszukiwali.

Dzieje się tak dlatego, że został popełniony błąd podczas projektowania gry. W funkcji RozpocznijGre ustawiamy limitNacisniec na wartość równą liczbaRund (czyli na wartość 3). W sytuacji, gdy gracze mają remis po 3 naciśnięcia (jeden ma 2 punkty, drugi 1) i może to być ostatni pojedynek, obaj naciskają prawie równocześnie, ale tylko jednemu z nich przyznamy punkt. Obu jednak zwiększymy licznikNacisniec. Czyli obaj mają po 4 naciśnięcia. W funkcji KoniecRundy uruchamiamy CzyKtosOszukiwal i jeżeli licznikNacisniec > limitNacisniec to wykrywamy oszusta.

Pojedynek Wygrywa gracz nr Punkty gracza nr 1 Punkty gracza nr 2 Liczba naciśnięć gracza 1 Liczba naciśnięć gracza 2
1 1 1 0 1 1
2 2 1 1 2 2
3 1 2 1 3 3
4 2 2 2 4
4 (oszust)

Tabelka ze stanami dla 3 rund (przed poprawką)

Poprawka czyli tak zwany “bug fix”

Możemy to naprawić poprzez odpowiednie ustawienie zmiennej limitNacisniec. W naszej grze najbardziej wrażliwy na pomyłkę jest przypadek, który właśnie omówiliśmy. Chcemy, aby takie zachowanie graczy było normalne i nie powodowało wykrycia oszustwa. Czyli dla 3 rund, maksymalny dozwolony limit naciśnięć to 5. A gdybyśmy mieli 4 rundy? Wtedy limit wyniósłby 7. Wprowadzamy więc poprawkę dla zmiennej limitNacisniec.

diff_1

Pojedynek Wygrywa gracz nr Punkty gracza nr 1 Punkty gracza nr 2 Liczba naciśnięć gracza 1 Liczba naciśnięć gracza 2
1 1 1 0 1 1
2 2 1 1 2 2
3 1 2 1 3 3
4 2 2 2 4 4
5 1 3 2 5 5
6 2 3 3 6 6
7 1 4 (zwycięzca)
3 7 7

Tabelka ze stanami dla 4 rund (po poprawce). Wygrywa gracz nr 1.

Grywalność

W celu zwiększenia przyjemności z grania w Szybkie Palce! wprowadziłem parę poprawek. Po pierwsze, zmodyfikowałem wyświetlane komunikaty.

Uznałem także, że po rozpoczęciu gry, nie będzie możliwe ponowne kliknięcie w przycisk START, dlatego został on zablokowany. W tym celu powstała funkcja WylaczPrzyciskStart, która pobiera element ze strony HTML (mechanizm został opisany w Gra Szybkie Palce! Funkcja RozpocznijGre.). Mając w kodzie JavaScript obiekt przycisku ustawiamy mu atrybut disabled czyli wyłączony. Dzięki temu nie jest możliwe jego ponowne naciśnięcie.

Ustawienie atrybutu na obiekcie przycisku spowodowało, że przestało działać naciskanie klawiszy przez graczy. Przeglądarka nie uruchamiała funkcji NacisnietoPrzycisk, mimo że klawisze były naciskane.

Poprawka wymagała ustawienia focusa na obiekcie dziennikZdarzen, tam gdzie wyświetlane są komunikaty dla gracza. Grę testowałem w przeglądarce Firefox (wersja 59.0.3). Co ciekawe podczas testowania w przeglądarce Internet Explorer (wersja 11.431) taki problem nie wystąpił 🙂

Dlatego konieczne jest sprawdzanie naszego programu, w tym przypadku strony WWW, w wielu przeglądarkach, na wielu telefonach czy z różnymi ustawieniami. Ponieważ nie wiemy, jakiej przeglądarki czy na przykład telefonu używają nasi użytkownicy.

Poza tymi zmianami, element dziennikZdarzen został ustawiony w tryb tylko do odczytu (readonly), dzięki temu gracze nie mogą już pisać w tym tekstowym okienku.

Wprowadzone poprawki oraz ulepszenia sprawiły że całkiem przyjemnie gra się w naszą grę. Pozostaje jedynie sprawić by była atrakcyjna wizualnie.

Dźwięki w grze

Zanim jednak przejdziemy do poprawy wyglądu, dodajmy kilka dźwięków. Są one bardzo ważnym elementem każdej, nawet tak prostej gry jak nasza. Dodajmy więc różne dźwięki odgrywane w odpowiednich momentach.

Najbardziej charakterystycznymi momentami gdzie moglibyśmy dodać dźwięki to: rozpoczęcie gry, rozpoczęcie pojedynku oraz zakończenie gry. W naszym przypadku mamy dwa możliwe scenariusze: możemy wyłonić zwycięzcę lub oszusta. Dodamy więc 4 dźwięki.

Do odgrywania dźwięków w JavaScript służy obiekt Audio któremu podajemy ścieżkę do pliku z dźwiękiem. A następnie na takim obiekcie audio wywołujemy metodę play.

Dodaliśmy funkcję Dzwiek która przyjmuje nazwę dźwięku do odegrania. Nazwa ta odpowiada nazwie pliku w katalogu dzwieki a plik ma rozszerzenie mp3. Dzięki tak skonstruowanej funkcji możemy uruchamiać odgrywanie dźwięków bez potrzeby pamiętania o dokładnej ścieżce do pliku.

Pliki pobrałem z serwisu https://freesound.org/.

Różnica pomiędzy ścieżką względną i bezwzględną

Tworząc strony internetowe należy poznać różnicę pomiędzy ścieżką względną i bezwzględną do plików. Tak jak w funkcji Dzwiek, muszę wskazać lokalizację pliku do odegrania. Podałem tam ścieżkę względną, czyli na przykład: dzwieki/pojedynek.mp3. Ścieżka względna odnosi się do pliku uwzględniając katalog w którym obecnie znajduje się nasz program.

Struktura plików gry Szybkie Palce przedstawia się tak:

  • DrogaProgramisty
    • SzybkiePalce
      • index.html >> tu jest uruchomiona gra, czyli “znajdujemy się” w katalogu “Szybkie Palce”
      • dzwieki
        • oszust.mp3
        • pojedynek.mp3
        • start.mp3
        • zwyciezca.mp3

Czym więc jest ścieżka bezwzględna? Nie uwzględnia ona bieżącego katalogu w którym się znajdujemy. Wskazuje natomiast na pełną ścieżkę w naszym systemie, na naszym lokalnym dysku. Na przykład u mnie na komputerze ścieżka bezwzględna to np.: C:\Projekty\DrogaProgramisty\SzybkiePalce\dzwieki\pojedynek.mp3.

Tak jak wspomniałem ścieżka bezwzględna odpowiada strukturze plików na moim lokalnym komputerze. Strony internetowe są umieszczane na publicznych serwerach, które mają inną strukturę plików i nasza strona znajdzie się w innej lokalizacji. U mnie lokalnie ścieżki bezwzględne zadziałają, ale na serwerze już nie.

Obie ścieżki: względna i bezwzględna wskazują na ten sam plik. Po prostu inaczej się do niego odwołujemy. Najlepiej właśnie ścieżką względną.

Na koniec

Jesteśmy już coraz bliżej skończenia gry Szybkie Palce! Jest już w pełni grywalna, umiemy wyłonić zwycięzcę oraz oszusta. A dzięki dodanym dźwiękom gra się bardzo fajnie.

Jest dostępna pod linkiem: Szybkie Palce!

Następnym krokiem będzie uatrakcyjnienie wyglądu gry za pomocą CSS czyli Cascading Style Sheets.

1 reply

Trackbacks & Pingbacks

  1. […] Gra Szybkie Palce! Wyłonienie zwycięzcy […]

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *