În acest articol, voi explica cum să creați un joc Snake folosind HTML, CSS și JavaScript.
Nu vom folosi biblioteci suplimentare; jocul va rula într-un browser. Crearea acestui joc este un exercițiu distractiv care vă ajută să vă întindeți și să vă exercitați mușchii care rezolvă problemele.
Cuprins
Schița proiectului
Snake este un joc simplu în care ghidezi mișcările unui șarpe către mâncare în timp ce eviți obstacolele. Când șarpele ajunge la hrană, o mănâncă și crește mai mult. Pe măsură ce jocul progresează, șarpele devine din ce în ce mai lung.
Șarpele nu ar trebui să se lovească de pereți sau de el însuși. Prin urmare, pe măsură ce jocul progresează, șarpele devine mai lung și devine din ce în ce mai greu de jucat.
Scopul acestui tutorial JavaScript Snake este de a construi jocul de mai jos:
Codul jocului este disponibil pe mine GitHub. O versiune live este găzduită Pagini GitHub.
Cerințe preliminare
Vom construi acest proiect folosind HTML, CSS și JavaScript. Vom scrie doar HTML și CSS de bază. Accentul nostru principal este pe JavaScript. Prin urmare, ar trebui să înțelegeți deja că trebuie urmat împreună cu acest tutorial JavaScript Snake. Dacă nu, vă recomand cu căldură să consultați articolul nostru despre cele mai bune locuri pentru a învăța JavaScript.
Veți avea nevoie și de un editor de cod în care să vă scrieți codul. În plus, veți avea nevoie de un browser, pe care probabil îl aveți dacă citiți acest lucru.
Configurarea Proiectului
Pentru început, să setăm fișierele de proiect. Într-un folder gol, creați un fișier index.html și adăugați următorul marcaj.
<!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>
Markupul de mai sus creează un ecran de bază „Game Over”. Vom comuta vizibilitatea acestui ecran folosind JavaScript. De asemenea, definește un element de pânză pe care vom desena labirintul, șarpele și mâncarea. Markupul conectează, de asemenea, foaia de stil și codul JavaScript.
Apoi, creați un fișier styles.css pentru stil. Adăugați următoarele stiluri la acesta.
* { 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; }
În setul de reguli „*”, vizam toate elementele și resetăm spațierea. De asemenea, setăm familia de fonturi pentru fiecare element și setăm dimensionarea elementelor la o metodă de dimensionare mai previzibilă numită border-box. Pentru corp, i-am setat înălțimea la înălțimea completă a feței de vizualizare și am aliniat toate elementele la centru. I-am dat și o culoare de fundal albastru.
În cele din urmă, am stilat ecranul „Game Over” pentru a-i oferi o înălțime și o lățime de 200, respectiv 500 de pixeli. I-am dat și o culoare de fundal magenta și un chenar negru. Am stabilit poziția sa la absolut, astfel încât să fie în afara fluxului normal de documente și aliniat la centrul ecranului. Apoi, i-am centrat conținutul. Îi setăm afișarea la niciunul, deci este ascuns în mod implicit.
Apoi, creați un fișier snake.js, pe care îl vom scrie în următoarele câteva secțiuni.
Crearea variabilelor globale
Următorul pas în acest tutorial JavaScript Snake este definirea unor variabile globale pe care le vom folosi. În fișierul snake.js, adăugați următoarele definiții de variabile în partea de sus:
// 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");
Aceste variabile stochează referințe la ecranul „Game Over” și elementele pânzei. Apoi, am creat un context, care va fi folosit pentru a desena pe pânză.
Apoi, adăugați aceste definiții variabile sub primul set.
// Maze definitions let gridSize = 400; let unitLength = 10;
Primul definește dimensiunea grilei în pixeli. Al doilea definește o unitate de lungime în joc. Această unitate de lungime va fi utilizată în mai multe locuri. De exemplu, îl vom folosi pentru a defini cât de groși sunt pereții labirintului, cât de gros este șarpele, înălțimea și lățimea hranei și treptele în care se mișcă șarpele.
Apoi, adăugați următoarele variabile de joc. Aceste variabile sunt folosite pentru a urmări starea jocului.
// Game play variables let snake = []; let foodPosition = { x: 0, y: 0 }; let direction = "right"; let collided = false;
Variabila șarpe ține evidența pozițiilor ocupate în prezent de șarpe. Șarpele cuprinde unități, iar fiecare unitate ocupă o poziție pe pânză. Poziția pe care o ocupă fiecare unitate este stocată în matricea șarpelor. Poziția va avea valorile x și y ca coordonate. Primul element din matrice reprezintă coada, în timp ce ultimul reprezintă capul.
Pe măsură ce șarpele se mișcă, vom împinge elemente până la capătul matricei. Acest lucru va muta capul înainte. De asemenea, vom elimina primul element sau coada din matrice, astfel încât lungimea să rămână aceeași.
Variabila de poziție a alimentelor stochează locația curentă a alimentelor folosind coordonatele x și y. Variabila de direcție stochează direcția în care se mișcă șarpele, în timp ce variabila ciocnită este o variabilă booleană marcată la adevărat atunci când a fost detectată o coliziune.
Declararea Funcțiilor
Întregul joc este împărțit în funcții, făcându-l mai ușor de scris și gestionat. În această secțiune, vom declara acele funcții și scopurile lor. Următoarele secțiuni vor defini funcțiile și vor discuta algoritmii acestora.
function setUp() {} function doesSnakeOccupyPosition(x, y) {} function checkForCollision() {} function generateFood() {} function move() {} function turn(newDirection) {} function onKeyDown(e) {} function gameLoop() {}
Pe scurt, funcția setUp setează jocul. Funcția checkForCollision verifică dacă șarpele s-a ciocnit cu un perete sau cu el însuși. Funcția doesSnakeOccupyPosition ia o poziție, definită de coordonatele x și y și verifică dacă vreo parte a corpului șarpelui se află în acea poziție. Acest lucru va fi util atunci când căutați o poziție liberă în care să adăugați mâncare.
Funcția de mutare mută șarpele în orice direcție îndreptată, în timp ce funcția de întoarcere schimbă acea direcție. Apoi, funcția onKeyDown va asculta apăsările de taste care sunt folosite pentru a schimba direcția. Funcția gameLoop va muta șarpele și va verifica dacă există coliziuni.
Definirea Funcțiilor
În această secțiune, vom defini funcțiile pe care le-am declarat mai devreme. Vom discuta, de asemenea, cum funcționează fiecare funcție. Va exista o scurtă descriere a funcției înainte de cod și comentarii pentru a explica rând cu rând acolo unde este necesar.
Funcția de configurare
Funcția de configurare va face 3 lucruri:
Prin urmare, codul pentru acesta va arăta astfel:
// 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();
doesSnakeOccupyPosition
Această funcție ia coordonatele x și y ca poziție. Apoi verifică existența unei astfel de poziții în corpul șarpelui. Folosește metoda JavaScript array find pentru a găsi o poziție cu coordonatele care se potrivesc.
function doesSnakeOccupyPosition(x, y) { return !!snake.find((position) => { return position.x == x && y == foodPosition.y; }); }
checkForCollision
Această funcție verifică dacă șarpele s-a ciocnit cu ceva și setează variabila ciocnită la adevărat. Vom începe prin a verifica dacă există coliziuni împotriva pereților din stânga și din dreapta, a pereților de sus și de jos și apoi de șarpele însuși.
Pentru a verifica coliziunile împotriva pereților din stânga și din dreapta, verificăm dacă coordonata x a capului șarpelui este mai mare decât gridSize sau mai mică decât 0. Pentru a verifica coliziunile împotriva pereților de sus și de jos, vom efectua aceeași verificare, dar cu coordonatele y.
În continuare, vom verifica dacă există ciocniri împotriva șarpelui însuși; vom verifica dacă vreo altă parte a corpului său ocupă poziția ocupată în prezent de cap. Combinând toate acestea, corpul pentru funcția checkForCllision ar trebui să arate astfel:
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; } }
generateFood
Funcția generateFood folosește o buclă do-while pentru a căuta o poziție pentru a plasa hrana care nu este ocupată de șarpe. Odată găsită, poziția alimentelor este înregistrată și desenată pe pânză. Codul pentru funcția generateFood ar trebui să arate astfel:
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); }
mișcare
Funcția de mutare începe prin a crea o copie a poziției capului șarpelui. Apoi, pe baza direcției curente, crește sau scade valoarea coordonatei x sau y a șarpelui. De exemplu, creșterea coordonatei x este echivalentă cu deplasarea spre dreapta.
Odată ce a fost făcut, împingem noua poziție headPosition în matricea șarpelui. De asemenea, desenăm noua poziție headPosition pe pânză.
Apoi, verificăm dacă șarpele a mâncat mâncare în acea mișcare. Facem acest lucru verificând dacă headPosition este egală cu foodPosition. Dacă șarpele a mâncat mâncare, numim funcția generateFood.
Dacă șarpele nu a mâncat mâncare, ștergem primul element al matricei de șarpe. Acest element reprezintă coada, iar îndepărtarea acestuia va menține lungimea șarpelui aceeași, dând în același timp iluzia de mișcare.
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); } }
întoarce
Ultima funcție majoră pe care o vom acoperi este funcția de întoarcere. Această funcție va lua o nouă direcție și va schimba variabila de direcție în acea nouă direcție. Cu toate acestea, șarpele se poate întoarce doar într-o direcție perpendiculară pe cea în care se deplasează în prezent.
Prin urmare, șarpele se poate întoarce la stânga sau la dreapta doar dacă se mișcă în sus sau în jos. În schimb, se poate întoarce în sus sau în jos doar dacă se deplasează la stânga sau la dreapta. Având în vedere aceste constrângeri, funcția de întoarcere arată astfel:
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
Funcția onKeyDown este un handler de evenimente care va apela funcția de întoarcere cu direcția corespunzătoare tastei săgeată care a fost apăsată. Prin urmare, funcția arată astfel:
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
Funcția gameLoop va fi apelată în mod regulat pentru a menține jocul să funcționeze. Această funcție va apela funcția de mutare și funcția checkForCollision. De asemenea, verifică dacă coliziunea este adevărată. Dacă da, oprește un temporizator de interval pe care îl folosim pentru a rula jocul și afișează ecranul „game over”. Funcția va arăta astfel:
function gameLoop() { move(); checkForCollision(); if (collided) { clearInterval(timer); gameOverScreen.style.display = "flex"; } }
Începând Jocul
Pentru a începe jocul, adăugați următoarele linii de cod:
setUp(); document.addEventListener("keydown", onKeyDown); let timer = setInterval(gameLoop, 200);
În primul rând, numim funcția setUp. Apoi, adăugăm ascultătorul de evenimente „keydown”. În cele din urmă, folosim funcția setInterval pentru a porni cronometrul.
Concluzie
În acest moment, fișierul dvs. JavaScript ar trebui să arate ca cel de pe mine GitHub. În cazul în care ceva nu funcționează, verificați de două ori cu repo. Apoi, poate doriți să aflați cum să creați un glisor de imagine în JavaScript.