マガジン

オンライン対戦のテスト方法:1人で2人分を試す

遊びをつくる

執筆にClaudeを使用しています。

途切れた小さな橋をつなぎ直し、途中のチェス盤へ戻るための道を整える情景。
記事のテーマをもとに制作したAI生成の写真風イメージ。

オンライン対戦機能を実装したとき、最初に直面する問題が「もう1台の端末がない」「友人を呼べない」という状況です。しかし実際のところ、対戦テストの大半は1人でも十分に行えます。問題は方法を知っているかどうかだけです。 この記事では、ブラウザゲームのオンライン対戦を1人・1台〜2台の環境で検証する具体的な手順と、よくある見落としポイントをまとめます。WebSocketやWebRTCを使ったシグナリング実装を想定していますが、Firebase RealtimeDatabaseやPeerJSのような高レイヤーのサービス経由でも考え方は同じです。

この記事の目次
  1. なぜ1人テストが成立するのか
  2. 基本セットアップ:2タブで対戦を再現する
  3. 確認すべき項目と手順
  4. 遅延を疑似的に再現する方法
  5. 2台目の端末が必要になるタイミング
  6. 次の行動
  7. 参考資料

なぜ1人テストが成立するのか

オンライン対戦の本質は「異なるクライアントが同じ状態を共有できるか」という確認です。この確認に「人間が2人いること」は必須ではありません。必要なのは「2つのクライアントコンテキストが同時に動いていること」です。

ブラウザは1台のPCで複数のウィンドウ・タブを開けます。それぞれが独立したJavaScriptの実行コンテキストを持ち、別のWebSocket接続を張れます。つまり同じPCの2タブが「プレイヤーA」「プレイヤーB」を演じることができます。ネットワーク遅延の再現には工夫が要りますが、状態同期・入力伝達・切断処理といった大半のロジックはこれで検証できます。

基本セットアップ:2タブで対戦を再現する

最も手軽な方法は、同じブラウザで2つのタブを開き、それぞれをプレイヤーとして接続することです。手順は次のとおりです。

  1. ローカル開発サーバー(localhost:3000 など)を起動します。
  2. タブAを開いてゲームを読み込み、「ルーム作成」などの操作でルームIDを取得します。
  3. タブBを開いて同じURLを読み込み、そのルームIDで「参加」します。
  4. タブAとタブBを左右に並べてウィンドウを分割し、両方の画面を同時に見ながら操作します。

ここで重要なのはタブを「シークレットウィンドウ」にしないことです。同じセッションCookieを共有したままでも、WebSocket接続は別コンテキストで開かれるため問題になりません。ただしゲームがlocalStorageのユーザーIDをキーに使っている場合は、タブBがタブAと同じIDを使ってしまいます。この場合はどちらかをシークレットウィンドウにするか、URLパラメータで?player=2のように区別するロジックを一時的に追加します。

確認すべき項目と手順

2タブが接続できたら、次の4つの観点で動作を確かめます。

状態同期の確認

タブAで何らかの操作(駒を動かす・弾を撃つ・スコアを更新する)を行い、タブBの画面に同じ変化が反映されるかを目視で確かめます。反映されない場合はサーバー側のブロードキャストロジック、もしくはクライアント側の受信ハンドラを先に疑います。

よくある失敗は「送信者自身にもイベントを送り返してしまい、処理が二重になる」というものです。サーバー側でsocket.broadcast.emit(送信者を除く全員へ)とio.emit(全員へ)を取り違えているとこれが起きます。2タブで確認するとタブAだけ状態が2回変わるため、すぐ気づけます。

ターン制御・入力権限の確認

「今どちらのターンか」をサーバーが管理している場合、非アクティブ側からの操作を拒否できるかをテストします。タブBのターン中にタブAから不正な操作リクエストを意図的に送り、サーバーが無視するか・エラーを返すかを確かめます。DevToolsのコンソールからsocket.emit('move', {...})と直接叩くと手軽に再現できます。

切断と再接続の確認

タブBを閉じ、タブAがどう振る舞うかを確認します。「相手が切断した」という通知が届くか、ゲームが適切に終了または待機状態になるかを見ます。その後タブBを再度開いて同じルームに再接続を試みたとき、ゲーム状態が復元されるか(あるいは意図どおりに復元されないか)も確認します。

再接続処理を実装していない場合、タブBが切断した瞬間にサーバー側でルームを破棄するのが最もシンプルです。それをしないと、孤立したルームオブジェクトがメモリ上に残り続けます。

メッセージの順序と整合性の確認

高速で連続した操作(連打・連続移動)をタブAから送り、タブBで表示が追いつくかを見ます。ブラウザのスロットリングやWebSocketのバッファリングによって、メッセージが束になって届くことがあります。受信側でタイムスタンプやシーケンス番号を見ているかどうかが問われる場面です。

遅延を疑似的に再現する方法

同一PC内の2タブ通信は実質遅延ゼロです。本番環境では数十〜数百ミリ秒の遅延が発生するため、それを事前に確かめたい場面があります。

ChromeのDevToolsには「Network conditions」というパネルがあり、タブ単位でネットワークスロットリングを設定できます。「Slow 3G」など既存プリセットを選ぶか、カスタムで遅延値(ms)を入力します。タブBだけスロットリングを掛け、「遅延したクライアントから入力が来たとき、ゲームが崩れないか」を確認するのが実用的な使い方です。

ただしこのスロットリングはブラウザのHTTPリクエストに影響しますが、WebSocketの既存接続に対するリアルタイム遅延付与は環境によって挙動が異なります。厳密な遅延テストにはnetem(Linuxのトラフィック制御)やCharlesプロキシのような専用ツールが適しています。開発初期段階であれば、DevToolsの簡易スロットリングで十分な手がかりが得られます。

2台目の端末が必要になるタイミング

1台・2タブのテストで検出しにくい問題もあります。判断基準を整理します。

  • 異なるネットワーク経路(NAT越え)の検証:WebRTCのICE接続やSTUN/TURNサーバーの動作は、同一LAN内の2タブでは正しくテストできません。実際のNAT環境は別ネットワークの端末が必要です。
  • モバイル端末の操作感・タッチイベント:PCとスマートフォンで実際に触って確かめる必要があります。
  • 異なるブラウザエンジン間の互換性:ChromeとSafariの挙動の差はエミュレーションできません。

逆に言えば、これら以外の大半のロジックは1台・2タブで確認できます。友人に頼むのはこれらの項目を確認する段階で十分です。

次の行動

まずローカルサーバーを起動して2タブを並べ、ルームに参加するところまでを動かしてみてください。接続が成立したら、DevToolsのコンソールから意図的に不正操作を送り、サーバーが拒否するかを確かめるのが次の一手です。ゲームが完成したら、アソバースにZIPまたはHTMLとして投稿して実際のプレイヤーに試してもらう段階へ進めます。

参考資料

  • MDN Web Docs — WebSocket API
  • MDN Web Docs — WebRTC API
  • Socket.IO 公式ドキュメント(Rooms・Namespaces の項)
  • Chrome DevTools — Network conditions パネル(公式ドキュメント)