0
0
Lập trình
Hưng Nguyễn Xuân 1
Hưng Nguyễn Xuân 1xuanhungptithcm

Hiểu về 'State' trong JavaScript và Ứng dụng trong Dự án

Đăng vào 1 tháng trước

• 11 phút đọc

Giới thiệu

Trong phát triển ứng dụng và game, khái niệm "state" hay trạng thái trong JavaScript đóng vai trò cực kỳ quan trọng. Nó không chỉ là dữ liệu biểu thị trạng thái hiện tại của ứng dụng mà còn ảnh hưởng trực tiếp đến logic và hiệu suất của chương trình. Trong bài viết này, tôi sẽ chia sẻ cách tôi đã sử dụng quản lý trạng thái trong dự án game Pac-Man của mình để cải thiện gameplay và đảm bảo tính ổn định của ứng dụng.

Khái niệm State trong JavaScript

State trong JavaScript là dữ liệu nền tảng thể hiện điều kiện hiện tại của ứng dụng hoặc game. Nó theo dõi nhiều khía cạnh như vị trí của người chơi, điểm số và các thành phần trên bảng. Trong phát triển game, việc duy trì một mảng trạng thái riêng biệt giúp đảm bảo rằng bạn luôn biết được tình trạng thực sự của bảng game, bất kể những gì được hiển thị trên màn hình. Cách tiếp cận này rất có lợi khi nhiều yếu tố có thể thay đổi một ô cùng lúc, vì nó cung cấp một cách đáng tin cậy để quản lý logic game độc lập với DOM.

Tại sao State lại Quan trọng?

  • Tách biệt mối quan tâm: Logic (trạng thái) tách biệt với trình bày (DOM).
  • Dễ dự đoán: Bạn luôn biết "sự thật" của game.
  • Khả năng mở rộng: Mô hình này hoạt động cho các game và ứng dụng phức tạp hơn!

Dự án JS Pac-Man

  • 🔗 [Liên kết tới dự án đã triển khai]
  • 🔗 [Liên kết tới GitHub]
  • 🔗 [Liên kết tới bảng sprint]

Quản lý Tình trạng Hiện Thị của Pellets và Tương Tác với Ghosts

Bài viết này sẽ tập trung vào việc thêm quản lý trạng thái JavaScript cho các pellets trong dự án game Pac-Man của tôi, nhằm cải thiện và làm cho gameplay đáng tin cậy hơn.

Trong game Pac-Man của tôi, các lớp CSS được sử dụng cho các pellets. Để tạo hiệu ứng hình ảnh cho một pellet, tôi đã làm nền của ô vuông 20×20px màu trắng và biến đổi tỷ lệ xuống 0.2 kích thước của ô (tức là 10×10px khi xem trên thiết bị di động).

Lưu ý: Biến đổi tỷ lệ chỉ ảnh hưởng đến cách nội dung xuất hiện, không ảnh hưởng đến kích thước hoặc vị trí của ô trong lưới.

css Copy
.pellet {
  background-color: whitesmoke;
  transform: scale(0.2);
}

Khi Pac-Man vào một div chứa lớp pellet, tôi sẽ loại bỏ nó và cập nhật điểm số cùng với logic khác tương ứng, đạt được hiệu ứng mong muốn. Tuy nhiên, việc sử dụng biến đổi tỷ lệ để thu nhỏ kích thước của pellet có một nhược điểm; nó cũng thu nhỏ bất kỳ thứ gì khác chia sẻ div xuống còn 0.2 kích thước ban đầu! Vì Pac-Man luôn ăn pellet, tôi không gặp vấn đề thu nhỏ. Nhưng khi những con ma đi qua các div chứa pellets, chúng lại bị thu nhỏ.

Giải pháp của tôi cho việc thu nhỏ của ghosts là loại bỏ lớp pellet khi chúng vào div và sau đó trả lại khi chúng rời khỏi đó. Và nó đã hoạt động! Một cách nào đó. Trong khi chơi game, các ghost không nhất quán trong việc đưa lớp pellet trở lại và thêm nó vào các ô game trống nơi nó không thuộc về.

Vì vậy, tôi đã nghĩ ra một cách tiếp cận mới: tôi sẽ tạo một biến để lưu giữ tất cả thông tin cần thiết từ ô mà một ghost vào, và sau đó sử dụng thông tin của biến để đưa nó trở lại khi ghost rời khỏi đó. Tôi đã mô tả giải pháp của mình cho GitHub CoPilot, mong được khen ngợi cho ý tưởng tuyệt vời của mình và sau đó để CoPilot hỗ trợ tôi trong việc triển khai mã. Thay vào đó, CoPilot đã đưa ra một giải pháp khác và giới thiệu cho tôi về quản lý trạng thái trong JavaScript, được sử dụng trong phát triển game.

State trong Dự án của Tôi

Tôi đã học được rằng "state" trong phát triển game JavaScript được sử dụng để theo dõi vị trí người chơi, điểm số, những gì có trên bảng và trong trường hợp của tôi, các pellets, trong số những thứ khác; trước đây, tôi nghĩ rằng quản lý trạng thái chỉ dành cho React và các framework khác.

CoPilot giải thích rằng DOM (các phần tử HTML và lớp của chúng) cho thấy những gì đang trên màn hình, nhưng nó không phải lúc nào cũng đáng tin cậy để theo dõi logic game, đặc biệt là trong trường hợp của tôi, khi nhiều thứ (như ghosts và Pac-Man) có thể thay đổi một ô cùng lúc. Bằng cách duy trì một mảng riêng biệt (mảng "state array") chứa những gì mỗi ô giữ, chúng ta luôn biết "sự thật" về bảng game, bất kể những gì được thấy về mặt hình ảnh.

Triển khai Quản lý Trạng thái

Trong dự án của tôi, hàm setGameBoard hoạt động như một lớp phủ để thiết lập bảng game Pac-Man. Bản thiết kế cho bảng game chứa các số cho các ô khác nhau, như tường, pellet, power pellet, không gian trống, hoặc vị trí bắt đầu của Pac-Man. Khi thiết lập, mã sẽ lặp qua từng ô, kiểm tra giá trị lớp phủ, và thêm lớp hình ảnh phù hợp.

Tôi đã sửa đổi hàm setGameBoard để bao gồm mã bổ sung cũng cập nhật mảng pelletState mới thêm vào tương ứng, khi tìm thấy cả pellets và power pellets.

Bên trong hàm setGameBoard, mã sử dụng mảng pelletState để theo dõi những gì mỗi ô trên bảng chứa:

  • Đối với mỗi ô, nó kiểm tra overlayArray để xem ô đó có nên có một power pellet (2), một pellet thông thường (3), hay không có gì.
  • Nếu ô đó nên có một power pellet, nó đặt pelletState[i] = 'powerPellet'.
  • Nếu ô đó nên có một pellet thông thường, nó đặt pelletState[i] = 'pellet'.
  • Nếu ô đó không nên có gì, nó đặt pelletState[i] = null.

Bây giờ, mảng pelletState luôn giữ trạng thái thực sự của mọi ô—bất kể những gì hiện đang hiển thị trên màn hình.

javascript Copy
export const pelletState = [];

export function setGameBoard() {
  // ...mã thiết lập bổ sung...

  for (let i = 0; i < squares.length; i++) {
    // ...loại bỏ lớp, đặt lại ô...

    // Logic trạng thái pellet (chỉ một trong số này sẽ đúng cho mỗi ô)
    if (overlayArray[i] === 2) {
      squares[i].classList.add('powerPellet');
      pelletState[i] = 'powerPellet'; // Theo dõi trạng thái
    } else if (overlayArray[i] === 3) {
      squares[i].classList.add('pellet');
      pelletState[i] = 'pellet'; // Theo dõi trạng thái
    } else {
      pelletState[i] = null; // Theo dõi trạng thái
    }

    // ...logic thiết lập bảng khác...
  }

  // ...thiết lập ghost và mã khác...
}

Cập nhật Trạng thái Pellet với Pac-Man

Trong hàm điều khiển Pac-Man, tôi đã thêm mã cập nhật mảng pelletState ngay sau mã loại bỏ các lớp pellet và power pellet khi Pac-Man ăn chúng. Bây giờ, khi Pac-Man ăn một pellet hoặc power pellet, mã không chỉ loại bỏ lớp CSS hình ảnh từ bảng, mà còn cập nhật mảng pelletState cho ô đó. Điều này đảm bảo trạng thái nội bộ của game luôn khớp với những gì thực sự có trên bảng, làm cho logic đáng tin cậy hơn.

javascript Copy
export function control(x) {
  // ...logic xác định bước đi và di chuyển...

  for (let i = 0; i < steps; i++) {
    // ...mã di chuyển Pac-Man...

    // Logic trạng thái pellet: cập nhật trạng thái khi Pac-Man ăn pellet
    if (squares[pacmanCurrentIndex].classList.contains('pellet')) {
      playPacManEatingPelletsSound();
      squares[pacmanCurrentIndex].classList.remove('pellet');
      pelletState[pacmanCurrentIndex] = null; // Cập nhật trạng thái: pellet giờ đã mất
      counterPelet += 1;
      score += 10;
    } else {
      stopPacManEatingPelletsSound();
    }

    // Logic trạng thái pellet: cập nhật trạng thái khi Pac-Man ăn power pellet
    if (squares[pacmanCurrentIndex].classList.contains('powerPellet')) {
      squares[pacmanCurrentIndex].classList.remove('powerPellet');
      pelletState[pacmanCurrentIndex] = null; // Cập nhật trạng thái: power pellet giờ đã mất
      score += 50;
      // ...hiệu ứng power pellet...
    }

    // ...cập nhật trái cây thưởng, điểm số và giao diện người dùng...
  }
}

Cập nhật Trạng thái Pellet với Ghosts

Khi một ghost di chuyển quanh bảng, nó đi qua các ô chứa pellets và power pellets. Mã cập nhật sẽ kiểm tra mảng trạng thái pellet mỗi khi một ghost di chuyển để giữ cho hình ảnh game và logic chính xác. Nếu ghost sắp vào một ô mà theo mảng trạng thái nên có một pellet hoặc power pellet, nó tạm thời loại bỏ pellet hình ảnh (lớp CSS) khỏi ô đó để ghost xuất hiện trên cùng. Nhưng, vì các ghosts không bao giờ ăn pellets hoặc power pellets, chúng luôn tồn tại trong mảng pelletState.

Sau đó, khi ghost rời khỏi ô trước đó, mã cập nhật sẽ kiểm tra lại mảng pelletState. Nếu ô đó dự kiến có một pellet hoặc power pellet, nó khôi phục pellet hình ảnh (lớp CSS) để nó xuất hiện lại sau khi ghost đã di chuyển đi. Bằng cách này, game luôn biết được những ô nào thực sự có pellets, và hình ảnh luôn khớp với trạng thái nội bộ của game—ngay cả khi ghosts di chuyển quanh bảng, sửa chữa sự sai lệch của pellet do lỗi của ghosts!

javascript Copy
export function moveGhost(ghost) {
  // ...logic thiết lập và di chuyển...

  ghost.timerId = setInterval(function() {
    // ...kiểm tra hướng và di chuyển...

    if (foundValid) {
      // ...logic hướng mắt...

      // Tính toán chỉ số tiếp theo
      const nextIndex = ghost.currentIndex + direction;

      // Logic trạng thái: cập nhật hình ảnh pellet dựa trên trạng thái
      // Loại bỏ lớp pellet/powerPellet khỏi ô mới nếu cần
      if (pelletState[nextIndex] === 'pellet') {
        squares[nextIndex].classList.remove('pellet');
      }
      if (pelletState[nextIndex] === 'powerPellet') {
        squares[nextIndex].classList.remove('powerPellet');
      }

      // Khôi phục lớp pellet/powerPellet cho ô trước đó nếu cần
      squares[ghost.currentIndex].classList.remove('pellet', 'powerPellet');
      if (pelletState[ghost.currentIndex] === 'pellet') {
        squares[ghost.currentIndex].classList.add('pellet');
      }
      if (pelletState[ghost.currentIndex] === 'powerPellet') {
        squares[ghost.currentIndex].classList.add('powerPellet');
      }

      // ...di chuyển ghost, bọc hầm, và thêm ghost vào ô mới...
    }

    // ...trạng thái sợ hãi, va chạm, và logic khác...
  }, ghost.speed);
}

Các bài viết liên quan khác của tôi

  • Ania Kubow: Xây dựng Tetris với JavaScript
  • Nắm vững Kiến thức JavaScript: Khai thác Sự sẵn sàng của Framework
  • Nắm vững JavaScript: Sức mạnh của forEach và Toán tử Ternary
  • JavaScript: Hiểu các phương pháp Set và Clear Interval

Kết luận

State trong JavaScript là dữ liệu đại diện cho điều kiện hiện tại của chương trình của bạn. Trong các game như game Pac-Man mà tôi đang làm việc, state thường được sử dụng để theo dõi vị trí người chơi, điểm số, và những gì có trên bảng, trong số những thứ khác. Khác với React, nơi mà state được quản lý bằng hooks hoặc thuộc tính lớp, trong JavaScript thuần, state được sử dụng trong các biến, mảng hoặc đối tượng.

Trước khi triển khai state trong dự án của mình, tôi đã phụ thuộc vào DOM cho logic game của mình, điều này đã chứng tỏ không ổn định. Cụ thể, khi Pac-Man và các ghost vào một div chứa một pellet, tôi sử dụng phương thức contains() của JavaScript để kiểm tra xem nó có lớp pellet hay không và sau đó xử lý logic. Kể từ khi thay đổi cách tiếp cận của mình trong việc xử lý logic bằng cách sử dụng state, game của tôi đã trở nên ổn định hơn rất nhiều, và tôi dự định sẽ tiếp tục cải tiến mã của mình để sử dụng state trên toàn bộ dự án.

Cách tiếp cận quản lý trạng thái cung cấp cho dự án và game của bạn một nguồn thông tin duy nhất. Trong khi DOM chỉ hiển thị những gì đang xảy ra trên màn hình, dữ liệu thực sự nằm trong state của bạn. Bằng cách sử dụng quản lý trạng thái cho logic của bạn thay vì phụ thuộc vào DOM hình ảnh, bạn có thể nâng cao đáng kể tính ổn định và hiệu suất của các game của mình!

Hãy kết nối!

Tôi đang hoạt động trên LinkedInTwitter.

Bạn đã hiểu rõ hơn về quản lý trạng thái trong các game JavaScript chưa? Bạn đã sẵn sàng để áp dụng các kỹ thuật này vào dự án của mình? Hãy chia sẻ bài viết và bình luận nhé!

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào