Overview
概要
WebRTCのDataChannelを使い、ブラウザ同士が直接通信して1対1で対戦するサーバーレスゲームです。ゲームのデータはサーバーを経由せずピア間を直接流れ、シグナリング(接続確立の合図)だけをNext.jsのAPI Routeとポーリングで自前実装しています。6桁のルームコードを共有すれば対戦開始。リアルタイム物理のエアホッケーと、1〜25を昇順で早取りする反射神経ゲーム『早押し25』の2種を収録します。
How to use
遊び方・使い方
片方が部屋を作ると6桁のコードが発行され、相手がそれを入力すると接続します(URLクエリでの共有も可能)。エアホッケーはマレットを動かしてパックを打ち合い7点先取、早押し25は5×5に散らばった1〜25を相手より早く昇順でタップして得点を競います。接続はGoogleのSTUNを利用しますが、対称NAT同士など環境によっては繋がらない場合があります。成績はブラウザに保存されます。
Tech stack
技術スタック
TypeScriptNext.js 16React 19WebRTCTailwind CSS v4
追加npm依存はなく、サーバー側もRoute Handlerのみ。接続状態はuseReducerのステートマシン(idle→signaling→connecting→connected→ingame→result)で管理し、IPあたりのレート制限を設けています。エアホッケーはCanvas 2D、早押し25はDOMグリッドで描画します。
Highlights
工夫した点
- シグナリングはpm2単一プロセスのメモリ上Map(TTL10分)でルームを保持し、offer/answerをポーリングで交換。DataChannelはUDP的な『game』(順序保証なし)と順序保証ありの『control』の2本を用途で使い分け
- 同期はホスト権威方式を採用。ホストが60Hzで物理を回し30HzでSTATEを全送信、ゲストは自マレットを即時反映しつつパック等を100ms遅延補間で描画(欠落時は速度外挿)。ロックステップは不採用
- PING/PONGでRTTとクロックオフセットを指数移動平均で推定し、3回連続無応答で切断を検知。受信バッファはリングバッファ化しGCを抑制、メッセージは判別共用体で型安全にエンコード/デコード
To improve
改善したい点
- TURNサーバーを持たないため、対称NAT同士など一部ネットワークでは接続できない
- ホスト権威ゆえホスト側が有利になりうる部分の補正に伸びしろがある
- 再接続・観戦・チャットなど対戦周りの機能が最小限
What's next
今後の展望
- ゲーム種類の追加(対戦パズル等)
- 簡易マッチメイキングやランキング
- 遅延補正(クライアント予測・ロールバック)の高度化
