ใน tutorial นี้ เราจะสร้าง real-time multiplayer Tic-Tac-Toe game ใช้ Node.js, Socket.IO, และ Redis เกมนี้อนุญาตให้สองผู้เล่นเชื่อมต่อจาก browser tabs ต่างกัน เล่นสลับกัน และเห็น real-time updates ขณะเล่น เราจะใช้ Redis จัดการ game state synchronization ข้าม multiple WebSocket servers ทำให้แอปพลิเคชัน scalable
จบ tutorial คุณจะได้เกมที่ทำงานสมบูรณ์ พร้อม real-time capabilities และความเข้าใจมั่นใจเกี่ยวกับการใช้ WebSockets และ Redis สร้าง real-time applications ที่ scalable
สิ่งที่คุณจะได้เรียนรู้
- วิธีใช้ Socket.IO สำหรับ real-time communication
- วิธีใช้ Redis Pub/Sub synchronize game state ข้าม multiple clients
- วิธีตั้งค่า scalable WebSocket server architecture
ข้อกำหนดเบื้องต้น (Prerequisites)
ก่อนเริ่ม ตรวจสอบว่าติดตั้งแล้ว:
- Node.js (v16 หรือสูงกว่า)
- Redis
- Docker (optional, สำหรับรัน Redis ใน container)
- ความรู้พื้นฐาน JavaScript, Node.js, และ WebSockets
สารบัญ
- Project Overview
- Step 1: Setting Up Your Development Environment
- Step 2: Setting Up the Project
- Step 3: Implementing the WebSocket Server with Redis
- Step 4: Implement the React Frontend interface
- Step 5: Running the Application
- Step 6: Viewing Redis Messages in Real-Time
- Demo
- Conclusion
Project Overview
เราจะสร้าง real-time Tic-Tac-Toe game พร้อม features:
- Two players เชื่อมต่อและเล่นเกมได้
- Game board update แบบ real-time ข้าม browser tabs ต่างกัน
- Game ประกาศ winner หรือ draw เมื่อ board เต็ม
เราจะใช้:
- Node.js กับ Socket.IO จัดการ WebSocket connections
- Redis Pub/Sub จัดการ game state synchronization ข้าม clients
Step 1: Setting Up Your Development Environment
Installing Node.js
ตรวจสอบว่า Node.js ติดตั้งอยู่:
node -v
หายังไม่มี ดาวน์โหลดจาก Node.js
Installing Redis
ติดตั้ง Redis locally หรือรันใน Docker container
macOS (Using Homebrew)
ตรวจสอบว่า Homebrew ติดตั้งอยู่ก่อนรันคำสั่งด้านล่าง:
brew install redis
brew services start redis
Verify Redis container รันอยู่:
redis-cli ping
ควรเห็น:
PONG
Using Docker to Run Redis
docker run --name redis-server -p 6379:6379 -d redis
ตรวจสอบ Redis รันอยู่:
docker exec -it redis-server redis-cli ping
Step 2: Setting Up the Project
1. Create the Project Directory
mkdir tic-tac-toe
cd tic-tac-toe
npm init -y
2. Install Dependencies
npm install express socket.io redis dotenv
3. Create Environment Variables
สร้าง .env ใน project root:
PORT=3000
REDIS_HOST=localhost
REDIS_PORT=6379
Step 3: Implementing the WebSocket Server with Redis
ในขั้นตอนนี้ เราจะตั้งค่า WebSocket server จัดการ real-time game interactions 用 Node.js, Socket.IO, และ Redis Server นี้จะจัดการ game state, handle player moves, และ ensure synchronization ข้าม multiple clients ใช้ Redis Pub/Sub
เราจะแยกแต่ละส่วนของโค้ด เพื่อให้คุณเข้าใจว่าทุกอย่างเข้ากันอย่างไร Server Code Explanation
สร้างไฟล์ server.js ใส่โค้ด:
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: "https://localhost:5173",
methods: ["GET", "POST"],
}
});
- dotenv: โหลด environment variables จาก
.envfile เก็บข้อมูล sensitive เช่น ports และ keys ให้ปลอดภัย - express: ตั้งค่า Express server พื้นฐานจัดการ HTTP requests
- http: สร้าง HTTP server 用 Node's built-in
httpmodule ใช้กับ Socket.IO สำหรับ WebSocket communication - Socket.IO: library นี้ enable real-time, bidirectional communication ระหว่าง server และ clients
- CORS Configuration: อนุญาต cross-origin requests จาก frontend รันบน
localhost:5173
ต่อไป สร้าง Redis publisher และ subscriber clients ใส่ใน server.js:
// Initialize Redis clients
const pubClient = createClient();
const subClient = createClient();
await pubClient.connect();
await subClient.connect();
เราใช้ Redis จัดการ real-time data synchronization ระหว่าง connected clients
- pubClient: ใช้ publish messages (เช่น game state updates)
- subClient: subscribe messages (ฟัง updates)
- connect(): สร้างการเชื่อมต่อไป Redis server
ใน paradigm นี้ ใช้ client หนึ่ง publish updates อีกอัน subscribe updates นี่ช่วยหลีกเลี่ยง blocking behavior เนื่องจาก Redis clients ใน subscribe mode รับ messages ได้อย่างเดียว
เพื่อ subscribe Redis channels สำหรับ game updates ใส่โค้ดใน server.js:
// Subscribe to the Redis channel for game updates
await subClient.subscribe('game-moves', (message) => {
gameState = JSON.parse(message);
io.emit('gameState', gameState);
});
- subClient.subscribe: ฟัง messages บน channel
game-moves - ทุกครั้งที่ player ทำ move game state จะ update ใน Redis และ clients ทั้งหมดจะได้รับแจ้ง state ใหม่
- พารามิเตอร์
messageมี game state เป็น string เรา parse เป็น JavaScript object และ broadcast updated state 用 Socket.IO
ต่อไป กำหนด game state และ functions ใส่ใน server.js:
// 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,
};
}
- gameState: ติดตาม current state ของ board และ turn ของใคร (
xIsNext)- Board แทนเป็น array 9 cells (แต่ละอันเป็น 'X', 'O', หรือ
null) - Flag
xIsNextกำหนดว่า turn ของ player ไหน
- Board แทนเป็น array 9 cells (แต่ละอันเป็น 'X', 'O', หรือ
- resetGame(): รีเซ็ต board และ turn indicator กลับ initial state เริ่มเกมใหม่ได้
ต่อไป จัดการ WebSocket connections ใส่ใน server.js:
io.on('connection', (socket) => {
console.log('New client connected:', socket.id);
// Send current game state to newly connected client
socket.emit('gameState', gameState);
- Event
io.on('connection')trigger เมื่อ client ใหม่เชื่อมต่อ - socket.id: unique identifier สำหรับแต่ละ connected client
- เราส่ง
gameStateปัจจุบันไป client ใหม่ทันที เพื่อให้เห็น board ปัจจุบัน
เพื่อจัดการ player moves ใส่ใน server.js:
// Handle player moves
socket.on('makeMove', (index) => {
// Prevent making move if cell already taken or game over
if (gameState.board[index] || calculateWinner(gameState.board)) return;
// Update board and switch turns
gameState.board[index] = gameState.xIsNext ? 'X' : 'O';
gameState.xIsNext = !gameState.xIsNext;
// Publish updated game state to Redis
pubClient.publish('game-moves', JSON.stringify(gameState));
io.emit('gameState', gameState);
});
- makeMove: event trigger เมื่อ player คลิก cell
- Validation: เช็คว่า cell ว่างและเกมยังไม่จบก่อนทำ move
- Updating Game State: หาก move valid update board และสลับ turn
- Updated game state แล้ว:
- Published to Redis: ให้ทุก instances ของ server stay in sync
- Broadcasted to all clients: update game board ทุก player ทันที
เพื่อจัดการ game restarts ใส่ใน server.js:
// Handle game restarts
socket.on('restartGame', () => {
resetGame();
io.emit('gameState', gameState);
});
จัดการ client disconnection ใส่ใน server.js:
socket.on('disconnect', () => {
console.log('Client disconnected:', socket.id);
});
});
สุดท้าย เพิ่ม game logic functions ใน server.js:
// 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);
}
- calculateWinner(): เช็คว่ามี winning combination บน board
- isBoardFull(): เช็คว่า cells เต็มทั้งหมด แสดงว่า draw
Step 4: Implement the React Frontend interface
ในขั้นตอนนี้ เราสร้าง React frontend เรียบง่ายและ interactive สำหรับ Tic-Tac-Toe game Frontend นี้ให้ players เชื่อมต่อ WebSocket server, ทำ moves, และเห็น game board update แบบ real-time
ใน App.jsx ใส่โค้ด:
import React, { useEffect, useState } from 'react';
import io from 'socket.io-client';
const socket = io('https://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) => (
);
return (
Multiplayer Tic-Tac-Toe
{[...Array(9)].map((_, i) => renderCell(i))}
);
}
export default App;
สรุปวิธี React app แบ่งส่วน:
WebSocket Connection:
- Frontend เชื่อมต่อ server 用
socket.io-client
- Frontend เชื่อมต่อ server 用
State Management:
- Game state (
gameState) จัดการด้วย React'suseStateรวม:- board (9 cells)
- Flag xIsNext ระบุ turn ของ player ปัจจุบัน
- winner status
- Game state (
Real-Time Updates:
useEffecthook:- ฟัง
gameStateupdates จาก server - Update local game state เมื่อตรวจพบการเปลี่ยนแปลง
- Clean up WebSocket listener เมื่อ component unmount
- ฟัง
Handling Player Moves:
handleClickfunction:- เช็คว่า cell ว่างและเกมยังไม่มี winner ก่อน allow move
- ส่ง event
makeMoveไป server พร้อม index ของ cell ที่คลิก
Game Board Rendering:
renderCellfunction สร้าง button สำหรับแต่ละ cell บน board- Board แสดงเป็น 3x3 grid
Restart Game:
- ปุ่ม "Restart Game" emit event
restartGamereset game board สำหรับทุก players
- ปุ่ม "Restart Game" emit event
User Interface:
- Layout เรียบง่ายและ interactive ให้ players สลับเล่นและเห็น updates real-time
Step 5: Running the Application
Starting the Backend
เริ่ม backend server เปิด terminal ใหม่ รัน:
cd tic-tac-toe
npm start
Starting the Frontend
เริ่ม React frontend server เปิด terminal ใหม่ รัน (อย่าใช้ terminal เดียวกับ backend เพราะต้องรันพร้อมกัน):
cd tic-tac-toe-client
npm run dev
Accessing the Game
เปิดเบราว์เซอร์ ไป:
https://localhost:5173
Step 6: Viewing Redis Messages in Real-Time
ขณะเกมรันอยู่ คุณดู Redis messages ได้ เพื่อเห็น real-time game state updates
เปิด terminal รัน:
redis-cli SUBSCRIBE game-moves
จะแสดง game updates:
1) "message"
2) "game-moves"
3) "{"board":["X",null,"O",null,"X",null,null,null,null],"xIsNext":false}"
ทุกครั้งที่มี move หรือ game state เปลี่ยน Server จะ publish updated game state ไป channel game-moves ใช้ redis-cli monitor updates แบบ real-time ขณะเล่นเกม
Demo
ใน demo นี้ คุณจะเห็น Tic Tac Toe game รัน locally สาธิต real-time updates ขณะ players สลับเล่น
Gameplay แสดง features เช่น turn switching, board updates, และ game state announcements (winner หรือ draw) นี้ highlight ว่าเกมใช้ WebSocket communication ให้ smooth, interactive experience
สรุป
ยินดีด้วย คุณสร้าง real-time multiplayer Tic-Tac-Toe game ด้วย Node.js, Socket.IO, และ Redis เรียบร้อยแล้ว นี่คือสิ่งที่คุณเรียนรู้:
- Real-time WebSocket communication 用 Socket.IO
- Game state management 用 Redis Pub/Sub
- สร้าง responsive front-end ด้วย React
Next Steps
- เพิ่ม player authentication
- Implement chat feature
- Deploy application ไป cloud provider เพื่อ scalability
Happy coding!
Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. Get started