Vysvětlení výukového programu hada JavaScriptu

V tomto článku vysvětlím, jak vytvořit hru Snake pomocí HTML, CSS a JavaScriptu.

Nebudeme používat další knihovny; hra poběží v prohlížeči. Vytvoření této hry je zábavné cvičení, které vám pomůže protáhnout a procvičit svaly, které řeší problémy.

Osnova projektu

Snake je jednoduchá hra, kde řídíte pohyby hada směrem k jídlu a zároveň uhýbáte překážkám. Když had dosáhne potravy, sežere ji a poroste déle. Jak hra postupuje, had je stále delší.

Had by neměl narazit do zdí nebo do sebe. Proto, jak hra postupuje, had se prodlužuje a je stále těžší hrát.

Cílem tohoto JavaScript Snake Tutoriálu je vytvořit níže uvedenou hru:

Kód ke hře je k dispozici na mém GitHub. Živá verze je hostována na Stránky GitHub.

Předpoklady

Tento projekt vytvoříme pomocí HTML, CSS a JavaScriptu. Budeme psát pouze základní HTML a CSS. Naším hlavním zaměřením je JavaScript. Proto byste již měli porozumět tomu, abyste jej následovali spolu s tímto výukovým programem hada JavaScript. Pokud ne, vřele doporučuji, abyste se podívali na náš článek o nejlepších místech, kde se můžete naučit JavaScript.

Budete také potřebovat editor kódu, do kterého zapíšete svůj kód. Kromě toho budete potřebovat prohlížeč, který pravděpodobně máte, pokud toto čtete.

Nastavení projektu

Pro začátek nastavíme soubory projektu. V prázdné složce vytvořte soubor index.html a přidejte následující označení.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="https://wilku.top/javascript-snake-tutorial-explained/./styles.css" />
    <title>Snake</title>
  </head>
  <body>
    <div id="game-over-screen">
      <h1>Game Over</h1>
    </div>
    <canvas id="canvas" width="420" height="420"> </canvas>
    <script src="./snake.js"></script>
  </body>
</html>

Výše uvedené označení vytváří základní obrazovku „Game Over“. Viditelnost této obrazovky přepneme pomocí JavaScriptu. Definuje také prvek plátna, na který nakreslíme bludiště, hada a jídlo. Označení také propojuje šablonu stylů a kód JavaScript.

Dále vytvořte soubor styles.css pro styl. Přidejte k tomu následující styly.

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Courier New', Courier, monospace;
}

body {
    height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background-color: #00FFFF;
}

#game-over-screen {
    background-color: #FF00FF;
    width: 500px;
    height: 200px;
    border: 5px solid black;
    position: absolute;
    align-items: center;
    justify-content: center;
    display: none;
}

V sadě pravidel ‚*‘ cílíme na všechny prvky a resetujeme mezery. Nastavili jsme také rodinu písem pro každý prvek a nastavili velikost prvků na předvídatelnější metodu velikosti zvanou border-box. U těla jsme nastavili jeho výšku na celou výšku výřezu a všechny položky zarovnali na střed. Dali jsme mu také modrou barvu pozadí.

Nakonec jsme stylizovali obrazovku „Game Over“ tak, aby měla výšku a šířku 200 a 500 pixelů. Dali jsme mu také purpurovou barvu pozadí a černý okraj. Nastavíme jeho polohu na absolutní, takže je mimo normální tok dokumentu a zarovná se na střed obrazovky. Poté jsme jeho obsah vycentrovali. Jeho zobrazení jsme nastavili na žádné, takže je ve výchozím nastavení skryté.

  Jak provést obnovení účtu Bethesda

Dále vytvořte soubor snake.js, který napíšeme v několika následujících částech.

Vytváření globálních proměnných

Dalším krokem v tomto tutoriálu JavaScript Snake je definování některých globálních proměnných, které budeme používat. V souboru snake.js přidejte na začátek následující definice proměnných:

// Creating references to HTML elements
let gameOverScreen = document.getElementById("game-over-screen");
let canvas = document.getElementById("canvas");

// Creating context which will be used to draw on canvas
let ctx = canvas.getContext("2d");

Tyto proměnné ukládají odkazy na obrazovku ‚Game Over‘ a prvky plátna. Dále jsme vytvořili kontext, který bude použit pro kreslení na plátno.

Dále přidejte tyto definice proměnných pod první sadu.

// Maze definitions
let gridSize = 400;
let unitLength = 10;

První definuje velikost mřížky v pixelech. Druhý definuje délku jednotky ve hře. Tato délka jednotky bude použita na několika místech. Použijeme ho například k definování, jak tlusté jsou stěny bludiště, jak tlustý je had, výšku a šířku jídla a přírůstky, ve kterých se had pohybuje.

Dále přidejte následující herní proměnné. Tyto proměnné slouží ke sledování stavu hry.

// Game play variables
let snake = [];
let foodPosition = { x: 0, y: 0 };
let direction = "right";
let collided = false;

Proměnná had sleduje pozice aktuálně obsazené hadem. Had se skládá z jednotek a každá jednotka zaujímá pozici na plátně. Pozice, kterou každá jednotka zaujímá, je uložena v poli hadů. Pozice bude mít hodnoty x a y jako své souřadnice. První prvek v poli představuje ocas, zatímco poslední představuje hlavu.

Jak se had pohybuje, posuneme prvky na konec pole. Tím se hlava posune dopředu. Z pole také odstraníme první prvek nebo konec, aby délka zůstala stejná.

Proměnná pozice jídla ukládá aktuální polohu jídla pomocí souřadnic x a y. Proměnná směru ukládá směr, kterým se had pohybuje, zatímco kolidovaná proměnná je booleovská proměnná označená jako true, když byla detekována kolize.

Deklarace funkcí

Celá hra je rozdělena do funkcí, což usnadňuje psaní a správu. V této části deklarujeme tyto funkce a jejich účely. V následujících částech budou definovány funkce a probrány jejich algoritmy.

function setUp() {}
function doesSnakeOccupyPosition(x, y) {}
function checkForCollision() {}
function generateFood() {}
function move() {}
function turn(newDirection) {}
function onKeyDown(e) {}
function gameLoop() {}

Stručně řečeno, funkce setUp nastaví hru. Funkce checkForCollision zkontroluje, zda se had srazil se zdí nebo do sebe. Funkce dosSnakeOccupyPosition zaujme polohu definovanou souřadnicemi x a y a zkontroluje, zda je nějaká část hadího těla v této poloze. To bude užitečné při hledání volné pozice pro přidání jídla.

Funkce pohybu pohybuje hadem v jakémkoli směru, na který ukazuje, zatímco funkce otáčení tento směr mění. Dále bude funkce onKeyDown naslouchat stisknutí kláves, které se používají ke změně směru. Funkce gameLoop bude pohybovat hadem a kontrolovat kolize.

Definování funkcí

V této části definujeme funkce, které jsme deklarovali dříve. Probereme také, jak jednotlivé funkce fungují. Před kódem bude stručný popis funkce a komentáře pro vysvětlení řádek po řádku v případě potřeby.

funkce nastavení

Funkce nastavení provede 3 věci:

  • Nakreslete okraje bludiště na plátno.
  • Nastavte hada přidáním jeho pozic do proměnné had a jeho nakreslením na plátno.
  • Vytvořte počáteční pozici jídla.
  • Proto bude kód vypadat takto:

      // Drawing borders on canvas
      // The canvas will be the size of the grid plus thickness of the two side border
      canvasSideLength = gridSize + unitLength * 2;
    
      // We draw a black square that covers the entire canvas
      ctx.fillRect(0, 0, canvasSideLength, canvasSideLength);
    
      // We erase the center of the black to create the game space
      // This leaves a black outline for the that represents the border
      ctx.clearRect(unitLength, unitLength, gridSize, gridSize);
    
      // Next, we will store the initial positions of the snake's head and tail
      // The initial length of the snake will be 60px or 6 units
    
      // The head of the snake will be 30 px or 3 units ahead of the midpoint
      const headPosition = Math.floor(gridSize / 2) + 30;
    
      // The tail of the snake will be 30 px or 3 units behind the midpoint
      const tailPosition = Math.floor(gridSize / 2) - 30;
    
      // Loop from tail to head in unitLength increments
      for (let i = tailPosition; i <= headPosition; i += unitLength) {
    
        // Store the position of the snake's body and drawing on the canvas
        snake.push({ x: i, y: Math.floor(gridSize / 2) });
    
        // Draw a rectangle at that position of unitLength * unitLength
        ctx.fillRect(x, y, unitLength, unitLength);
      }
    
      // Generate food
      generateFood();

    děláSnakeOccupyPosition

    Tato funkce bere souřadnice x a y jako polohu. Poté zkontroluje, zda taková pozice v těle hada existuje. K nalezení pozice se shodnými souřadnicemi používá metodu hledání pole JavaScriptu.

    function doesSnakeOccupyPosition(x, y) {
      return !!snake.find((position) => {
        return position.x == x && y == foodPosition.y;
      });
    }

    checkForCollision

    Tato funkce zkontroluje, zda se had s něčím nesrazil, a nastaví kolidovanou proměnnou na hodnotu true. Začneme tím, že zkontrolujeme kolize s levou a pravou stěnou, horní a spodní stěnou a poté s hadem samotným.

      Jak na to správně? [+5 Tools]

    Abychom zkontrolovali kolize s levou a pravou stěnou, zkontrolujeme, zda je souřadnice x hlavy hada větší než velikost mřížky nebo menší než 0. Pro kontrolu kolize s horní a spodní stěnou provedeme stejnou kontrolu, ale s y-ové souřadnice.

    Dále zkontrolujeme kolize se samotným hadem; zkontrolujeme, zda nějaká jiná část jeho těla zaujímá pozici, kterou aktuálně zaujímá hlava. Když to vše zkombinujeme, tělo funkce checkForCllision by mělo vypadat takto:

     function checkForCollision() {
      const headPosition = snake.slice(-1)[0];
      // Check for collisions against left and right walls
      if (headPosition.x < 0 || headPosition.x >= gridSize - 1) {
        collided = true;
      }
    
      // Check for collisions against top and bottom walls
      if (headPosition.y < 0 || headPosition.y >= gridSize - 1) {
        collided = true;
      }
    
      // Check for collisions against the snake itself
      const body = snake.slice(0, -2);
      if (
        body.find(
          (position) => position.x == headPosition.x && position.y == headPosition.y
        )
      ) {
        collided = true;
      }
    }

    generovat jídlo

    Funkce createFood využívá smyčku do-while k vyhledání pozice pro umístění jídla, které had neobsazuje. Po nalezení je poloha jídla zaznamenána a nakreslena na plátno. Kód pro funkci createFood by měl vypadat takto:

    function generateFood() {
      let x = 0,
        y = 0;
      do {
        x = Math.floor((Math.random() * gridSize) / 10) * 10;
        y = Math.floor((Math.random() * gridSize) / 10) * 10;
      } while (doesSnakeOccupyPosition(x, y));
    
      foodPosition = { x, y };
      ctx.fillRect(x, y, unitLength, unitLength);
    }

    hýbat se

    Funkce přesunu začíná vytvořením kopie pozice hlavy hada. Poté na základě aktuálního směru zvyšuje nebo snižuje hodnotu souřadnice x nebo y hada. Například zvětšení x souřadnice je ekvivalentní pohybu doprava.

      8 Proxy služeb ISP k použití právě teď

    Jakmile to uděláme, posuneme novou pozici headPosition do pole hadů. Na plátno také nakreslíme novou pozici hlavy.

    Dále zkontrolujeme, zda had při tomto pohybu snědl potravu. To provedeme kontrolou, zda se headPosition rovná foodPosition. Pokud had snědl potravu, zavoláme funkci createFood.

    Pokud had nesnědl jídlo, odstraníme první prvek pole hadů. Tento prvek představuje ocas a jeho odstraněním zůstane délka hada stejná a zároveň vznikne iluze pohybu.

    function move() {
      // Create a copy of the object representing the position of the head
      const headPosition = Object.assign({}, snake.slice(-1)[0]);
    
      switch (direction) {
        case "left":
          headPosition.x -= unitLength;
          break;
        case "right":
          headPosition.x += unitLength;
          break;
        case "up":
          headPosition.y -= unitLength;
          break;
        case "down":
          headPosition.y += unitLength;
      }
    
      // Add the new headPosition to the array
      snake.push(headPosition);
    
      ctx.fillRect(headPosition.x, headPosition.y, unitLength, unitLength);
    
      // Check if snake is eating
      const isEating =
        foodPosition.x == headPosition.x && foodPosition.y == headPosition.y;
    
      if (isEating) {
        // Generate new food position
        generateFood();
      } else {
        // Remove the tail if the snake is not eating
        tailPosition = snake.shift();
    
        // Remove tail from grid
        ctx.clearRect(tailPosition.x, tailPosition.y, unitLength, unitLength);
      }
    }

    otočit se

    Poslední hlavní funkcí, kterou se budeme zabývat, je funkce otočení. Tato funkce nabere nový směr a změní směrovou proměnnou na tento nový směr. Had se však může otočit pouze ve směru kolmém k tomu, kterým se právě pohybují.

    Proto se had může otočit doleva nebo doprava pouze při pohybu nahoru nebo dolů. Naopak se může otočit nahoru nebo dolů pouze při pohybu doleva nebo doprava. S ohledem na tato omezení funkce otočení vypadá takto:

    function turn(newDirection) {
      switch (newDirection) {
        case "left":
        case "right":
          // Only allow turning left or right if they were originally moving up or down
          if (direction == "up" || direction == "down") {
            direction = newDirection;
          }
          break;
        case "up":
        case "down":
          // Only allow turning up or down if they were originally moving left or right
          if (direction == "left" || direction == "right") {
            direction = newDirection;
          }
          break;
      }
    }

    onKeyDown

    Funkce onKeyDown je obsluha události, která zavolá funkci otočení ve směru odpovídajícím stisknuté šipce. Funkce tedy vypadá takto:

    function onKeyDown(e) {
      switch (e.key) {
        case "ArrowDown":
          turn("down");
          break;
        case "ArrowUp":
          turn("up");
          break;
        case "ArrowLeft":
          turn("left");
          break;
        case "ArrowRight":
          turn("right");
          break;
      }
    }

    gameLoop

    Funkce gameLoop bude pravidelně volána, aby hra běžela. Tato funkce zavolá funkci přesunu a funkci checkForCollision. Také kontroluje, zda je kolize pravdivá. Pokud ano, zastaví se intervalový časovač, který používáme ke spuštění hry, a zobrazí se obrazovka ‚konec hry‘. Funkce bude vypadat takto:

    function gameLoop() {
      move();
      checkForCollision();
    
      if (collided) {
        clearInterval(timer);
        gameOverScreen.style.display = "flex";
      }
    }

    Spuštění hry

    Chcete-li spustit hru, přidejte následující řádky kódu:

    setUp();
    document.addEventListener("keydown", onKeyDown);
    let timer = setInterval(gameLoop, 200);

    Nejprve zavoláme funkci setUp. Dále přidáme posluchač události ‚keydown‘. Nakonec použijeme funkci setInterval ke spuštění časovače.

    Závěr

    V tomto okamžiku by váš soubor JavaScript měl vypadat jako ten na mém GitHub. V případě, že něco nefunguje, znovu to zkontrolujte u repo. Dále se možná budete chtít naučit, jak vytvořit jezdec obrázku v JavaScriptu.