Codemas - 23. den

Včera naši hru upravili tak, aby skončila, když čas dojde na nulu. Zjistili jsme ale, že i když zobrazíme závěrečnou obrazovku, hra na pozadí se stále "hraje sama". I když je herní plocha skrytá, sáně stále jezdí, dárky stále padají, dokonce můžeme stále hýbat robotem, aniž bychom ho viděli. Stále se zvyšuje skóre, když dárek náhodou dopadne na robota.

Jednoduše - celá hra funguje dál, my jsme jen skryli herní plochu. Abychom hru opravdu regulérně a správně ukončili, musíme po sobě uklidit. Já vím, že předvánoční úklid nemá asi nikdo z nás rád, ale občas je prostě nutný.

Až budeme mít uklizeno, můžeme na závěrečné obrazovce zprovozni tlačítko Hrát znovu a naše hra bude hotová.

Co bude náš úklid zahrnovat?

  • Zastavíme časovač, který 50× za vteřinu spouští funkci aktualizovatHru.
  • Odstraníme dárky, které jsou stále ve hře, když čas doběhl na nulu.
  • Zrušíme posluchač události keydown, tj. přestaneme reagovat na stisk kláves.

Zrušení časovače setInterval

My jsme v našem programu používali pro vytvoření časovače zápis, kdy jsme napsali pouze setInterval(aktualizujHru, 20). Tak jsme sice vytvořili časovač, ale už s ním nemůžeme dále nic dělat.

Funkce setInterval ale má i návratovou hodnotu. Když si tuto hodnotu uložíme při vytvoření časovače do proměnné, můžeme s její pomocí později časovač zrušit, což je přesně to, co potřebujeme.

hra.casovac = setInterval(aktualizujHru, 20);

V našem objektu hra si vytvoříme vlastnosti casovac, kam si budeme ukládat odkaz na náš časovač. Ve výchozím stavu ji nastavíme na hodnotu null (v počítačové řeči to znamená něco jako nic). V souboru script.js najdi objekt hra a přidej do něj poslední řádek:

// objekt pro hru
let hra = {
  element: document.getElementById('hra'),
  sirka: 900,
  vyska: 600,
  dalsiDarek: 150,
  skore: 0,
  skoreElement: document.getElementById('pocet'),
  hudba: document.getElementById('hudba'),
  zvukNaraz: document.getElementById('zvuk-naraz'),
  zvukSebrano: document.getElementById('zvuk-sebrano'),
  uvod: document.getElementById('uvod'),
  tlacitkoStart: document.getElementById('start'),
  konec: document.getElementById('konec'),
  tlacitkoZnovu: document.getElementById('znovu'),
  vysledek: document.getElementById('vysledek'),
  cas: 0,
  casElement: document.getElementById('cas'),
  casovac: null
}

Když teď máme odkaz na časovače kam uložit, musíme v programu najít místo, kde časovač startujeme. Časovač, který se stará o spouštění funkce aktualizujHru 50× za vteřinu, vytváříme uvnitř funkce startHry.

Ve funkci startHry najdeme řádek, na kterém je napsáno setInterval(aktualizujHru, 20); a změníme ho na:

// nastartujeme časovač, který bude 50× za vteřinu posouvat sáně, dárky, apod.
hra.casovac = setInterval(aktualizujHru, 20);

Časovač můžeme v JavaScriptu zrušit pomocí funkce clearInterval(odkaz), do které uvnitř závorek předáme jako parametr proměnnou, která obsahuje odkaz na běžící časovač.

Všechen náš "úklid" ve hře bude probíhat na začátku funkce konecHry. Pojďme jako první zastavit náš časovač. Najděte v programu funkci konecHry a na její začátek přidejte:

// zastavíme časovač, který se 50× za vteřinu stará o běh hry
clearInterval(hra.casovac);

Zrušení posluchače události

Velmi podobně můžeme zrušit i posluchače události, je nemusíme nic ukládat do proměnné, abychom to později mohli zrušit. Naslouchat události keydown (tj. čekat na stisk klávesy) jsme začali uvnitř funkce startHry pomocí řádku:

document.addEventListener('keydown', priStiskuKlavesy);

Když chceme posluchač události zrušit, použijeme úplně stejný zápis, jen metodu addEventListener nahradíme metodou removeEventListener. Když jí předáme úplně stejné parametry, jaké jsme předávali při vytváření posluchače události, tak dojde k jeho zrušení.

Pojďme náš posluchač události také "uklidit". Najdeme znovu funkci konecHry a na její začátek, za řádek, který ruší časovač, přidáme:

// zrušíme posluchač události, který čeká na stisk klávesy
document.removeEventListener('keydown', priStiskuKlavesy);

Dva úkoly ze tří máme hotové, zbývá už je "uklidit" dárky na herní ploše. Vytvoříme si na to v programu novou funkci odeberDarky.

Uvnitř této funkce projdeme pomocí cyklu všechny dárky v seznamu (poli) a odebereme z HTML element (obrázek) každého jednotlivého dárku.

Po skončení cyklu ještě vymažeme celé pole dárků tím, že do proměnné vložíme prázdné pole.

Na konec souboru script.js přidejte funkci odeberDarky:

// odstraní všechny dárky z herní plochy při skončení hry
function odeberDarky() {
  // projdeme všechny dárky v seznamu
  for (let i = 0; i < darky.length; i++) {
    // a smažeme z herní plochy jejich obrázky
    darky[i].element.remove();
  }

  // vyprázdníme pole dárků
  darky = [];
}

Naši novou funkci musíme samozřejmě také zavolat. Najdeme dnes už naposledy funkci konecHry a na její začátek, ale za řádky, které "uklízí" časovač a posluchač události, přidáme:

// vymažeme dárky, které zůstaly na herní ploše
odeberDarky();

Celá funkce konecHry tedy vypadá takto:

// zobrazí závěrečnou obrazovku a vypíše do ní dosažené skóre
function konecHry() {

  // zastavíme časovač, který se 50× za vteřinu stará o běh hry
  clearInterval(hra.casovac);

  // zrušíme posluchač události, který čeká na stisk klávesy
  document.removeEventListener('keydown', priStiskuKlavesy);

  // vymažeme dárky, které zůstaly na herní ploše
  odeberDarky();

  // přepneme na závěrečnou obrazovku
  prepniObrazovku('konec');

  // podle dosaženého výsledku vypíšeme hlášku
  if (hra.skore === 0) {

    hra.vysledek.textContent = 'Bohužel jsi nechytil žádný dárek. Ale snaha byla.';

  } else if (hra.skore === 1) {

    hra.vysledek.textContent = 'Zachránil jsi pouze 1 dárek, ale i ta jedna rozzářená dětská očička za to určitě stojí.';

  } else if (hra.skore < 5) {

    hra.vysledek.textContent = 'Chytil jsi ' + hra.skore + ' dárky. Mohlo to být lepší, ale určitě to zkusíš znovu, že ano?';

  } else {

    hra.vysledek.textContent = 'Výborně, chytil jsi ' + hra.skore + ' dárků. Tolik dětí bude mít díky tobě radostné Vánoce.';

  }
}

Když nyní hra skončí, tak sice nevidíme herní plochu, ale alespoň poslechem se můžeme přesvědčit, že už se hra "nehraje sama", dárky přestaly padat. Máme uklizeno!

Zbývá nám už jen poslední věc - musíme zprovoznit tlačítko Hrát znovu. Jsem si jistý, že teď už sami moc dobře víte, jak na to.

V objektu hra jsme si už v některém z předchozích kroků návodu vytvořili na tlačítko odkaz, který se jmenuje tlacitkoZnovu. Jediné, co musíme udělat, je při startu hry na tlačítko přidat posluchač události click, který při kliknutí zavolá funkci startHry.

Najdeme funkci uvodHry, která se spustí na začátku programu po kompletním načtení celá stránky. Ve funkci už přidáváme událost na tlačítko Start hry na úvodní obrazovce. Uděláme to stejné, ale pro tlačítko na závěrečné obrazovce.

Do funkce uvodHry přidáme řádek:

// čekáme na kliknutí na tlačítku na záverečné obrazovce
hra.tlacitkoZnovu.addEventListener('click', startHry);

Celá funkce uvodHry tedy bude vypadat následovně:

// funkce pro zobrazení úvodu hry
function uvodHry() {
  // přepneme na úvodní obrazovku
  prepniObrazovku('uvod');

  // na tlačítku budeme čekat na kliknutí
  // při kliknutí zavoláme startHry
  hra.tlacitkoStart.addEventListener('click', startHry);

  // čekáme na kliknutí na tlačítku na záverečné obrazovce
  hra.tlacitkoZnovu.addEventListener('click', startHry);
}

Když hru spustíme a necháme doběhnout čas, můžeme si vyzkoušet, že tlačítko Hrát znovu funguje. Hra se znovu spustí, ale bohužel nás trápí ještě jeden nedostatek. V levém rohu máme nevynulované skóre z předchozí hry.

Vynulujeme počítadlo skóre v hra.skore a zároveň zapíšeme nulu i do textového v hlavičce hry, na který máme uložený odkaz v hra.skoreElement.

Skóre bychom měli nulovat při startu každé nové hry, takže najdeme v programu funkci startHry a hned na její začátek přidáme:

// na začátku hry vynulujeme skóre
hra.skore = 0;
hra.skoreElement.textContent = '0';

Na závěr

A je to, naše hra je hotová! Stihli jsme to úplně přesně. Ježíšek je bez sebe radostí, Vánoce jsou zachráněné. Teď už stačí jen hrát a zachránit co nejvíce dárků a udělat tak radost co nejvíce dětem.

Zítra je Štědrý den. Možná na nás pod kódovacím stromečkem bude čekat ještě jedno překvapení, takže když si najdeš chvilku, určitě si přijď vyloupnout i poslední čokoládičku z našeho adventního kalendáře :)

Pro případ, že se ti dnes něco nepovedlo, kompletní kód hry po dnešních úpravách si můžeš prohlédnout zde.

Máš-li dotazy, diskutuj u příspěvku pro dnešní den ve facebookové události. Těšíme se na tebe opět zítra.