Node.js、Socket.IO、Redis を使用してスケーラブルなリアルタイム マルチプレイヤー Tic-Tac-Toe ゲームを作成する
このチュートリアルでは、リアルタイム マルチプレイヤー 三目並べ ゲームを構築します。 Node.js を使用する 、Socket.IO 、 そしてRedis 。このゲームでは、2 人のプレーヤーが異なるブラウザ タブから接続し、交代でプレイし、プレイ中にリアルタイムの更新を確認できます。 Redis を使用します。 複数の WebSocket サーバー間でのゲーム ステートの同期を管理し、アプリケーションをスケーラブルにします。
最終的には、リアルタイム機能を備えた完全に機能するゲームが完成し、WebSocket と Redis を使用してスケーラブルなリアルタイム アプリケーションを構築する方法をしっかりと理解できるようになります。
何を学ぶか
-
Socket.IO の使用方法 リアルタイム通信用。
-
Redis Pub/Sub の使用方法 複数のクライアント間でゲームの状態を同期します。
-
スケーラブルな WebSocket サーバー アーキテクチャをセットアップする方法。
前提条件
始める前に、以下がインストールされていることを確認してください。
-
Node.js (v16 以降)
-
レディス
-
Docker (オプション、コンテナ内で Redis を実行する場合)
-
JavaScript、Node.js、WebSocket の基本的な知識。
目次
-
プロジェクトの概要
-
ステップ 1:開発環境のセットアップ
-
ステップ 2:プロジェクトのセットアップ
-
ステップ 3:Redis を使用した WebSocket サーバーの実装
-
ステップ 4:React フロントエンド インターフェイスを実装する
-
ステップ 5:アプリケーションの実行
-
ステップ 6:Redis メッセージをリアルタイムで表示する
-
デモ
-
結論
プロジェクトの概要
次の機能を備えたリアルタイム三目並べゲームを構築します。
-
プレイヤー 2 人 接続してゲームをプレイできます。
-
ゲーム ボードは、さまざまなブラウザ タブにわたってリアルタイムで更新されます。
-
ゲームは勝者を発表するか、ボードがいっぱいになったときに引き分けを宣言します。
以下を使用します:
-
Node.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.環境変数を作成する
.env を作成します プロジェクト ルートに次の内容のファイルを作成します。
PORT=3000
REDIS_HOST=localhost
REDIS_PORT=6379
ステップ 3:Redis を使用した WebSocket サーバーの実装
このステップでは、Node.js を使用してリアルタイムのゲーム インタラクションを処理する WebSocket サーバーをセットアップします。 、Socket.IO 、 そしてRedis 。このサーバーは、ゲームの状態を管理し、プレーヤーの動きを処理し、Redis Pub/Sub を使用して複数のクライアント間の同期を確保します。
コードの各セクションを詳しく説明して、すべてがどのように組み合わされるかを正確に理解できるようにします。サーバー コードの説明
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: "http://localhost:5173",
methods: ["GET", "POST"],
}
});
-
ドテンフ :
.envから環境変数をロードします。 ファイルを使用して、ポートやキーなどの機密情報を安全に保ちます。 -
急行 :HTTP リクエストを処理するための基本的な Express サーバーをセットアップします。
-
http :Node の組み込み
httpを使用して HTTP サーバーを作成します。 モジュール。Socket.IO で使用します。 WebSocket 通信用。 -
Socket.IO :このライブラリにより、サーバーとクライアント間のリアルタイムの双方向通信が可能になります。
-
CORS 構成 :
localhost:5173で実行されているフロントエンドからのクロスオリジン リクエストを許可します。 .
次に、Redis パブリッシャー クライアントとサブスクライバー クライアントを作成するために、次のコードを server.js に追加します。 :
// Initialize Redis clients
const pubClient = createClient();
const subClient = createClient();
await pubClient.connect();
await subClient.connect();
Redis を使用します 接続されたクライアント間のリアルタイムのデータ同期を処理します。
-
パブリッククライアント :メッセージ (ゲーム状態の更新など) を公開するために使用されます。
-
サブクライアント :メッセージを購読します (更新をリッスンします)。
- connect() :Redis サーバーへの接続を確立します。
このパラダイムでは、1 つのクライアントは更新の公開に使用され、もう 1 つのクライアントは更新をサブスクライブします。これは、Redis クライアントがサブスクライブしているため、ブロック動作を回避するのに役立ちます。 モードではメッセージの受信のみが可能です。
ゲームの更新のために Redis チャンネルを購読するには、次のコードを 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 :
game-movesでメッセージをリッスンします。 チャンネル。 -
プレイヤーが新しい動きをするたびに、ゲームの状態が Redis で更新され、接続されているすべてのクライアントに新しい状態が通知されます。
-
messageパラメータにはゲームの状態が文字列として含まれます。これを JavaScript オブジェクトに解析し、Socket.IO を使用して更新された状態をブロードキャストします。 .
次に、ゲームの状態と関数を定義するために、次のコードを 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,
};
}
-
ゲームの状態 :ボードの現在の状態と誰のターンであるかを追跡します (
xIsNext) ).-
ボードは 9 つのセルの配列として表されます (各セルは「X」、「O」、または
nullにすることができます) ). -
xIsNextフラグによって、どのプレイヤーのターンになるかが決まります。
-
-
resetGame() :ボードと方向指示器を初期状態にリセットし、新しいゲームを開始できるようにします。
次に、WebSocket 接続を処理するために、次のコードを server.js に追加しましょう。 :
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);
-
io.on('connection')イベントは、新しいクライアントが接続するとトリガーされます。 -
ソケット.id :接続されている各クライアントの一意の識別子。
-
現在の
gameStateをすぐに送信します。 新しいクライアントに現在のボードを表示できるようにします。
プレイヤーの動きを処理するには、次のコードを server.js に追加します。 :
// 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);
});
-
メイクムーブ :このイベントは、プレーヤーがセルをクリックするとトリガーされます。
-
検証 :移動する前に、セルがすでに占有されているかどうか、またはゲームが終了しているかどうかを確認します。
-
ゲーム状態の更新 :動きが有効な場合、ボードを更新し、ターンを切り替えます。
-
-
更新されたゲームの状態は次のようになります。
<オル> -
Redis に公開 :これにより、サーバーのすべてのインスタンスが確実に同期されます。
-
すべてのクライアントにブロードキャストされます :これにより、すべてのプレイヤーのゲームボードが即座に更新されます。
ゲームの再起動を処理するには、次のコードを server.js に追加します。 :
// Handle game restarts
socket.on('restartGame', () => {
resetGame();
io.emit('gameState', gameState);
});
クライアントの切断処理を処理するには、次のコードを server.js に追加します。 :
socket.on('disconnect', () => {
console.log('Client disconnected:', socket.id);
});
});
最後に、ゲームのロジックを処理するために、次の関数を 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() :ボード上に勝ちの組み合わせがあるかどうかを確認します。
-
isBoardFull() :すべてのセルが埋まっているかどうかをチェックし、引き分けを示します。
ステップ 4:React フロントエンド インターフェイスを実装する
このステップでは、三目並べゲーム用のシンプルでインタラクティブな React フロントエンドを構築します。このフロントエンドにより、プレイヤーは WebSocket サーバーに接続し、手を動かし、ゲーム ボードの更新をリアルタイムで確認できるようになります。
App.jsx で 、次のコードを追加します。
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 接続 :
- フロントエンドは、
socket.io-clientを使用してサーバーへの接続を確立します。 .
- フロントエンドは、
-
状態管理 :
-
ゲームの状態 (
gameState) ) は React のuseStateで管理されます 以下が含まれます:-
ボード (9 セル)。
-
フラグ xIsNext 現在のプレイヤーのターンを示します。
-
勝者 ステータス。
-
-
-
リアルタイム更新 :
-
useEffectフック:-
gameStateをリッスンします サーバーからの更新。 -
変更が検出されると、ローカル ゲームの状態を更新します。
-
コンポーネントがアンマウントされるときに、WebSocket リスナーをクリーンアップします。
-
-
-
プレイヤーの動きの処理 :
-
handleClick関数:-
移動を許可する前に、セルがすでに占有されているかどうか、またはゲームに勝者がいるかどうかを確認します。
-
makeMoveを送信します クリックされたセル インデックスを含むイベントをサーバーに送信します。
-
-
-
ゲームボードのレンダリング :
-
renderCell関数はボード上の各セルにボタンを作成します。 -
ボードは 3x3 グリッドを使用して表示されます。
-
-
ゲームを再開 :
- 「ゲームを再開」ボタンは
restartGameを出力します。 すべてのプレイヤーのゲームボードをリセットするイベント
- 「ゲームを再開」ボタンは
-
ユーザー インターフェース :
- プレーヤーが順番に交代してリアルタイムで更新を確認できる、シンプルでインタラクティブなレイアウト
ステップ 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}"
動きが行われるか、ゲーム状態が変化するたびに、サーバーは更新されたゲーム状態を game-moves に公開します。 チャンネル。 redis-cli を使用する 、ゲームのプレイ中にこれらの更新をリアルタイムで監視できます。
デモ
このデモでは、Tic Tac Toe ゲームがローカルで実行され、プレイヤーが交代するにつれてリアルタイムで更新される様子が表示されます。
ゲームプレイでは、ターンの切り替え、ボードの更新、ゲーム状態のアナウンス (勝者または引き分け) などの機能が紹介されます。これは、ゲームが WebSocket 通信を活用してスムーズでインタラクティブなエクスペリエンスを提供する方法を強調しています。
結論
おめでとうございます。Node.js、Socket.IO、Redis を使用してリアルタイム マルチプレイヤー 三目並べゲームを構築することができました。学んだことは次のとおりです。
-
Socket.IO を使用したリアルタイム WebSocket 通信 .
-
Redis Pub/Sub を使用したゲーム状態管理 .
-
React を使用してレスポンシブなフロントエンドを構築する .
次のステップ
-
プレーヤー認証を追加します。
-
チャット機能を実装します。
-
スケーラビリティを確保するために、アプリケーションをクラウド プロバイダーにデプロイします。
コーディングを楽しんでください!
無料でコーディングを学びましょう。 freeCodeCamp のオープンソース カリキュラムは、40,000 人以上の人々が開発者としての職に就くのに役立ちました。始めましょう
-
RedisInsight1.6はRedisGearsサポートとRedis6ACL互換性をもたらします
RedisInsightはRedis用の簡単で直感的なGUIであり、最も人気のあるRedisモジュールのサポートが組み込まれているため、すべてのデータベースを監視してデータを管理できます。データベースのメモリ使用量を分析し、そのパフォーマンスをプロファイルするためのツールを提供します。 最新リリースのRedisInsight1.6で、RedisInsightは、開発者エクスペリエンスをさらに楽しくするように設計された新しい機能と拡張機能を備えた、もう1つの重要なマイルストーンに到達します。このブログ投稿では、RedisInsightの最新の開発に関するすべての詳細を学びます。 概要
-
LangChain、Faiss、Next.js を使用してカスタム AI チャットボットを構築する – 実践ガイド
この投稿では、Upstash、Next.js、LangChain、Fly.io を使用してオープンソースのカスタム コンテンツ AI チャットボットを構築した方法について説明します。 Upstash は、モデル トレーニングのスケジュールを立てるのに役立ち、寛大なレート制限と OpenAI API 応答のキャッシュの方法を提供してくれました。 使用するもの Next.js(フロントエンドとバックエンド) LangChain (言語モデルを利用したアプリケーション開発用フレームワーク) Upstash(QStash によるトレーニング モデルのスケジュール設定、レート制限と OpenA