Trong bài viết này, tôi sẽ chỉ cho bạn cách xây dựng một trò chơi rắn bằng JavaScript.
Trò chơi con rắn là một trò chơi đơn giản trong đó một con rắn di chuyển quanh hộp để cố gắng ăn một quả táo. Sau khi ăn thành công quả táo, chiều dài của con rắn tăng lên và chuyển động trở nên nhanh hơn.
Sau đó, trò chơi kết thúc khi con rắn chạy vào chính nó hoặc bất kỳ bức tường nào trong bốn bức tường của hộp.
Được rồi, hãy bắt đầu với HTML và CSS (bộ xương cho trò chơi của chúng ta).
HTML
<h1>Nokia 3310 snake</h1>
<div class="scoreDisplay"></div>
<div class="grid"></div>
<div class="button">
<button class="top">top</button>
<button class="bottom">bottom</button>
<button class="left">left</button>
<button class="right">right</button>
</div>
<div class="popup">
<button class="playAgain">play Again</button>
</div>
HTML ở trên là khá cơ bản.
- Chúng tôi có một div của lớp
scoreDisplay
mà sẽ hiển thị điểm số của chúng tôi. - Có một div của lớp
grid
nơi sẽ tổ chức trò chơi (đây sẽ là một lưới 10 nhân 10) - Lớp
button
về cơ bản chứa một nút cho người dùng chơi trò chơi trên điện thoại (chúng tôi sẽ tự động hóa nó bằng bàn phím cho người dùng máy tính để bàn). - Và
popup
lớp sẽ giữ nút phát lại của chúng tôi.
Bây giờ, hãy thêm một số kiểu dáng bằng CSS.
CSS
body {
background: rgb(212, 211, 211);
}
.grid {
width: 200px;
height: 200px;
border: 1px solid red;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
}
.grid div {
width: 20px;
height: 20px;
/*border:1px black solid;
box-sizing:border-box*/
}
.snake {
background: blue;
}
.apple {
background: yellow;
border-radius: 20px;
}
.popup {
background: rgb(32, 31, 31);
width: 100px;
height: 100px;
position: fixed;
top: 100px;
left: 100px;
display: flex;
justify-content: center;
align-items: center;
}
Trong CSS, grid
đó là bảng trò chơi có kích thước đã đặt và màn hình hiển thị flex
. Điều này cho phép nội dung (div) của lưới này xếp hàng theo chiều ngang như thể chúng là các phần tử nội tuyến thay vì hiển thị khối thông thường mà chúng sở hữu.
Các flex wrap
chỉ cần di chuyển các div sang dòng tiếp theo, ngăn chúng vượt qua kích thước đã đặt của phần tử cha (lưới).
Chúng tôi sẽ tự động tạo nội dung bảng trò chơi từ JS nhưng chúng tôi có thể cung cấp chiều rộng và chiều cao ở đây (với .grid
div). Tôi đã bao gồm các nhận xét ở đây để giúp bạn thực sự nhìn thấy các div, vì vậy khi thời gian trôi qua, chúng tôi sẽ bỏ ghi chú mã.
Các snake
và Apple
các lớp học là để chỉ cho chúng tôi vị trí của con rắn và phần thưởng trong trò chơi, trong khi popup
lớp là một div cố định chứa replay
div.
Tại thời điểm này, bạn sẽ có một cái gì đó như thế này:

Bây giờ chúng tôi đã sẵn sàng cho JavaScript.
JavaScript
Điều đầu tiên chúng ta cần làm là xác định các biến của chúng ta:
let grid = document.querySelector(".grid");
let popup = document.querySelector(".popup");
let playAgain = document.querySelector(".playAgain");
let scoreDisplay = document.querySelector(".scoreDisplay");
let left = document.querySelector(".left");
let bottom = document.querySelector(".bottom");
let right = document.querySelector(".right");
let up = document.querySelector(".top");
let width = 10;
let currentIndex = 0;
let appleIndex = 0;
let currentSnake = [2, 1, 0];
let direction = 1;
let score = 0;
let speed = 0.8;
let intervalTime = 0;
let interval = 0;
Chiều rộng thay đổi chính xác như nó vốn có (chiều rộng của lưới, tức là 10). Các biến khác sẽ có ý nghĩa hơn khi chúng ta tiếp tục – nhưng tin hay không thì con rắn của chúng ta thực sự là một mảng được gọi là currentSnake
.
Bây giờ hãy bắt đầu với các chức năng:
document.addEventListener("DOMContentLoaded", function () {
document.addEventListener("keyup", control);
createBoard();
startGame();
playAgain.addEventListener("click", replay);
});
Đây là một eventListener
trên đối tượng tài liệu được gọi là DomContentLoaded
và sự kiện này được kích hoạt ngay lập tức sau khi nội dung HTML được tải trên màn hình của chúng tôi.
Khi điều này xảy ra, chúng tôi đặt một eventListener trên tài liệu để theo dõi các lần nhấp trên bàn phím (sẽ nói thêm về điều này sau). Sau đó, chúng tôi muốn tạo gameBoard
bắt đầu trò chơi và coi chừng các lần nhấp vào nút phát lại của chúng tôi.
Chức năng tạo bảng
function createBoard() {
popup.style.display = "none";
for (let i = 0; i < 100; i++) {
let div = document.createElement("div");
grid.appendChild(div);
}
}
Như tôi đã nói trước đó, đây là lưới 10 nhân 10, nghĩa là chúng ta sẽ cần 100 div. Vì vậy, từ phía trên, chúng tôi đóng cửa sổ bật lên div và chúng tôi lặp đến 100 mỗi khi chúng tôi tạo một div mới và nối nó vào lưới (bảng trò chơi).
Thao tác này sẽ ngay lập tức thêm một số kiểu dáng mà chúng ta đã tạo ở trên (div .grid). Bạn có thể bỏ ghi chú các kiểu CSS và bạn sẽ thấy các div được tạo (bỏ ghi chú lại).
Chức năng startGame
function startGame() {
let squares = document.querySelectorAll(".grid div");
randomApple(squares);
//random apple
direction = 1;
scoreDisplay.innerHTML = score;
intervalTime = 1000;
currentSnake = [2, 1, 0];
currentIndex = 0;
currentSnake.forEach((index) => squares[index].classList.add("snake"));
interval = setInterval(moveOutcome, intervalTime);
}
Các startGame
chức năng đầu tiên lấy tất cả các div (vì chúng tôi đang tạo các div trong thời gian chạy, chúng tôi không thể lấy chúng ở đầu mã).
Tiếp theo, chúng tôi chọn một vị trí cho quả táo của chúng tôi. Chúng tôi sẽ làm điều đó dưới đây trong randomApple
chức năng. Các direction
chỉ nơi con rắn hướng tới – 1 cho bên phải, -1 cho bên trái, v.v.
intervalTime
đặt thời gian cần thiết để con rắn di chuyển xung quanh, trong khi currentSnake
xác định vị trí chính xác của con rắn trên lưới (lưu ý rằng con rắn về cơ bản là một vài div được cung cấp một loại màu cụ thể).
Để hiển thị con rắn của chúng ta trên màn hình, chúng ta sẽ lặp lại currentSnake
với forEach
. Với mỗi giá trị chúng tôi nhận được, chúng tôi sẽ sử dụng nó với hình vuông. Hãy nhớ rằng chúng tôi đã truy cập các div lưới với querySelectorAll
, và sau đó chúng ta có thể truy cập chúng như một mảng, tức là sử dụng các số. Trong trường hợp của chúng tôi, đây là những giá trị của currentSnake
.
Sau này, chúng tôi chỉ cần thêm một setInterval
gọi (với chức năng di chuyển Outcome
và một thời gian intervalTime
mà chúng tôi đã đặt ở trên) vào biến interval
. Điều này là để chúng ta có thể dễ dàng gọi clearInterval
trên biến đó.
Các moveOutcome
chạy cứ sau 1000ms (1 giây) và về cơ bản xác định điều gì sẽ xảy ra khi bạn di chuyển con rắn.
Hàm moveOutcome
function moveOutcome() {
let squares = document.querySelectorAll(".grid div");
if (checkForHits(squares)) {
alert("you hit something");
popup.style.display = "flex";
return clearInterval(interval);
} else {
moveSnake(squares);
}
}
Vì vậy, giống như startGame
chức năng trên, trước tiên chúng tôi nhận được tất cả các grid
div, và sau đó chúng tôi kiểm tra xem checkForHits
hàm trả về true.
Nếu đúng như vậy, điều này có nghĩa là chúng tôi đã đánh một thứ gì đó và sau đó nó hiển thị nút phát lại và xóa khoảng thời gian. Nếu nó trả về false, điều này có nghĩa là chúng tôi đã không đánh bất cứ thứ gì và chúng tôi di chuyển con rắn bằng moveSnake
chức năng.
Vì vậy, về cơ bản, cứ sau 1 giây trò chơi sẽ kết thúc nếu checkForHits
là đúng hoặc chúng ta di chuyển con rắn về phía trước một bước nếu checkForHits
là sai. tôi sẽ nói về moveSnake
chức năng đầu tiên.
Hàm moveSnake
function moveSnake(squares) {
let tail = currentSnake.pop();
squares[tail].classList.remove("snake");
currentSnake.unshift(currentSnake[0] + direction);
// movement ends here
eatApple(squares, tail);
squares[currentSnake[0]].classList.add("snake");
}
Các moveSnake
chức năng nhận được một đối số gọi là squares
để chúng ta không phải lấy div .grid một lần nữa trong chức năng này.
Điều đầu tiên chúng ta cần làm là loại bỏ phần tử cuối cùng của currentSnake
mảng thông qua pop (đây là phần đuôi và phần tử đầu tiên luôn là phần đầu). Về cơ bản, con rắn di chuyển về phía trước một bước, rời khỏi vị trí trước đó của nó. Sau đó, chúng ta chỉ cần thêm một giá trị mới vào đầu mảng với unShift
.
Giả sử rằng con rắn của chúng ta mới bắt đầu di chuyển và hướng về bên phải (nghĩa là hướng = 1). Hướng đó sẽ được thêm vào currentSnake
‘s head và tổng sẽ được đẩy thành cái mới snakeHead
.
Ví dụ, nếu con rắn ở vị trí [2,1,0]chúng tôi loại bỏ phần tử cuối cùng để nó ở vị trí [2,1]. Sau đó, chúng tôi lấy cái đầu đó là 2 và thêm hướng đó là 1 và biến giá trị này thành giá trị mới [3,2,1] giúp di chuyển con rắn của chúng ta một bước về phía bên phải sau một giây.
Nếu chúng ta muốn di chuyển con rắn xuống dưới, hướng sẽ được đặt thành chiều rộng (là 10) và được thêm vào phần tử đầu tiên (tức là 12 và được đẩy) [12,2,1].
Sau đó, chúng tôi chỉ cần kiểm tra xem con rắn đã ăn táo chưa và hiển thị con rắn mới trên DOM.
Hàm checkForHits
function checkForHits(squares) {
if (
(currentSnake[0] + width >= width * width && direction === width) ||
(currentSnake[0] % width === width - 1 && direction === 1) ||
(currentSnake[0] % width === 0 && direction === -1) ||
(currentSnake[0] - width <= 0 && direction === -width) ||
squares[currentSnake[0] + direction].classList.contains("snake")
) {
return true;
} else {
return false;
}
}
Các checkForHits
chức năng có một câu lệnh if. Tùy thuộc vào điều kiện được xác định, nó có thể trả về true (có nghĩa là chúng ta đánh vào thứ gì đó) hoặc false.
Điều kiện đầu tiên là nếu currentSnake
[0] (đầu rắn) + chiều rộng (10) bằng tổng diện tích của chiều rộng (nghĩa là chiều rộng * chiều rộng = 100) và hướng bằng chiều rộng.
Vì vậy, về cơ bản, hãy giả sử rằng đầu của con rắn ở vị trí 97, lớp cuối cùng của lưới của chúng ta. Nếu bạn cộng 10 với 97 (= 107), thì số đó lớn hơn toàn bộ lưới là 100. Nếu hướng của con rắn vẫn hướng xuống dưới, thì con rắn đã chạm vào đường viền dưới cùng.
Nếu con rắn ở 97 , 97 + 10 = 107, nhưng người chơi có thể đổi hướng thành 1 (chẳng hạn như họ nhấn phím trái), thì nó sẽ không trúng bất cứ thứ gì.
Hoặc (||) nếu phần còn lại khi đầu con rắn chia cho chiều rộng = chiều rộng-1 (ví dụ: 9) và hướng là 1. Mỗi div cuối cùng ở phía bên tay phải có giá trị là 9, 19, 29 và như thế. Vì vậy, về cơ bản, nó sẽ luôn là 9 khi bạn chia cho 10.
Nếu đầu con rắn của chúng ta ở vị trí 39 và hướng vẫn là 1 (tức là con rắn vẫn đang di chuyển về phía bức tường), thì nó đã va vào thứ gì đó (bức tường bên phải).
Mọi điều kiện khác hoàn toàn ngược lại với hai điều kiện trên. Điều kiện cuối cùng cho phép rằng nếu đầu rắn hướng đến một nơi đã có sẵn một con rắn cùng hạng, điều đó đơn giản có nghĩa là con rắn đang tự cắn mình.
Vì vậy…nếu bất kỳ điều kiện nào ở trên là đúng, thì con rắn đã va vào thứ gì đó và thật sẽ được trả lại (khác sai). Và nếu đúng như vậy, trò chơi sẽ kết thúc. Nhưng nếu nó sai, hãy di chuyển con rắn về phía trước một bước với moveSnake
.
Hàm eatApple
function eatApple(squares, tail) {
if (squares[currentSnake[0]].classList.contains("apple")) {
squares[currentSnake[0]].classList.remove("apple");
squares[tail].classList.add("snake");
currentSnake.push(tail);
randomApple(squares);
score++;
scoreDisplay.textContent = score;
clearInterval(interval);
intervalTime = intervalTime * speed;
interval = setInterval(moveOutcome, intervalTime);
}
}
Các eatApple
chức năng được gọi từ moveSnake
hoạt động mỗi khi con rắn di chuyển một bước.
Nó nhận được hai ô đối số, div .grid và đuôi (về cơ bản giá trị xuất hiện từ con rắn trong moveOutcome
). Sau đó, nó sẽ kiểm tra xem vị trí tiếp theo mà con rắn của chúng ta di chuyển có chứa một quả táo hay không.
Nếu đúng như vậy, nó chỉ cần thêm phần đuôi mà chúng tôi đã xuất hiện trở lại mảng. Điều này là do mỗi khi con rắn của chúng ta ăn một quả táo, chúng ta muốn tăng chiều dài của con rắn lên một giá trị – và còn cách nào tốt hơn là thêm cái đuôi đã bật ra khi nó di chuyển?
Sau đó, chúng tôi chỉ cần chọn một vị trí mới cho quả táo của mình với randomApple
(xem bên dưới). Sau đó, chúng tôi thêm một giá trị của một vào điểm số của chúng tôi và hiển thị nó cho người dùng, xóa timeInterval
(để chúng ta có thể tăng tốc độ của con rắn, đó là thời gian mỗi chuyển động xảy ra) và sau đó chúng ta chỉ cần đặt lại khoảng thời gian.
Hàm RandomApple
function randomApple(squares) {
do {
appleIndex = Math.floor(Math.random() * squares.length);
} while (squares[appleIndex].classList.contains("snake"));
squares[appleIndex].classList.add("apple");
}
randomApple
chỉ cần chọn một vị trí để đặt quả táo của chúng tôi bằng cách sử dụng làm trong khi vòng. Đầu tiên, nó chọn một vị trí ngẫu nhiên với Math.random()
trong vòng lặp do và kiểm tra xem vị trí nó chọn đã chứa lớp rắn chưa.
Điều này có nghĩa là điều kiện trong câu lệnh do sẽ tiếp tục chạy cho đến khi nó tìm thấy một vị trí không chứa con rắn (hãy tiếp tục làm điều này trong khi điều này là đúng). Khi nó tìm thấy một vị trí, nó chỉ cần cho vị trí đó một loại táo.
Thiết lập điều khiển
Bây giờ chúng ta cần thiết lập các điều khiển của mình. Chúng tôi sẽ bắt đầu với người dùng bàn phím.
function control(e) {
if (e.keycode === 39) {
direction = 1; // right
} else if (e.keycode === 38) {
direction = -width; //if we press the up arrow, the snake will go ten divs up
} else if (e.keycode === 37) {
direction = -1; // left, the snake will go left one div
} else if (e.keycode === 40) {
direction = +width; // down the snake head will instantly appear 10 divs below from the current div
}
}
Hãy nhớ từ phía trên, chúng tôi đặt một eventListener
vì keyup
. Chức năng này kích hoạt ngay lập tức sau khi bạn nhấn và. để lại một phím trên bàn phím.
Giờ đây, mỗi nút trên bàn phím có một giá trị được gọi là mã phím (số) mà chúng tôi có quyền truy cập và cho chúng tôi biết số nào đã được nhấp. Về cơ bản, chúng tôi sẽ theo dõi các phím mũi tên với mã phím tương ứng của chúng. Cùng với đó, chúng tôi thực hiện thay đổi theo hướng, ví dụ -1, 10 và như thế.
Được rồi, tôi hy vọng bạn hiểu làm thế nào chúng ta có thể di chuyển con rắn bây giờ.
Tiếp theo, bộ nút này dành cho thiết bị di động và về cơ bản chúng ta cũng đang làm điều tương tự:
up.addEventListener("click", () => (direction = -width));
bottom.addEventListener("click", () => (direction = +width));
left.addEventListener("click", () => (direction = -1));
right.addEventListener("click", () => (direction = 1));
Điều cuối cùng chúng ta cần làm là tạo replay
div cái này sẽ bật lên khi con rắn va vào thứ gì đó. Nút giúp chúng tôi thiết lập lại trò chơi.
Chức năng phát lại
function replay() {
grid.innerHTML = "";
createBoard();
startGame();
popup.style.display = "none";
}
Từ trên, về cơ bản, chúng tôi xóa lưới (gameboard) và chạy các chức năng trước đó.
Xin chúc mừng – bạn đã đi đến cuối cùng! Đây là kết quả cuối cùng:

Tôi hy vọng bạn có thể viết mã theo và bạn thích nó.
Trong hướng dẫn này, chúng ta đã học cách tạo trò chơi rắn của riêng mình bằng JavaScript. Một số khái niệm quan trọng khác mà chúng tôi đề cập bao gồm đẩy, bật, setInterval, ClearInterval và sự kiệnListener.
Bạn có thể xem trò chơi cuối cùng tại đây: https://codepen.io/Fako29/pen/dyppXZG.
Cảm ơn bạn đã đọc. Theo dõi tôi trên Twitter tại đây: https://twitter.com/fakoredeDami