If you're seeing this message, it means we're having trouble loading external resources on our website.

Pokud používáš webový filtr, ujisti se, že domény: *.kastatic.org and *.kasandbox.org jsou vyloučeny z filtrování.

Hlavní obsah

Pexeso: Otáčení kartiček

V tomto bodě už máme vytvořenou mřížku kartiček, které můžeme zobrazit lícem nahoru nebo dolů. Nemáme však žádný způsob, jak hrát samotnou hru. Zde je připomenutí toho, jak hra funguje:
  • Když hra začne, všechny kartičky budou otočeny směrem dolů.
  • Hráč pak otočí dvě karty, vybere je kliknutím na ně.
  • Pokud mají obě karty stejný obrázek, zůstanou lícem nahoru. Pokud ne, měly by se po krátké prodlevě opět otočit dolů.

Otáčení kartiček kliknutím

Momentálně máme program, který nakreslí mřížku kartiček a pak již nic dalšího nekreslí. Časem bychom ale mohli chtít, aby náš program dokázal nakreslit různé věci - začne kreslením karet otočených směrem dolů, ale pak zobrazí kliknuté kartičky a pokud se hráči bude dařit (držíme prsty!), zobrazí se i vítězná obrazovka.
Pojďme tedy přesunout celý náš kód kreslení do ProcessingJS funkce draw. Počítač bude průběžně volat draw() během celého programu, takže kartičky budou neustále kresleny podle toho, zda jsou otočeny směrem nahoru nebo dolů:
draw = function() {
    background(255, 255, 255);
    for (var i = 0; i < tiles.length; i++) {
        tiles[i].draw();
    }
};
Pojďme otočit některé z kartiček směrem nahoru! Pro otočení kartičky na ni musí hráč kliknout. Chceme-li reagovat na kliknutí v ProcessingJS programech, musíme definovat funkci mouseClicked, jejíž kód pak počítač spustí pokaždé, když dojde ke kliknutí myši.
mouseClicked = function() {
  // reakce na kliknutí
};
Když náš program zaznamená, že hráč někde klikl, chceme zjistit, zda klikl na kartičku. Zjistíme to pomocí mouseX a mouseY. Začneme přidáním metody isUnderMouse do Tile, která vrací true, pokud se dané x a y nachází v oblasti kartičky.
Dle našeho způsobu nakreslení karet odpovídá x a y souřadnicím levému hornímu rohu karty, takže bychom měli vrátit "true" pouze v případě, že se dané x nachází mezi this.x a this.x + this.size, a pokud je dané y mezi this.y a this.y + this.size:
Tile.prototype.isUnderMouse = function(x, y) {
    return x >= this.x && x <= this.x + this.size  &&
        y >= this.y && y <= this.y + this.size;
};
Nyní, když máme tuto metodu, můžeme použít cyklus for v mouseClicked k ověření toho, zda je každá kartička pod mouseX a mouseY. Pokud ano, nastavíme vlastnost karty isFaceUp na true:
mouseClicked = function() {
  for (var i = 0; i < tiles.length; i++) {
    if (tiles[i].isUnderMouse(mouseX, mouseY)) {
      tiles[i].isFaceUp = true;
    }
  }
};
Zde je ukázka toho, jak kód vypadá. Klikni na několik kartiček a uvidíš, co se stane:

Počet otočení

Všímáš si něčeho divného? Zavedli jsme jeden aspekt hry, a to ten, že hráč je schopen otočit kartičky, ale chybí nám důležité omezení: hráč by neměl být schopen otočit více než dvě karty najednou.
Budeme muset nějak zaznamenávat počet otočených karet. Jednoduchým způsobem by bylo použít globální proměnnou numFlipped, kterou zvýšíme pokaždé, když hráč otočí kartu tváří nahoru. Kartu otočíme pouze v případě, že numFlipped je menší než 2 a karta ještě nebyla otočena:
var numFlipped = 0;
mouseClicked = function() {
    for (var i = 0; i < tiles.length; i++) {
        var tile = tiles[i];
        if (tiles.isUnderMouse(mouseX, mouseY)) {
            if (numFlipped < 2 && !tile.isFaceUp) { 
              tile.isFaceUp = true;
              numFlipped++;
            }
        }
    }
};

Otáčení karet s prodlevou

Dobře, naše logika pro otáčení dvou karet je kompletní. Co bude dál? Znovu si připomeňme pravidla hry:
Pokud mají dvě karty stejný obrázek, zůstanou otočeny vzhůru. Jinak se karty po určitém časovém úseku otočí zpět.
Nejprve zavedeme druhou část, která automaticky otáčí kartičky zpět, protože bude těžké otestovat první část, pokud nemůžeme snadno hledat nové dvojice.
Karty otočíme tak, že nastaveníme isFaceUp na false. Ale jak to uděláme po nějaké časové prodlevě? Každý jazyk a prostředí má odlišný přístup k vykonání kódu s prodlevou a my musíme zjistit, jak se to dělá v ProcessingJS. Potřebujeme nějaký způsob pro sledování času - zda uplynul daný úsek času - a způsob pro volání kódu po uplynutí časového úseku. Zde je naše doporučení jak na to:
  • Vytvoříme globální proměnnou s názvem delayStartFC, s počátkem na nule (null).
  • Ve funkci mouseClicked, po otočení druhé karty, uložíme aktuální hodnotu frameCount do delayStartFC. Tato proměnná nám říká, kolik snímků uplynulo od doby, kdy program začal běžet, a je jedním ze způsobů, jak pracovat v našich programech s časem.
  • V rámci funkce draw ověřujeme, zda je nová hodnota frameCount výrazně vyšší než ta stará, a pokud ano, otočíme všechny karty zpět a nastavíme numFlipped na 0. Také resetujeme delayStartFC na null.
Je to celkem pěkné řešení, které nevyžaduje mnoho kódu k implementaci. V rámci optimalizace výkonu můžeme použít funkce loop a noLoop k tomu, abychom se ujistili, že kód draw je volán pouze tehdy, když dochází k prodlevě. Zde to máš pohromadě:
var numFlipped = 0;
var delayStartFC = null;

mouseClicked = function() {
  for (var i = 0; i < tiles.length; i++) {
    var tile = tiles[i];
    if (tile.isUnderMouse(mouseX, mouseY)) {
      if (numFlipped < 2 && !tile.isFaceUp) {
        tile.isFaceUp = true;
        numFlipped++;
        if (numFlipped === 2) {
          delayStartFC = frameCount;
        }
        loop();
      } 
    }
  }
};

draw = function() {
  if (delayStartFC &&
     (frameCount - delayStartFC) > 30) {
    for (var i = 0; i < tiles.length; i++) {
      tiles[i].isFaceUp = false;
    }
    numFlipped = 0;
    delayStartFC = null;
    noLoop();
  }

  background(255, 255, 255);
  for (var i = 0; i < tiles.length; i++) {
    tiles[i].draw();
  }
};
Zkus otočit několik kartiček níže - vypadá to hezky, když se karty automaticky otáčí zpět, že ano? Pro lepší pochopení kódu zkus upravit, jak dlouho trvá prodleva a kolik karet musí být otočeno před započetím prodlevy.

Nalézání párů

Pokud se ti výše povedlo najít libovolný pár kartiček, pravděpodobně tě moc nepotěšilo, že se otočily zpět, přestože se ti povedlo najít pár! Takže nyní nastal čas zavést toto pravidlo hry:
Pokud se dvě karty shodují, pak by měly zůstat otočeny.
To znamená, že bychom měli kontrolovat párování karet kdykoliv jsou 2 otočeny a před nastavením prodlevy. V pseudokódu by to vypadalo následovně:
pokud jsou dvě karty otočeny:
    pokud má první karta stejný obrázek jako druhá karta:
       karty zůstanou otočeny
V našem kódu již kontrolujeme, zda máme otočeny dvě karty (numFlipped === 2), jak tedy zkontrolujeme, zda mají karty shodný obrázek? Nejdříve potřebujeme způsob, jak získat přístup ke dvěma otočeným kartám. Jak na to?
Mohli bychom pokaždé iterovat v rámci našeho pole, najít všechny karty s isFaceUp nastaveno na "true", a pak je uložit do pole.
Tady je zkratka: pro snadný přístup stačí při každém otočení karet je uložit do pole. Tímto způsobem nebudeme muset iterovat přes celé pole tiles pokaždé, když hráč otočí kartu.
Jako první krok můžeme nahradit numFlipped polem a pak použít flippedTiles.length všude, kde jsme předtím používali numFlipped. Naše funkce mouseClicked vypadá následovně:
var flippedTiles = [];
var delayStartFC = null;

mouseClicked = function() {
  for (var i = 0; i < tiles.length; i++) {
    var tile = tiles[i];
    if (tile.isUnderMouse(mouseX, mouseY)) {
      if (flippedTiles.length < 2 && !tile.isFaceUp) {
        tile.isFaceUp = true;
        flippedTiles.push(tile);
        if (flippedTiles.length === 2) {
          delayStartFC = frameCount;
          loop();
        }
      } 
    }
  }
};
Nyní musíme zjistit, zda mají dvě karty v poli flippedTiles skutečně stejný obrázek. Co za vlastnost je face? Je to objekt - a "face" odpovídajících kartiček by měl být ten samý objekt, jako když proměnná obou ukazuje na stejné místo v paměti počítače. Je to kvůli tomu, že jsme vytvořili každý objekt obrázků pouze jednou (např. getImage("avatars/old-spice-man")) a pak jsme ten samý objekt předali do pole s obrázky dvakrát:
var face = possibleFaces[randomInd];
selected.push(face);
selected.push(face);
Minimálně v JavaScriptu operátor rovnosti vrátí hodnotu true, pokud je použita na dvou proměnných, které odkazují k objektům, a obě tyto proměnné odkazují k tomu samému objektu v paměti. To znamená, že naše kontrola může být jednoduchá - stačí použít operátora rovnosti pro vlastnost face každé karty:
if (flippedTiles[0].face === flippedTiles[1].face) {
  ...
}
Teď, když už dokážeme rozeznat spárované karty, potřebujeme je udržet otočené. V současné podobě by se po prodlevě všechny obrátily. V tomto případě lze jednoduše nepoužít animaci, ale pamatuj, k animaci dojde později - takže se na ni nemůžeme spolehnout.
Namísto toho potřebujeme způsob, jak poznat následující: "Když začneme otáčet všechny karty zpátky, neměli bychom otočit tyhle." Zní to jako vhodná chvíle pro použití vlastnosti boolean! Přidejme proto do konstruktoru Tile vlastnost isMatch a pak nastavme isMatch na true pouze uvnitř bloku if
if (flippedTiles[0].face === flippedTiles[1].face) {
  flippedTiles[0].isMatch = true;
  flippedTiles[1].isMatch = true;
}
Nyní můžeme díky této vlastnosti rozhodnout, zda karty po prodlevě obrátit či nikoliv.
for (var i = 0; i < tiles.length; i++) {
  var tile = tiles[i];
  if (!tile.isMatch) {
    tile.isFaceUp = false;
  }
}
Pohraj si s kódem níže! Když najdeš dva odpovídající páry, měly by zůstat otočeny i po prodlevě (a po všech budoucích otočeních):

Chceš se zapojit do diskuze?

Zatím žádné příspěvky.
Umíš anglicky? Kliknutím zobrazíš diskuzi anglické verze Khan Academy.