├── assets └── audio.mp3 ├── css └── style.css ├── index.html └── js └── script.js /assets/audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manualdodev/snake-game/ea197dc70519aa12922c6011caf4ef31db5d63f9/assets/audio.mp3 -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800&display=swap"); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: "Poppings", sans-serif; 8 | } 9 | 10 | body { 11 | background-color: #191919; 12 | display: flex; 13 | min-height: 100vh; 14 | align-items: center; 15 | justify-content: center; 16 | flex-direction: column; 17 | color: white; 18 | } 19 | 20 | canvas { 21 | background-color: #111; 22 | } 23 | 24 | .score { 25 | display: flex; 26 | flex-direction: column; 27 | align-items: center; 28 | font-size: 1.8rem; 29 | } 30 | 31 | .score--value { 32 | font-weight: 700; 33 | font-size: 4.5rem; 34 | display: block; 35 | margin-top: -10px; 36 | } 37 | 38 | .menu-screen { 39 | position: absolute; 40 | display: none; 41 | flex-direction: column; 42 | align-items: center; 43 | z-index: 1; 44 | } 45 | 46 | .game-over { 47 | text-transform: uppercase; 48 | font-weight: 700; 49 | font-size: 3rem; 50 | } 51 | 52 | .final-score { 53 | font-weight: 500; 54 | font-size: 1.5rem; 55 | } 56 | 57 | .btn-play { 58 | border: none; 59 | border-radius: 100px; 60 | padding: 10px 15px 10px 12px; 61 | font-size: 1rem; 62 | font-weight: 600; 63 | color: #333; 64 | margin-top: 40px; 65 | display: flex; 66 | align-items: center; 67 | justify-content: center; 68 | gap: 5px; 69 | cursor: pointer; 70 | } 71 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Snake Game 12 | 13 | 14 |
score: 00
15 | 16 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /js/script.js: -------------------------------------------------------------------------------- 1 | const canvas = document.querySelector("canvas") 2 | const ctx = canvas.getContext("2d") 3 | 4 | const score = document.querySelector(".score--value") 5 | const finalScore = document.querySelector(".final-score > span") 6 | const menu = document.querySelector(".menu-screen") 7 | const buttonPlay = document.querySelector(".btn-play") 8 | 9 | const audio = new Audio("../assets/audio.mp3") 10 | 11 | const size = 30 12 | 13 | const initialPosition = { x: 270, y: 240 } 14 | 15 | let snake = [initialPosition] 16 | 17 | const incrementScore = () => { 18 | score.innerText = +score.innerText + 10 19 | } 20 | 21 | const randomNumber = (min, max) => { 22 | return Math.round(Math.random() * (max - min) + min) 23 | } 24 | 25 | const randomPosition = () => { 26 | const number = randomNumber(0, canvas.width - size) 27 | return Math.round(number / 30) * 30 28 | } 29 | 30 | const randomColor = () => { 31 | const red = randomNumber(0, 255) 32 | const green = randomNumber(0, 255) 33 | const blue = randomNumber(0, 255) 34 | 35 | return `rgb(${red}, ${green}, ${blue})` 36 | } 37 | 38 | const food = { 39 | x: randomPosition(), 40 | y: randomPosition(), 41 | color: randomColor() 42 | } 43 | 44 | let direction, loopId 45 | 46 | const drawFood = () => { 47 | const { x, y, color } = food 48 | 49 | ctx.shadowColor = color 50 | ctx.shadowBlur = 6 51 | ctx.fillStyle = color 52 | ctx.fillRect(x, y, size, size) 53 | ctx.shadowBlur = 0 54 | } 55 | 56 | const drawSnake = () => { 57 | ctx.fillStyle = "#ddd" 58 | 59 | snake.forEach((position, index) => { 60 | if (index == snake.length - 1) { 61 | ctx.fillStyle = "white" 62 | } 63 | 64 | ctx.fillRect(position.x, position.y, size, size) 65 | }) 66 | } 67 | 68 | const moveSnake = () => { 69 | if (!direction) return 70 | 71 | const head = snake[snake.length - 1] 72 | 73 | if (direction == "right") { 74 | snake.push({ x: head.x + size, y: head.y }) 75 | } 76 | 77 | if (direction == "left") { 78 | snake.push({ x: head.x - size, y: head.y }) 79 | } 80 | 81 | if (direction == "down") { 82 | snake.push({ x: head.x, y: head.y + size }) 83 | } 84 | 85 | if (direction == "up") { 86 | snake.push({ x: head.x, y: head.y - size }) 87 | } 88 | 89 | snake.shift() 90 | } 91 | 92 | const drawGrid = () => { 93 | ctx.lineWidth = 1 94 | ctx.strokeStyle = "#191919" 95 | 96 | for (let i = 30; i < canvas.width; i += 30) { 97 | ctx.beginPath() 98 | ctx.lineTo(i, 0) 99 | ctx.lineTo(i, 600) 100 | ctx.stroke() 101 | 102 | ctx.beginPath() 103 | ctx.lineTo(0, i) 104 | ctx.lineTo(600, i) 105 | ctx.stroke() 106 | } 107 | } 108 | 109 | const chackEat = () => { 110 | const head = snake[snake.length - 1] 111 | 112 | if (head.x == food.x && head.y == food.y) { 113 | incrementScore() 114 | snake.push(head) 115 | audio.play() 116 | 117 | let x = randomPosition() 118 | let y = randomPosition() 119 | 120 | while (snake.find((position) => position.x == x && position.y == y)) { 121 | x = randomPosition() 122 | y = randomPosition() 123 | } 124 | 125 | food.x = x 126 | food.y = y 127 | food.color = randomColor() 128 | } 129 | } 130 | 131 | const checkCollision = () => { 132 | const head = snake[snake.length - 1] 133 | const canvasLimit = canvas.width - size 134 | const neckIndex = snake.length - 2 135 | 136 | const wallCollision = 137 | head.x < 0 || head.x > canvasLimit || head.y < 0 || head.y > canvasLimit 138 | 139 | const selfCollision = snake.find((position, index) => { 140 | return index < neckIndex && position.x == head.x && position.y == head.y 141 | }) 142 | 143 | if (wallCollision || selfCollision) { 144 | gameOver() 145 | } 146 | } 147 | 148 | const gameOver = () => { 149 | direction = undefined 150 | 151 | menu.style.display = "flex" 152 | finalScore.innerText = score.innerText 153 | canvas.style.filter = "blur(2px)" 154 | } 155 | 156 | const gameLoop = () => { 157 | clearInterval(loopId) 158 | 159 | ctx.clearRect(0, 0, 600, 600) 160 | drawGrid() 161 | drawFood() 162 | moveSnake() 163 | drawSnake() 164 | chackEat() 165 | checkCollision() 166 | 167 | loopId = setTimeout(() => { 168 | gameLoop() 169 | }, 300) 170 | } 171 | 172 | gameLoop() 173 | 174 | document.addEventListener("keydown", ({ key }) => { 175 | if (key == "ArrowRight" && direction != "left") { 176 | direction = "right" 177 | } 178 | 179 | if (key == "ArrowLeft" && direction != "right") { 180 | direction = "left" 181 | } 182 | 183 | if (key == "ArrowDown" && direction != "up") { 184 | direction = "down" 185 | } 186 | 187 | if (key == "ArrowUp" && direction != "down") { 188 | direction = "up" 189 | } 190 | }) 191 | 192 | buttonPlay.addEventListener("click", () => { 193 | score.innerText = "00" 194 | menu.style.display = "none" 195 | canvas.style.filter = "none" 196 | 197 | snake = [initialPosition] 198 | }) 199 | --------------------------------------------------------------------------------