Większość wolnego czasu zajmuje mi obecnie nauka do sesji (Ahh te uroki studiowania zaocznie – sesja w połowie lipca :D), dlatego znów krótki wpis. Będzie to kontynuacja ostatniego posta, w którym opisuje teorię javascript game loop’a. Przedstawię wam kolejny przykład game loop w javascripcie. Bardziej sensowny niż ten ostatni 😉
Odtworzyłem popularną grę wisielec. Gracz musi odgadnąć hasło, podając po jednej literce. Może pomylić się tylko pięć razy. Jeżeli odgadnie całe hasło – wygrywa. Jeżeli poda pięć nieprawidłowych liter – przegrywa.
W grę wisielec możecie zagrać tutaj.
Opiszę dokładnie skrypt JS. HTMLa zostawię wam do rozgryzienia. Nic nadzwyczajnego się tam nie dzieje 🙂 Do manipulowania elementami HTML użyłem popularnego frameworka jQuery.
Na samym początku deklaruję parę zmiennych. Są to dane naszej gry. Nie potrzebujemy ich wiele.
1 2 3 4 5 6 7 8 |
var gameFinished = false; var maxWrong = 5; var wrongGuesses = 0; var words = ["kot","pies"]; var theWord = ""; var soFar = []; var guesses = ""; var guessCond = true; |
ich zastosowanie opiszę w momencie kiedy pojawią się w kodzie.
Następną rzeczą, którą programuje jest logika gry. Czyli Game Loop:
1 2 3 4 5 6 7 |
setupGame(); while(!gameFinished) { playGame(); gameFinished = checkIfFinished(); render(); } endGame(); |
Powinno już wyglądać znajomo. Podręcznikowy przykład pętli gry 🙂 Najpierw mamy funkcję setupGame, która ustawi nam wszystko, co potrzebujemy. Następnie rozpoczyna się główna pętla. Trwa ona dopóki zmienna gameFinished równa jest false.
Wewnątrz pętli najpierw wywoływana jest funkcja playGame, która odpowiada za przyjęcie danych od gracza, zapisanie ich w programie i odpowiednią reakcje na te dane. Jest to najważniejsza funkcja w tym skrypcie.
Następnie mamy funkcje checkIfFinished, która sprawdza czy gra została zakończona (słowo odgadnięte, lub liczba błędnych odpowiedzi równa jest 5). W zależności od tego co wyszło, funkcja zwraca true lub false.
Na końcu pętli mamy funkcję render, która przedstawia graczowi wyniki jego działań.
Jeżeli program wyjdzie z pętli (gameFinished będzie równe true), odpalona zostanie funkcja endGame();
Przyjrzyjmy się teraz funkcjom. najpierw funkcja setupGame:
1 2 3 4 5 6 7 8 9 10 11 |
function setupGame(){ var numWords = words.length; var wordNum = Math.floor(Math.random()*numWords); theWord = words[wordNum]; theWord = theWord.toUpperCase(); for(var i = 0;i<theWord.length;i++){ soFar[i] = "-"; } jQuery("#guess").append("<span>"+soFar.join("")+"</span>"); jQuery("#lives").append("<span>"+(maxWrong-wrongGuesses)+"</span>"); } |
Funkcja ta wybiera losowo jedno słowo z tablicy words. Ustawiamy wszystkie litery w słowie na wielkie litery. Dzięki temu później będzie nam łatwo porównywać je z propozycjami gracza.
Tworzymy również zawartość zmiennej soFar. Będzie to tablica, składająca się z tylu myślników, ile liter ma nasze wybrane słowo. Użyjemy tej zmiennej do wyświetlenia graczowi, co już odgadł.
Następnie robimy dokładnie to, używając jQuery. Wyświetlamy również pozostałą ilość prób (otrzymujemy tę liczbę poprzez odjęcie liczby złych prób od maksymalnej liczby złych prób).
Gra ustawiona. Przejdźmy do serca programu, czyli funkcji playGame.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
function playGame(){ var guess = ""; while(guessCond){ guess = prompt("podaj jedna litere"); if(guess == null) { continue; }; if(guess.length!=1) { alert("podaje JEDNA litere!") continue; }; if(guesses.indexOf(guess.toUpperCase()) > -1 ){ alert("podales juz '" +guess+"'") continue; }; guessCond = false; } guess = guess.toUpperCase(); guessIndex = theWord.indexOf(guess); if(guessIndex > -1) { soFar[guessIndex] = theWord[guessIndex] } else { wrongGuesses++; } guesses+=guess; guessCond = true; } |
Pierwsza rzecz, ustawiamy nową zmienna guess. Będzie ona przechowywała propozycje gracza. Następnie pobieramy od gracza propozycję literki poprzez prompt (bardzo wyszukane rozwiązanie, ale stawiam tu na prostote, to ma byc przykład logiki 🙂 ).
Prompt znajduje się wewnątrz swojej własnej pętli, ponieważ chcemy upewnić się, że gracz poda odpowiednią propozycje. Pętla będzie powtarzać się dopóki propozycja gracza nie będzie akceptowalna.
Postawiłem tutaj na przejrzystość kodu i zamiast upychać wszystkie warunki przy while’u, użyłem zmiennej pomocniczej. guessCond jest domyślnie ustawione na true, a na końcu pętli zmieniam ją na false. Jednak aby dojść do końca, program musi przejść przez warunki w postaci ifów. Jeśli któregoś nie przejdzie, wywołana jest instrukcja continue, która przerywa aktualny obieg pętli i rozpoczyna kolejny. Dodatkowym atutem tego rozwiązania jest to, że mogę powiedzieć graczowi, zrobił nie tak, używając alerta.
Zerknijmy więc na te warunki. Pierwszy to zabezpieczenie przed domyślnie pojawiającym się w prompcie przyciskiem „cancel”. Jeśli gracz go użyje, gra się zakończy. Nie chcemy tego. Chcemy go zmusić do grania w naszą grę 🙂
Drugi warunek sprawdza czy podana przez gracza propozycja to na pewno tylko jedna litera. Jeżeli nie, prompt pojawi się znowu.
Ostatni warunek jest dość ważny. Używając zmiennej guesses, sprawdza czy gracz nie podał już wcześniej takiej litery. Jeżeli tak, prompt pojawi się znów 🙂
Oczywiście można byłoby wrzucić tu jeszcze parę warunków, natomiast ja zatrzymam się na tym. Jeżeli gracz chce tracić szanse na wpisywanie liczb lub znaków specjalnych, jego problem 🙂
Dobra, co się dzieje dalej w funkcji playGame.
Propozycja gracza jest zamieniana na wielka literę. Następnie sprawdzamy, na jakiej pozycji znajduje się podana przez gracza litera w haśle, które trzeba odgadnąć. Jeżeli jest większa od -1, oznacza, że gracz odgadł literę. Aktualizujemy zawartość tablicy soFar. Jeżeli pozycja w haśle proponowanej przez gracza litery jest mniejsza lub równa -1, oznacza to, że nie trafił. Podnosimy liczbę złych odpowiedzi.
Na końcu dopisujemy propozycje gracza do użytych liter i ustawiamy zmienna guessCond spowrotem na true.
Kolejna funkcja to checkIfFinished
1 2 3 4 5 6 7 8 9 |
function checkIfFinished() { if(soFar.join("") === theWord) { return true; } else if (maxWrong-wrongGuesses == 0) { return true; } else { return false; } } |
Bardzo prosta funkcja. Jeżeli połączone litery z tablicy są równe słowu, które trzeba odgadnąć, funkcja zwraca true. Zwraca true, również w wypadku, gdy graczowi zabrakło już prób. Te dwie opcje oznaczają, że gra została zakończona. W przeciwnym wypadku, game loop nie jest przerwany i gra trwa nadal.
Dwie ostatnie funkcje to endGame oraz render.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function endGame() { if(soFar.join("") === theWord) { jQuery("#guesses").html("Gratulacje odgadles Slowo! :D"); } else { jQuery("#guesses").html("Niestety, nie odgadles slowa :("); } } function render() { jQuery("#guess").html("Slowo do odganiecia: <span>"+soFar.join("")+"</span>"); jQuery("#lives").html("zostalo prob: <span>"+(maxWrong-wrongGuesses)+"</span>"); jQuery("#guesses").html("wykorzystane litery: "+guesses); } |
Obie nie robią nic specjalnego. Ich główna rola to aktualizacja warstwy widoku dla gracza. endGame wyświetla odpowiedni komunikat po zakończeniu gry, a render aktualizuje widok, o użyte litery, oraz aktualny stan hasła.