Codemas - 15. den

Včera jsme dokázali, že naše dárky padají. Máme sice zatím pouze jeden testovací, manuálně přidaný dárek, ale náš program je připravený pro libovolný počet dárků. Dnes bychom měli zařídit, aby dárky nepropadly mimo herní plochu, když dopadnou až dolů na zem.

Jak zjistíme, že je dárek až na zemi. Stejně, jako jsme zjišťovali, zda už jsou sáně u pravého okraje okraje obrazovky. Ověříme, zda dolní okraj dárku už není mimo herní plochu, tj. jeho souřadnice y + jeho výška není větší, než výška herní plochy.

Podívej se na nákres, hned ti to bude jasnější. Proměnná i je v tomto příkladu počitadlo cyklu, kterým postupně procházíme všechny dárky:

Codemas Souradnice Darek

Ještě než se pustíme do programování, pojďme vymyslet, co se má stát, když dárek dopadne až dolů. Nebudeme zatím řešit žádné trestné body nebo životy, bude nám stačit, že "rozbitý" dárek odstraníme ze hry.

Chceme-li dárek odstranit ze hry, musíme odstranit obrázek dárku z herní plochy a zároveň i herní objekt dárku z pole všech dárků.

Pojďme na to postupně. Logiku pro testování padajících dárků bychom klidně mohli přidat dovnitř funkce posunDarky, kde už je stejně jeden po druhém procházíme. Většinou ale v programování chceme, aby funkce byly krátké a plnily pouze jeden účel. Nechme tedy funkci posunDarky tak, jak je, a vytvořme novou funkci, kterou nazveme třeba otestujDarky, ve které budeme zjišťovat, zda dárek nedopadl na zem nebo ho nechytil robot.

Úplně na konec našeho programu přidej:

// funkce pro testování padajících dárků
// - dopadl dárek na zem?
// - chytil dárek robot?
function otestujDarky() {

}

I v této funkci budeme postupně procházet všechny dárky v poli, takže uvnitř funkce budeme cyklus. Ale tentokrát si musíme uvědomit, co uvnitř cyklu potřebujeme dělat. Když zjistíme, že dárek dopadl na zem, potřebujeme ho z pole odstranit.

Představme si, že máme takovouto situaci: v poli máme 4 dárky, jejich index jsou 0, 1, 2, a 3. Máme cyklus, který postupuje od 0 do 3. Jsme na dárku s indexem 1 (tj. druhý dárek). Zjistíme, že dárek dopadl na zem, takže ho musíme z pole odstranit. Odstraníme dárek z prostředku pole, následující položky v poli se posunou o jeden index dolů. Ten co měl index 2 má nyní index 1, z indexu 3 se stal index 2. Když se cyklus přesune na další index, přesune se na index 2. Na indexu 2 ale nyní leží původně čtvrtý dárek. Původní třetí dárek jsem tak úplně přeskočili, protože ten má nyní index 1 a ten už náš cyklus považuje za zkontrolovaný.

Jednoduše řečeno, nemůžeme si mazat dárky pod rukama ve frontě, kterou jsme ještě neprošli. Kdybychom ale v cyklu postupovali obráceně, od posledního dárku k prvnímu, nevadilo by nám, kdybychom dárek, na kterém aktuálně v cyklu stojíme, museli smazat. Opět by se sice přisunul zbytek pole na pozici smazaného dárku, ale všechno už by to byly dárky, které už jsme prošli, takže by nám změna indexů nevadila.

Cyklus pozpátku je stejný, jako ten "normální", jen jako výchozí hodnotu uvedeme index posledního dárku. Index posledního dárku v poli je délka pole darky.length - 1, protože když je v poli např. 5 dárků, má poslední index 4 (čísluje se od 0).

Při každém průchodu cyklu nebudeme počitadlo cyklu o 1 zvětšovat, ale naopak o 1 zmenšovat příkazem i--. A celý cyklus budeme provádět dokud bude počitadlo větší nebo rovno 0.

Do funkce otestujDarky tedy vložme tento cyklus:

// projdeme pozpátku všechny dárky v poli
for (let i = darky.length - 1; i >=0; i--) {

}

Uvnitř cyklu musíme otestovat, zda souřadnice našeho dárku už nejsou za hranicí obrazovky, jak jsem si ukázali na obrázku a kousek výše. Dovnitř cyklu přidejme podmínku:

// dopadl dárek na zem?
if (darky[i].y + darky[i].vyska > hra.vyska) {
      
}

Mazání HTML prvků

Jako první musíme smazat obrázek (značku <img>) z herní plochy. Odkaz na HTML prvek, který představuje náš dárek, máme uložený ve vlastnosti element herního objektu dárku. Prvek, na který máme odkaz, odstraníme velmi jednoduše, zavoláním metody prvek.remove(). V našem případě tedy uvnitř cyklu odstraníme obrázek pomocí darky[i].element.remove();

Doplňme odstranění dárku dovnitř podmínky, která testuje, zda dárek dopadl na zem:

// dopadl dárek na zem?
if (darky[i].y + darky[i].vyska > hra.vyska) {

  // odstraníme obrázek dárku z herní plochy
  darky[i].element.remove();

}

Když hru spustíme, teoreticky by měl dárek zmizet, až dopadne na zem. Ale nezmizí. Na něco jsme totiž zapomněli. Naši funkci otestujDarky musíme pravidelně volat. Ideálně hned potom, co všechny dárky posuneme o kousek dolů. Pojďme do funkce aktualizujHru (to je ta, která se spouští 50× za vteřinu) a na její konec přidejme volání funkce otestujDarky(). Celá funkce aktualizujHru tedy bude vypadat takto:

// funkce pro aktualizování polohy objektů na obrazovce
// spouští se 50× za vteřinu
function aktualizujHru() {
  // posuneme sáně
  posunSane();

  // posuneme padající dárky
  posunDarky();

  // otestujeme padající dárky
  otestujDarky();
}

Odstranění položky z pole

Když hru spustíme teď a počkáme, až dárek dopadne na zem, dárek zmizí a vypadá to, že jsme hotovi. Ale nejsme. Zatím jsme ze hry odstranili pouze obrázek dárku, musíme odstranit ještě herní objekt dárku z pole dárků.

Odstranění položky je jednoduché, slouží k tomu metoda pole splice, která má dva parametry - první je, od kolikátého indexu chceme v poli mazat, druhý je kolik položek chceme smazat. Takže kdybychom napsali darky.splice(3, 1), znamená to, že chceme smazat čtvrtý dárek v poli (mažeme od indexu 3, což je čtvrtý dárek, a mažeme 1 dárek).

My jsme uvnitř cyklu, kde index aktuálního dárku určuje počitadlo cyklu i, takže když budeme chtít mazat aktuální dárek, který právě testujeme, použijeme darky.splice(i, 1).

Přidejme tento krátký a jednoduchý příkaz do naší podmínky, která testuje, zda dárek dopadl na zem:

// dopadl dárek na zem?
if (darky[i].y + darky[i].vyska > hra.vyska) {

  // odstraníme obrázek dárku z herní plochy
  darky[i].element.remove();

  // smažeme herní objekt z pole dárků
  darky.splice(i, 1);

}

Jen pro jistotu si ukažme, jak nyní vypadá celá funkce otestujDarky:

// funkce pro testování padajících dárků
// - dopadl dárek na zem?
// - chytil dárek robot?
function otestujDarky() {
  // projdeme pozpátku všechny dárky v poli
  for (let i = darky.length - 1; i >=0; i--) {
    // dopadl dárek na zem?
    if (darky[i].y + darky[i].vyska > hra.vyska) {
      // odstraníme obrázek dárku z herní plochy
      darky[i].element.remove();

      // smažeme herní objekt z pole dárků
      darky.splice(i, 1);
    }
  }
}

Na závěr

Uff, dnes toho bylo zase hodně a možná jsme se u přemýšlení i trochu zapotili. Ale naše hra zdárně pokračujeme a musíme myslet hlavně na to, že zachraňujeme Vánoce pro děti, které by jinak nedostaly svoje dárky. Kvůli nim to přemýšlení asi čas od času vydržíme.

Ve hře zatím máme jeden manuálně přidaný dárek, který spadne a zmizí. Zítra hru upravíme tak, aby dárky začaly padat přímo ze saní v náhodných intervalech.

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.