Chào các bạn, ở bài viết trước, chúng ta đã thực hiện setup khi người chơi chọn level và bắt đầu game, ở bài viết này chúng ta sẽ thực hiện các chức năng còn lại để hoàn thiện game Memory card bao gồm:
- Xử lý sự kiện mở card
- Kiểm tra khi card trùng nhau hoặc không?
- Điều kiện win game
- Hiển thị phần end game, thông báo kết quả
- Xử lý chơi lại, thoát game
OK, bắt đầu thôi nào 😊😊
Xử lý sự kiện mở card
Trong bài viết trước, trong function renderCard(), step Hiển thị lên trên giao diện, mình có gọi function flipCard() trong này (nhưng mà chưa định nghĩa). Mục đích của function flipCard() chính là lật card lại khi người chơi click vào, bây giờ chúng ta sẽ đi định nghĩa function này
let lockBoard = false; // Khóa không cho ấn
let firstCard = null; // Chứa phần tử DOM CARD khi mở lần 1
let secondCard = null; // Chứa phần tử DOM CARD khi mở lần 2
function flipCard(card) {
if (lockBoard) {
return;
}
// Người chơi không được click 2 lần trên cùng 1 card
if (card === firstCard) {
return;
}
card.classList.toggle('flip');
// Khi click CARD đầu tiên
if (!firstCard) {
firstCard = card;
return;
}
// Khi click CARD thứ 2
secondCard = card;
checkForMatch();
// Update step
updateStep();
}
Khi người chơi click vào 1 card sẽ có 1 số trường hợp xảy ra như sau
- TH1 : lockBoard == true (game đang ở trạng thái khóa, ví dụ trong trường hợp đang kiểm tra khi người chơi mở 2 card) => return kết thúc hàm
- TH2 : Khi người chơi đã chọn 1 card và lần mở tiếp theo cũng chọn card đó => return kết thúc hàm
Nếu không phải trong 2 trường hợp trên, lúc này chúng ta sẽ toggle class "flip" để card lật lại
Khi mở đủ 2 card, lúc này chúng ta sẽ gọi function checkForMatch() để kiểm tra xem 2 card chúng ta mở ra có chính xác hay không?
function checkForMatch() {
// Kiểm tra xem NAME của 2 CARD có giống nhau không?
let isMatch = firstCard.dataset.name === secondCard.dataset.name;
// isMatch = true => xóa sự kiện click ở 2 CARD đó
// isMatch = false => úp CARD xuống
isMatch ? disableCards() : unflipCards();
}
Để kiểm tra xem 2 card sau khi mở có trùng nhau hay không chúng ta định nghĩa function checkForMatch(). trong function này chúng ta sẽ kiểm tra thuộc tính data-name của 2 card này có trùng nhau hay không?(data-name được set khi render card - các bạn có thể inspect lên để quan sát) và lưu kết quả và biến isMatch
Trường hợp isMatch = true (trùng nhau) chúng ta thực hiện function disableCards(), còn trường hợp isMatch = false (không trùng nhau) thì thực hiện function unflipCards()
Trường hợp mở đúng
function disableCards() {
firstCard.removeEventListener('click', flipCard);
secondCard.removeEventListener('click', flipCard);
resetBoard();
score++;
checkWin();
}
Trường hợp 2 card mà trùng nhau, lúc này chúng ta sẽ remove phần lắng nghe sự kiện trên 2 card này để người chơi không click vào được nữa
Tăng giá trị điểm của người chơi score lên 1 đơn vị, và sau đó kiểm tra trường hợp win game của người chơi thông qua function checkWin()
function checkWin() {
if (score == level) {
clearInterval(interval);
setTimeout(() => {
gameEl.style.display = 'none';
endGameEl.style.display = 'flex';
updateEndGame();
}, 1500);
}
}
Trong function checkWin() đơn giản là chúng ta kiểm tra xem số điểm mà chúng ta có qua mỗi lần mở đúng có bằng level hay không (score == level). Nếu trường hợp này xảy ra tức là chúng ta đã mở đúng hết toàn bộ số card đang có, lúc này màn hình sẽ đi đến màn end game
setTimeout(() => {
gameEl.style.display = 'none';
endGameEl.style.display = 'flex';
updateEndGame();
}, 1500);
Chúng ta sẽ đợi sau 1.5s, để ẩn màn game => show màn end game, và đồng thời hiển thị 1 số thông tin trong màn end game thông qua function updateEndGame()
function updateEndGame() {
document.querySelector('.time-box p').innerText = convertTime(time);
document.querySelector('.step-box p').innerText = `${step} bước`
}
Ở đây chúng ta sẽ cập nhật thời gian chúng ta chơi để hoàn thành game và số step đã đi lên trên giao diện
Và 1 điều quan trọng nữa là chúng ta cần ngừng quá trình chạy time lại
clearInterval(interval);
Để ngừng đếm thời gian chúng ta sử dụng hàm clearInterval(). Điều này rất quan trọng
Trường hợp mở sai
function unflipCards() {
lockBoard = true;
setTimeout(() => {
firstCard.classList.remove('flip');
secondCard.classList.remove('flip');
resetBoard();
}, 1000);
}
function resetBoard() {
lockBoard = false;
firstCard = null;
secondCard = null;
}
Còn trong trường hợp 2 card mở không trùng nhau, lúc này chúng ta sẽ đợi 1000ms (1s) và sau đó cho 2 card up xuống bằng cách remove class "flip" trên mỗi card
Trong quá trình đợi chúng ta sẽ set giá trị lockBoard = true để người chơi không thể click vào card khác trong quá trình này
Xử lý chơi lại và thoát game
Chơi lại game
const btnPlayAgain = document.querySelector('#btn-play-again');
btnPlayAgain.addEventListener('click', function () {
// Reset điểm về 0
score = 0;
time = 0;
step = 0;
timeEl.innerText = convertTime(time);
stepEl.innerText = `${step} bước`;
// Chạy thời gian
interval = setInterval(updateTime, 1000);
// Dựa vào level đã có => khởi tạo game
renderCards(level);
// Ẩn END => show GAME
endGameEl.style.display = 'none';
gameEl.style.display = 'flex';
});
Đầu tiên chúng ta sẽ lắng nghe sự kiện khi người chơi click vào button "Chơi lại" (id="btn-play-again")
Khi thực hiện chơi lại game tức là người chơi đang từ màn End game -> quay trở lại màn chơi game với level được giữ nguyên như cũ. Vì vậy trong hàm xử lý sự kiện chúng ta cần phải thực hiện reset 1 số thành phần sau :
- Cho score, time, step reset về 0 và hiển thị lại các giá trị này ở trên giao diên
- Bắt đầu đếm thời gian
- Hiển thị lại danh sách card trên giao diện
- Ẩn màn end game -> show màn chơi game
Thoát game
const btnReload = document.querySelector('#btn-reload');
btnReload.addEventListener('click', function () {
window.location.reload();
});
Để thực hiện chức năng thoát game, đơn giản chúng ta chỉ cần reload trang lại là xong, javascript đã hỗ trợ sẵn cho chúng ta điều này
Nếu các bạn có phần nào thắc mắc, thì hãy comment cho mình biết nhé ❤️
Source code của phần này mình để ở đây nhé : https://github.com/buihien0109/memory-card-blog/tree/main/part-2
Các bạn có thể tham khảo thêm khóa học này nhé:
Bình luận