同一フィールドで戦うスネークバトルを実装してみた
Cloudflare Durable Objectsを使ったリアルタイムスネークバトルゲームの実装解説。サーバー側でのゲームロジック処理、衝突判定、WebSocketによる状態同期の仕組みを詳しく紹介します。
同一フィールドで戦うスネークバトルを実装してみた
「スネークゲームの対戦版を作ろう」
そう思い立ったとき、最初に考えたのは「どうやって2匹のスネークを同じフィールドで戦わせるか」でした。
この記事では、Cloudflare Durable Objectsを使って実装したリアルタイムスネークバトルの技術的な裏側を解説します。
完成したもの
まずは完成したゲームの概要です:
- 同一フィールド対戦: 2人のプレイヤーが30×30のグリッド上で対戦
- リアルタイム同期: 100msごとにゲーム状態を同期(10 FPS)
- 多様な衝突判定: 壁、自分の体、相手の体、正面衝突
- クイックマッチ: ランダムなプレイヤーとすぐに対戦可能
なぜサーバー側でゲームロジックを実行するのか?
クライアント側実行の問題点
スネークゲームをクライアント側で実行する場合、いくつかの問題があります:
特に「正面衝突」のような判定では、どちらが先に衝突したかで勝敗が変わってしまいます。
サーバー側実行のメリット
サーバーを**唯一の信頼できる情報源(Single Source of Truth)**にすることで、この問題を解決できます:
Cloudflare Durable Objectsの採用
なぜDurable Objectsなのか?
リアルタイムゲームサーバーには以下の要件があります:
| 要件 | Durable Objectsの対応 |
|---|---|
| WebSocket対応 | ネイティブサポート |
| ステートフル | オブジェクト内で状態を保持 |
| 低レイテンシ | エッジで実行される |
| スケーラビリティ | 自動的にスケール |
通常のサーバーレス関数(AWS Lambda等)はステートレスなので、ゲームの状態を外部に保存する必要があります。Durable Objectsはステートフルなので、ゲーム状態をメモリに保持できます。
ルーム構造
各ルームが独立したDurable Objectとして存在し、接続したプレイヤーの状態を管理します。
ゲームループの実装
100msごとのティック
ゲームは100ミリ秒ごとにサーバー側で状態を更新します:
「100msは遅くない?」と思うかもしれませんが、スネークゲームにはちょうど良いテンポです。古典的なスネークゲームも同様のスピードでした。
ティック処理の流れ
衝突判定の詳細
4種類の衝突
スネークバトルには4種類の衝突判定があります:
それぞれの判定を見ていきましょう。
1. 壁衝突
グリッドの範囲外に出たら壁に衝突したと判定します。
2. 自分の体への衝突
自分のスネークの体と新しい頭の位置が重なったらアウト。
3. 正面衝突(両者死亡)
これが一番面白い判定です。両者が同じマスに移動しようとした場合、両方とも死亡します。この場合はスコアで勝敗を決めます。
4. 相手の体への衝突
相手のスネークの体に突っ込んだ場合。相手は無傷です。
プレイヤーの初期配置
対戦ゲームとして公平にするため、プレイヤーは対角線上に配置されます:
クライアント側の実装
方向変更のみ送信
クライアントは方向変更のみをサーバーに送信します:
スネークの位置計算はすべてサーバー側で行い、クライアントは結果を描画するだけです。
キーボード操作
クライアント側でも180度反転をブロックしていますが、最終的な判定はサーバー側で行います。これは二重チェックで、チート対策にもなっています。
タッチ操作(モバイル対応)
スワイプ操作で直感的に方向を変えられるようにしています。
死亡通知のアニメーション
誰かが死んだときは、全プレイヤーに通知を表示します:
Framer Motionを使ったアニメーションで、通知がスムーズに表示・非表示されます:
勝敗判定のロジック
通常の勝利
両者死亡の場合
同時死亡でも必ず勝者を決定できる仕組みです。
マッチメイキングの仕組み
クイックマッチ
マッチング待ちの場合は、500msごとにサーバーをポーリングしてマッチを確認します。
まとめ
| 技術要素 | 採用した解決策 |
|---|---|
| ゲーム状態の同期 | サーバー側でロジック実行 |
| リアルタイム通信 | WebSocket(Durable Objects) |
| 衝突判定 | サーバー側で一元管理 |
| 公平性 | 対角線配置 + シード乱数 |
| モバイル対応 | スワイプ操作 |
サーバー側でゲームロジックを実行することで、チート耐性と公平性を両立できました。
Cloudflare Durable Objectsは、こういったステートフルなリアルタイムアプリケーションに最適です。従来のサーバーレスでは実現が難しかったゲームサーバーも、簡単に構築できるようになりました。
遊んでみる
実際にスネークバトルを遊んでみたい方は、こちらからどうぞ:
ソースコード
この記事で紹介したコードの完全版はGitHubで公開しています:
このシリーズの他の記事
このサイトでは、他にもオンライン対戦ゲームの実装記事を公開しています:
- 2048オンライン対戦で「公平」を実現する方法:シード乱数の魔法 - シード乱数で両プレイヤーに同じタイル配置を保証
- 日本語タイピングゲームの「shi」と「si」問題を解決する - 150以上のローマ字パターンに対応
- テトリスは運ゲーじゃない!7-bagアルゴリズムとマルチプレイ同期の仕組み - 7-bagとシード乱数の解説