非同期対戦の設計:相手がいなくても遊べる対戦
遊びをつくる執筆にClaudeを使用しています。

「対戦ゲームを作りたいが、同時接続できるプレイヤーが少ないと成立しない」という悩みは、個人開発者にとって現実的な壁です。リアルタイム対戦はサーバーインフラとマッチング待機の両方を解決しなければならず、完成直後のゲームほどプレイヤーが集まっていないため、待機中に離脱されやすくなります。 非同期対戦は、この問題を構造から切り離す設計手法です。プレイヤーAが自分のターンを終えてデータを保存し、プレイヤーBが都合のよいタイミングでそのデータを読んで応答する。双方が同じ時間にオンラインである必要がないため、同時接続数が少なくても対戦が成立します。 この記事では、非同期対戦に向くゲームジャンルの見極め方から、ターン状態の管理・保存方法、UXの設計、よくある実装ミスまでを具体的に説明します。
この記事の目次
非同期対戦に向くゲームと向かないゲーム
すべての対戦ゲームが非同期に適しているわけではないため、設計を始める前にジャンルの適性を確認しておくことが重要です。
非同期に向くゲームの条件は、「1ターンの操作が完結していること」「リアルタイムの反応速度が勝敗を左右しないこと」の二点です。チェス・オセロ・将棋・麻雀・カードゲーム・ターン制ストラテジーはこの条件を満たします。相手の行動が確定するまで自分の行動が発生しない構造なので、時間軸をずらしても論理的に破綻しません。
一方、アクション・シューティング・格闘ゲームは向きません。これらは「相手の入力に対して0.1秒以内に反応する」という体験が本質であり、非同期化すると別のゲームになってしまいます。リアルタイム要素を非同期でエミュレートしようとする試みは大抵、どちらの良さも消えた中途半端な設計になります。
パズルゲームの一部は「スコアを競う非同期対戦」として成立します。同じ問題を別々のタイミングでプレイし、スコアや手数で比べる形式です。これは厳密にはターン交換ではなく「記録の比較」ですが、プレイヤーには対戦感が生まれます。
ターン状態をどう保存するか
非同期対戦の技術的な核心は、「ゲームの現在状態をいつでも完全に復元できる形でサーバーに保存すること」です。
ゲーム状態(State) とは、そのゲームを再現するために必要な最小限のデータです。チェスであれば、現在の全駒の位置・手番・キャスリング権・アンパサン可否・手数が揃えば完全に局面を再現できます。画面描画用の変数やアニメーションフラグは状態に含める必要はありません。
保存の方針には二種類あります。
スナップショット方式は、毎ターン終了時点のゲーム全状態をJSONなどで保存します。実装がシンプルで、任意の時点に戻りやすい反面、データサイズがターン数に比例します。手数の少ないゲームに向いています。
差分(ログ)方式は、初期状態と操作の履歴だけを保存し、ゲーム側が再生して現在状態を復元します。データは小さくなりますが、履歴から状態を再計算する処理が必要で、途中の検証にも使えるためチート検知と相性が良いです。
個人開発の初期段階ではスナップショット方式が扱いやすく、問題が出てから差分方式に移行する判断で十分です。重要なのは、どちらの方式でも「保存されたデータだけで次のターンの合法手を検証できる」ことを設計段階で確認しておくことです。サーバー側で検証できない構造だと、クライアント改ざんを防げません。
ユーザー体験の設計:「待つ」をどう見せるか
非同期対戦の最大のUX課題は、「相手のターンを待っている状態」を放棄と区別させないことです。
通知設計が最初の優先事項です。相手がターンを終えたことをプレイヤーに知らせる手段がなければ、ゲームを開いて確認するという習慣が生まれません。Webゲームではブラウザのプッシュ通知、またはメールやSNS連携による外部通知が有効です。通知が実装できない場合でも、「最終更新:3時間前」のような更新時刻の表示があるだけで、プレイヤーが再訪するかどうかの判断材料になります。
ターン制限時間を設けることも選択肢です。「24時間以内に応答しないとデフォルト負け」のようなルールは対戦の進行を保証します。ただし、カジュアルなゲームで厳しすぎる制限は離脱を招くため、「48時間でリマインド、72時間でタイムアウト」程度から始めて調整するのが現実的です。
対戦一覧画面の設計も重要です。自分がアクションを取れる対戦(自分のターン)と、待ちの対戦を視覚的に分けて表示することで、複数対戦を同時進行しているプレイヤーが混乱しません。「あなたのターン」バッジや色分けは実装コストが低い割に効果が大きい改善です。
よくある設計の失敗と対処
失敗1:手番チェックをクライアントだけで行う
「相手のターンなのに操作できてしまう」バグは、クライアント側だけで手番を判定しているときに起きます。サーバーへのターン提出時に、そのプレイヤーが現在の手番プレイヤーであることをサーバー側でも検証することで防げます。
失敗2:同時更新(レースコンディション)を考慮しない
ほぼ同時に双方が操作した場合や、通信遅延でリクエストが重複した場合に、状態が壊れることがあります。FirebaseやSupabaseなどのリアルタイムデータベースにはトランザクション機能があるため、「現在の状態がXであれば更新する」という条件付き書き込みを使うことで防止できます。独自のAPIサーバーを立てる場合も、楽観的ロック(バージョン番号の照合)で対応するのが一般的です。
失敗3:ゲーム終了後の状態を捨てない
対戦結果と棋譜(手順の記録)は、ゲームが終わった後も価値があります。プレイヤーが過去の対戦を振り返ったり、開発者がバランス調整のデータとして使ったりできます。終了後に状態を削除する設計にすると後から取り返しがつかないため、アーカイブとして別の領域に移す方針を最初から決めておくと安全です。
失敗4:相手がいない状態でテストできない構造
非同期対戦は、開発中に自分一人でテストするのが難しいゲームです。同じブラウザの別タブや別アカウントでの操作、あるいはテスト用の「CPUが即座に応答する」モードを開発初期に組み込んでおくと、デバッグのサイクルが大幅に速くなります。
非同期対戦の「拡張」としてのゴースト対戦
非同期対戦の発展形として、ゴースト対戦(Ghost Match) という設計パターンがあります。過去にプレイヤーが残した操作記録を「対戦相手」として再生する仕組みで、厳密には非同期ではなく記録の再現ですが、プレイヤーには対戦の感覚が生まれます。
レースゲームの「ゴースト車」が典型例ですが、ターン制ゲームにも応用できます。過去の自分の棋譜を相手として再挑戦する練習モードや、ランキング上位者の記録に挑む「挑戦モード」は、同時接続がゼロでも常に対戦コンテンツを提供できます。
実装の観点では、ゴースト対戦は非同期対戦より単純です。相手側の入力を配列として保存し、ターンごとに順番に適用するだけで成立します。非同期対戦の完全実装が難しい場合の代替手段としても機能するため、初期バージョンでゴースト対戦を実装し、後から本物の非同期対戦に移行する段階的な開発戦略も有効です。
次の行動
まず、作ろうとしているゲームが「1ターンが完結しているか」を確認し、非同期に適したジャンルかどうかを判断してください。適していれば、ゲーム状態をJSONで書き出して外部から完全に復元できる構造を設計することが最初の一歩です。ターン制ゲームをアソバースに投稿する場合は、ZIPファイルにまとめてアップロードできます。非同期のターン管理にはFirebaseやSupabaseなど既存のBaaSを組み合わせる方法が実装コストを抑えやすいため、公式ドキュメントで料金と制限を確認してから設計を進めてください。
参考資料
- Firebase Realtime Database 公式ドキュメント(Google)
- Supabase 公式ドキュメント
- MDN Web Docs — Web Push API



