Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

สร้างเกม TicTacToe ผู้เล่นหลายคนแบบเรียลไทม์ที่ปรับขนาดได้ด้วย Node.js, Socket.IO และ Redis

สร้างเกม TicTacToe ผู้เล่นหลายคนแบบเรียลไทม์ที่ปรับขนาดได้ด้วย Node.js, Socket.IO และ Redis

ในบทช่วยสอนนี้ เราจะสร้างเกม Tic-Tac-Toe ที่มีผู้เล่นหลายคนแบบเรียลไทม์ โดยใช้ Node.js , Socket.IO และ แดง . เกมนี้อนุญาตให้ผู้เล่นสองคนเชื่อมต่อจากแท็บเบราว์เซอร์ที่แตกต่างกัน ผลัดกันเล่น และดูการอัปเดตแบบเรียลไทม์ขณะเล่น เราจะใช้ Redis เพื่อจัดการการซิงโครไนซ์สถานะของเกมบนเซิร์ฟเวอร์ WebSocket หลายเซิร์ฟเวอร์ ทำให้แอปพลิเคชันของเราปรับขนาดได้

ในตอนท้าย คุณจะมีเกมที่มีฟังก์ชันการทำงานเต็มรูปแบบพร้อมความสามารถแบบเรียลไทม์และความเข้าใจที่ชัดเจนเกี่ยวกับวิธีการใช้ WebSockets และ Redis เพื่อสร้างแอปพลิเคชันแบบเรียลไทม์ที่ปรับขนาดได้

สิ่งที่คุณจะได้เรียนรู้

  • วิธีใช้ Socket.IO สำหรับการสื่อสารแบบเรียลไทม์

  • วิธีใช้ Redis Pub/Sub เพื่อซิงโครไนซ์สถานะของเกมกับไคลเอนต์หลายตัว

  • วิธีการตั้งค่าสถาปัตยกรรมเซิร์ฟเวอร์ WebSocket ที่ปรับขนาดได้

ข้อกำหนดเบื้องต้น

ก่อนที่เราจะเริ่ม ตรวจสอบให้แน่ใจว่าคุณได้ติดตั้งสิ่งต่อไปนี้แล้ว:

  • Node.js (v16 หรือสูงกว่า)

  • เรดดิส

  • นักเทียบท่า (เป็นทางเลือก สำหรับการเรียกใช้ Redis ในคอนเทนเนอร์)

  • ความรู้พื้นฐานเกี่ยวกับ JavaScript, Node.js และ WebSockets

สารบัญ

  • ภาพรวมโครงการ

  • ขั้นตอนที่ 1:การตั้งค่าสภาพแวดล้อมการพัฒนาของคุณ

  • ขั้นตอนที่ 2:การตั้งค่าโครงการ

  • ขั้นตอนที่ 3:การใช้งาน WebSocket Server ด้วย Redis

  • ขั้นตอนที่ 4:ใช้อินเทอร์เฟซ React Frontend

  • ขั้นตอนที่ 5:เรียกใช้แอปพลิเคชัน

  • ขั้นตอนที่ 6:การดูข้อความ Redis แบบเรียลไทม์

  • สาธิต

  • บทสรุป

ภาพรวมโครงการ

เราจะสร้างเกม Tic-Tac-Toe แบบเรียลไทม์พร้อมคุณสมบัติดังต่อไปนี้:

  • ผู้เล่นสองคน สามารถเชื่อมต่อและเล่นเกมได้

  • บอร์ดเกมจะอัปเดตตามเวลาจริงผ่านแท็บเบราว์เซอร์ต่างๆ

  • เกมจะประกาศผู้ชนะหรือเสมอเมื่อกระดานเต็ม

เราจะใช้:

  • โหนด js ด้วย Socket.IO สำหรับการจัดการการเชื่อมต่อ WebSocket

  • เรดิส Pub/Sub เพื่อจัดการการซิงโครไนซ์สถานะของเกมกับไคลเอนต์

ขั้นตอนที่ 1:การตั้งค่าสภาพแวดล้อมการพัฒนาของคุณ

การติดตั้ง Node.js

ตรวจสอบให้แน่ใจว่าคุณได้ติดตั้ง Node.js บนระบบของคุณ:

node -v

หากคุณยังไม่ได้ติดตั้ง ให้ดาวน์โหลดจาก Node.js

การติดตั้ง Redis

คุณสามารถติดตั้ง Redis ในเครื่องหรือเรียกใช้ในคอนเทนเนอร์ Docker ได้

macOS (ใช้ Homebrew)

ขั้นแรก ตรวจสอบให้แน่ใจว่าคุณได้ติดตั้ง Homebrew บนระบบของคุณก่อนที่จะรันคำสั่งด้านล่าง:

brew install redis
brew services start redis

ตรวจสอบว่าคอนเทนเนอร์ Redis ทำงานด้วยคำสั่งต่อไปนี้:

redis-cli ping

คุณควรเห็น:

PONG

การใช้ Docker เพื่อเรียกใช้ Redis

docker run --name redis-server -p 6379:6379 -d redis

ตรวจสอบว่า Redis ทำงานโดยใช้:

docker exec -it redis-server redis-cli ping

ขั้นตอนที่ 2:การตั้งค่าโครงการ

1. สร้างไดเรกทอรีโครงการ

mkdir tic-tac-toe
cd tic-tac-toe
npm init -y

2. ติดตั้งการอ้างอิง

npm install express socket.io redis dotenv

3. สร้างตัวแปรสภาพแวดล้อม

สร้าง 09 ในรูทโปรเจ็กต์ของคุณโดยมีเนื้อหาดังต่อไปนี้:

PORT=3000
REDIS_HOST=localhost
REDIS_PORT=6379

ขั้นตอนที่ 3:การใช้งาน WebSocket Server ด้วย Redis

ในขั้นตอนนี้ เราจะตั้งค่าเซิร์ฟเวอร์ WebSocket ที่จัดการการโต้ตอบของเกมแบบเรียลไทม์โดยใช้ Node.js , Socket.IO และ แดง . เซิร์ฟเวอร์นี้จะจัดการสถานะของเกม จัดการการเคลื่อนไหวของผู้เล่น และรับรองการซิงโครไนซ์กับไคลเอนต์หลายตัวโดยใช้ Redis Pub/Sub

เราจะแจกแจงโค้ดแต่ละส่วนเพื่อให้คุณเข้าใจว่าทุกอย่างเข้ากันได้อย่างไรคำอธิบายโค้ดเซิร์ฟเวอร์

สร้างไฟล์ชื่อ 13 และเพิ่มรหัสต่อไปนี้:

import dotenv from 'dotenv';
import express from 'express';
import http from 'http';
import { Server } from 'socket.io';
import { createClient } from 'redis';
dotenv.config(); // Load environment variables from .env file
const app = express();
const server = http.createServer(app);
const io = new Server(server, {
 cors: {
 origin: "http://localhost:5173",
 methods: ["GET", "POST"],
 }
});
  • โดเทนวี :โหลดตัวแปรสภาพแวดล้อมจาก 27 ไฟล์เพื่อเก็บข้อมูลสำคัญเช่นพอร์ตและคีย์ให้ปลอดภัย

  • ด่วน :ตั้งค่าเซิร์ฟเวอร์ Express พื้นฐานเพื่อจัดการคำขอ HTTP

  • http :เราสร้างเซิร์ฟเวอร์ HTTP โดยใช้ 32 ในตัวของ Node โมดูลซึ่งเราจะใช้กับ Socket.IO สำหรับการสื่อสาร WebSocket

  • Socket.IO :ไลบรารีนี้เปิดใช้งานการสื่อสารแบบสองทิศทางแบบเรียลไทม์ระหว่างเซิร์ฟเวอร์และไคลเอนต์

  • การกำหนดค่า CORS :อนุญาตคำขอข้ามต้นทางจากส่วนหน้าของเราที่ทำงานบน 43 .

จากนั้น เพื่อสร้างลูกค้าผู้เผยแพร่และสมาชิก Redis เราจะเพิ่มโค้ดต่อไปนี้ใน 53 :

// Initialize Redis clients
const pubClient = createClient();
const subClient = createClient();
await pubClient.connect();
await subClient.connect();

เราใช้ เรดดิส เพื่อจัดการการซิงโครไนซ์ข้อมูลแบบเรียลไทม์ระหว่างไคลเอนต์ที่เชื่อมต่อ

  • pubClient :ใช้เพื่อเผยแพร่ข้อความ (เช่น การอัปเดตสถานะของเกม)

  • ลูกค้าย่อย :สมัครรับข้อความ (ฟังการอัปเดต)

  • เชื่อมต่อ() :สร้างการเชื่อมต่อกับเซิร์ฟเวอร์ Redis

ในกระบวนทัศน์นี้ ไคลเอนต์หนึ่งถูกใช้เพื่อเผยแพร่การอัพเดต และอีกอันหนึ่งสมัครรับการอัพเดต ซึ่งจะช่วยหลีกเลี่ยงพฤติกรรมการบล็อก เนื่องจากไคลเอ็นต์ Redis อยู่ใน สมัครสมาชิก โหมดสามารถรับได้เฉพาะข้อความเท่านั้น

หากต้องการสมัครรับข้อมูลช่อง Redis สำหรับการอัปเดตเกม เราจะเพิ่มโค้ดต่อไปนี้ลงใน 65 :

// Subscribe to the Redis channel for game updates
await subClient.subscribe('game-moves', (message) => {
 gameState = JSON.parse(message);
 io.emit('gameState', gameState);
});
  • subClient.subscribe :ฟังข้อความบน 75 ช่อง.

  • เมื่อใดก็ตามที่ผู้เล่นทำการเคลื่อนไหวใหม่ สถานะของเกมจะได้รับการอัปเดตใน Redis และไคลเอนต์ที่เชื่อมต่อทั้งหมดจะได้รับแจ้งเกี่ยวกับสถานะใหม่

  • 88 พารามิเตอร์ประกอบด้วยสถานะของเกมเป็นสตริง เราแยกวิเคราะห์ออกเป็นวัตถุ JavaScript และเผยแพร่สถานะที่อัปเดตโดยใช้ Socket.IO .

ต่อไป เพื่อกำหนดสถานะและฟังก์ชันของเกม เราจะเพิ่มโค้ดต่อไปนี้ใน 92 :

// Define initial game state
let gameState = {
 board: Array(9).fill(null),
 xIsNext: true,
};
// Function to reset the game
function resetGame() {
 gameState = {
 board: Array(9).fill(null),
 xIsNext: true,
 };
}
  • สถานะเกม :ติดตามสถานะปัจจุบันของบอร์ดและตาของใคร (103 ).

    • กระดานจะแสดงเป็นอาร์เรย์จำนวน 9 เซลล์ (แต่ละเซลล์สามารถเป็น 'X', 'O' หรือ 114 ).

    • 126 ธงจะกำหนดตาของผู้เล่นคนใด

  • รีเซ็ตเกม() :รีเซ็ตบอร์ดและไฟเลี้ยวเป็นสถานะเริ่มต้น เพื่อให้สามารถเริ่มเกมใหม่ได้

ต่อไป ในการจัดการการเชื่อมต่อ WebSocket ให้เพิ่มโค้ดต่อไปนี้ใน 135 :

io.on('connection', (socket) => {
 console.log('New client connected:', socket.id);
 // Send the current game state to the newly connected client
 socket.emit('gameState', gameState);
  • 141 เหตุการณ์จะถูกทริกเกอร์เมื่อไคลเอนต์ใหม่เชื่อมต่อ

  • socket.id :ตัวระบุที่ไม่ซ้ำกันสำหรับไคลเอ็นต์ที่เชื่อมต่อแต่ละเครื่อง

  • เราส่ง 154 ปัจจุบันทันที ให้กับลูกค้าใหม่เพื่อให้พวกเขาสามารถดูบอร์ดปัจจุบันได้

เพื่อจัดการกับการเคลื่อนไหวของผู้เล่น เราจะเพิ่มโค้ดต่อไปนี้ใน 168 :

 // Handle player moves
 socket.on('makeMove', (index) => {
 // Prevent making a move if cell is already taken or game is over
 if (gameState.board[index] || calculateWinner(gameState.board)) return;
 // Update the board and switch turns
 gameState.board[index] = gameState.xIsNext ? 'X' : 'O';
 gameState.xIsNext = !gameState.xIsNext;
 // Publish the updated game state to Redis
 pubClient.publish('game-moves', JSON.stringify(gameState));
 io.emit('gameState', gameState);
 });
  • เมคมูฟ :เหตุการณ์นี้จะเกิดขึ้นเมื่อผู้เล่นคลิกบนเซลล์

    • การตรวจสอบความถูกต้อง :เราตรวจสอบว่าเซลล์ถูกครอบครองแล้วหรือเกมจบลงแล้วก่อนที่จะทำการย้าย

    • การอัปเดตสถานะของเกม :หากการย้ายถูกต้อง เราจะอัปเดตบอร์ดและสลับผลัดกัน

  • สถานะของเกมที่อัปเดตคือ:

    1. เผยแพร่ไปยัง Redis :สิ่งนี้ทำให้แน่ใจได้ว่าอินสแตนซ์ทั้งหมดของเซิร์ฟเวอร์ยังคงซิงค์อยู่

    2. เผยแพร่ไปยังลูกค้าทุกคน :นี่เป็นการอัปเดตบอร์ดเกมสำหรับผู้เล่นทุกคนทันที

เพื่อจัดการกับการรีสตาร์ทเกม เราจะเพิ่มโค้ดต่อไปนี้ใน 172 :

// Handle game restarts
socket.on('restartGame', () => {
 resetGame();
 io.emit('gameState', gameState);
});

ในการจัดการกับการขาดการเชื่อมต่อของไคลเอ็นต์ เราจะเพิ่มโค้ดต่อไปนี้ใน 185 :

 socket.on('disconnect', () => {
 console.log('Client disconnected:', socket.id);
 });
});

สุดท้ายนี้ เพื่อประมวลผลตรรกะของเกม เราจะเพิ่มฟังก์ชันต่อไปนี้ใน 194 :

// Function to check if there's a winner
function calculateWinner(board) {
 const lines = [
 [0, 1, 2], [3, 4, 5], [6, 7, 8],
 [0, 3, 6], [1, 4, 7], [2, 5, 8],
 [0, 4, 8], [2, 4, 6]
 ];
 for (let [a, b, c] of lines) {
 if (board[a] && board[a] === board[b] && board[a] === board[c]) {
 return board[a];
 }
 }
 return null;
}
function isBoardFull(board) {
 return board.every((cell) => cell !== null);
}
  • คำนวณผู้ชนะ() :ตรวจสอบว่ามีชุดค่าผสมที่ชนะบนกระดานหรือไม่

  • isBoardFull() :ตรวจสอบว่าเซลล์ทั้งหมดเต็มหรือไม่ ซึ่งบ่งชี้ว่าเสมอกัน

ขั้นตอนที่ 4:ใช้อินเทอร์เฟซ React Frontend

ในขั้นตอนนี้ เราสร้างส่วนหน้า React ที่เรียบง่ายและโต้ตอบได้สำหรับเกม Tic-Tac-Toe ของเรา ส่วนหน้านี้อนุญาตให้ผู้เล่นเชื่อมต่อกับเซิร์ฟเวอร์ WebSocket ทำการเคลื่อนไหว และดูการอัปเดตบอร์ดเกมแบบเรียลไทม์

ใน 202 ให้เพิ่มโค้ดต่อไปนี้:

import React, { useEffect, useState } from 'react';
import io from 'socket.io-client';
const socket = io('http://localhost:3000');
function App() {
 const [gameState, setGameState] = useState({
 board: Array(9).fill(null),
 xIsNext: true,
 winner: null
 });
 useEffect(() => {
 socket.on('gameState', (state) => {
 setGameState(state);
 });
 return () => socket.off('gameState');
 }, []);
 const handleClick = (index) => {
 if (gameState.board[index] || gameState.winner) return;
 socket.emit('makeMove', index);
 };
 const renderCell = (index) => (
 <button onClick={() => handleClick(index)}>{gameState.board[index]}</button>
 );
 return (
 <div>
 <h1>Multiplayer Tic-Tac-Toe</h1>
 <div className="board">
 {[...Array(9)].map((_, i) => renderCell(i))}
 </div>
 <button onClick={() => socket.emit('restartGame')}>Restart Game</button>
 </div>
 );
}
export default App;

ต่อไปนี้เป็นบทสรุปเกี่ยวกับวิธีการแยกย่อยแอป React:

  • การเชื่อมต่อ WebSocket :

    • ส่วนหน้าสร้างการเชื่อมต่อกับเซิร์ฟเวอร์โดยใช้ 218 .
  • การจัดการของรัฐ :

    • สถานะของเกม (220 ) ได้รับการจัดการด้วย 230 ของ React และรวมถึง:

      • กระดาน (9 เซลล์)

      • ธง xIsNext เพื่อระบุตาของผู้เล่นปัจจุบัน

      • ผู้ชนะ สถานะ

  • การอัปเดตตามเวลาจริง :

    • 240 ตะขอ:

      • ฟัง 251 อัปเดตจากเซิร์ฟเวอร์

      • อัปเดตสถานะของเกมในเครื่องเมื่อตรวจพบการเปลี่ยนแปลง

      • ทำความสะอาด WebSocket Listener เมื่อส่วนประกอบไม่ได้ต่อเชื่อม

  • การจัดการการเคลื่อนไหวของผู้เล่น :

    • 268 ฟังก์ชัน:

      • ตรวจสอบว่าเซลล์ถูกครอบครองแล้วหรือเกมมีผู้ชนะหรือไม่ก่อนที่จะอนุญาตให้เคลื่อนที่

      • ส่ง 276 เหตุการณ์ไปยังเซิร์ฟเวอร์ด้วยดัชนีเซลล์ที่ถูกคลิก

  • การเรนเดอร์บอร์ดเกม :

    • 288 ฟังก์ชั่นสร้างปุ่มสำหรับแต่ละเซลล์บนกระดาน

    • กระดานจะแสดงโดยใช้ตารางขนาด 3x3

  • เริ่มเกมใหม่ :

    • ปุ่ม "เริ่มเกมใหม่" ส่งเสียง 294 กิจกรรมเพื่อรีเซ็ตกระดานเกมสำหรับผู้เล่นทุกคน
  • อินเทอร์เฟซผู้ใช้ :

    • รูปแบบที่เรียบง่ายและโต้ตอบได้ซึ่งช่วยให้ผู้เล่นผลัดกันดูการอัปเดตแบบเรียลไทม์

ขั้นตอนที่ 5:การเรียกใช้แอปพลิเคชัน

การเริ่มต้นแบ็กเอนด์

หากต้องการเริ่มต้นเซิร์ฟเวอร์ส่วนหลัง ให้เปิดหน้าต่างเทอร์มินัลใหม่และรันคำสั่งต่อไปนี้:

cd tic-tac-toe
npm start

การเริ่มต้นส่วนหน้า

หากต้องการเริ่ม React เซิร์ฟเวอร์ส่วนหน้า ให้เปิดหน้าต่างเทอร์มินัลใหม่และรันคำสั่งด้านล่าง (อย่าใช้คำสั่งเดียวกับที่เซิร์ฟเวอร์แบ็คเอนด์ทำงานอยู่ เนื่องจากคุณต้องใช้ทั้งสองอย่างพร้อมกันเพื่อรันเกม)

cd tic-tac-toe-client
npm run dev

การเข้าถึงเกม

เปิดเบราว์เซอร์ของคุณและไปที่:

http://localhost:5173

ขั้นตอนที่ 6:การดูข้อความ Redis แบบเรียลไทม์

ในขณะที่เกมกำลังทำงาน คุณสามารถดูข้อความ Redis เพื่อดูการอัปเดตสถานะของเกมแบบเรียลไทม์ได้

เปิดเทอร์มินัลแล้วรัน:

redis-cli
SUBSCRIBE game-moves

นี่จะแสดงการอัปเดตเกม:

1) "message"
2) "game-moves"
3) "{\"board\":[\"X\",null,\"O\",null,\"X\",null,null,null,null],\"xIsNext\":false}"

ทุกครั้งที่มีการเคลื่อนไหวหรือสถานะของเกมเปลี่ยนแปลง เซิร์ฟเวอร์จะเผยแพร่สถานะเกมที่อัปเดตไปที่ 306 ช่อง. ใช้ 316 คุณสามารถตรวจสอบการอัปเดตเหล่านี้ได้แบบเรียลไทม์ในขณะที่เกมกำลังเล่นอยู่

สาธิต

ในการสาธิตนี้ คุณจะเห็นเกม Tic Tac Toe ทำงานในพื้นที่ ซึ่งสาธิตการอัปเดตแบบเรียลไทม์ในขณะที่ผู้เล่นสลับกัน

รูปแบบการเล่นจะแสดงคุณสมบัติต่างๆ เช่น การสลับเทิร์น การอัพเดตบอร์ด และการประกาศสถานะของเกม (ผู้ชนะหรือเสมอ) สิ่งนี้เน้นย้ำถึงวิธีที่เกมใช้ประโยชน์จากการสื่อสารของ WebSocket เพื่อมอบประสบการณ์การโต้ตอบที่ราบรื่น

บทสรุป

ขอแสดงความยินดี คุณประสบความสำเร็จในการสร้างเกม Tic-Tac-Toe ผู้เล่นหลายคนแบบเรียลไทม์โดยใช้ Node.js, Socket.IO และ Redis นี่คือสิ่งที่คุณได้เรียนรู้:

  • การสื่อสาร WebSocket แบบเรียลไทม์โดยใช้ Socket.IO .

  • การจัดการสถานะของเกมโดยใช้ Redis Pub/Sub .

  • การสร้างส่วนหน้าแบบตอบสนองด้วย React .

ขั้นตอนถัดไป

  • เพิ่มการตรวจสอบผู้เล่น

  • ใช้คุณลักษณะการแชท

  • ปรับใช้แอปพลิเคชันของคุณกับผู้ให้บริการระบบคลาวด์เพื่อความสามารถในการขยายขนาด

ขอให้สนุกกับการเขียนโค้ด!

เรียนรู้การเขียนโค้ดฟรี หลักสูตรโอเพ่นซอร์สของ freeCodeCamp ช่วยให้ผู้คนมากกว่า 40,000 คนได้งานในตำแหน่งนักพัฒนา เริ่มต้น