目次
はじめに
「NEON AIR HOCKEY」は、ネオンの光で縁取られた近未来風の3Dアリーナで遊ぶ、ブラウザ向けのオンライン対戦エアホッケーです。Claude Codeを使って作成しました。
この記事では、機能や技術の紹介ではなく、AIコーディングエージェントにどう指示を出し、どうテストし、どう試行錯誤しながら完成に近づけたか、その過程をご紹介します。あわせて、さくらのレンタルサーバーのような共有サーバーでも、なぜこうしたリアルタイムのオンライン対戦を公開できるのか、その仕組みについてもご紹介します。
本文中、実際にAIへ伝えた内容は、次のように引用形式で示します。
AIへの指示の例
「(このように伝えた、という内容)」
それ以外の地の文では、その指示に対してAI側がどう対応したか、技術的にどういう仕組みになっているかを解説させていただきます。
NEON AIR HOCKEYとは

- ブラウザのみで動作し、ダウンロードは不要
- 対CPU戦と、2人で遊べるオンライン対戦の両方に対応
- 自分のマレット(打つ側の円盤)をマウス・タッチで操作し、パックを相手ゴールへ打ち込む
- 5種類のアイテム(速度アップ・パック分裂・パドル巨大化・シールド・マグネット)を取得しながら戦う
- BGMは、タイトル画面用と対戦中用の2曲とも、Pythonのプログラムで波形を合成して作っています。効果音も、音声処理ツールで合成したものです
- 標準では20点を先取した方が勝利。オプションで、何点でも取り続けられるエンドレスモードに切り替えることができます
オンラインでの対戦方法
友人などと2人で対戦する場合は、タイトル画面で「オンライン対戦」を選ぶと、部屋(ルーム)のIDを扱うロビー画面が表示されます。

- 1人が「部屋を作成」を押すと、部屋のIDが発行されます(上の画面では
room123) - このIDを、チャットや通話などゲームの外の方法で対戦相手に伝えます
- 対戦相手は、同じ部屋IDを入力欄に入力し、「部屋に参加」を押します
- 2人のブラウザがつながると、そのまま対戦が始まります
先に部屋を作成した側が「ホスト」、後から参加した側が「ゲスト」という役割になります。見ている景色は左右反転していますが、どちらの画面でも自分のゴールが手前に見えるようになっています。

さくらのレンタルサーバーでもバックエンドを持てる
このゲームでいちばんお伝えしたいのは、実はゲームの内容そのものよりも、さくらのレンタルサーバーのような共有サーバー(1台のサーバーを複数の利用者で共有するプラン)でも、こうしたリアルタイム通信を使ったアプリケーションを公開できる、という点です。
2人でリアルタイムに対戦するゲームを作ろうとすると、通常は「サーバー側で常に動き続けるプログラム」が必要になります。しかし多くの共有サーバープランでは、Node.js等のプログラムをずっと起動させたままにしておくことができません。PHPのように、リクエストが来たときだけ短時間動いて処理を終える仕組みが前提になっています。
この制約の中でオンライン対戦を成立させているのが、WebRTC(ブラウザ同士が仲介サーバーを介さず直接データをやり取りできる仕組み)です。対戦中のパックの位置やスコアといった実際のゲームデータは、2人のブラウザの間で直接やり取りされます。サーバー側が担うのは、「対戦相手を見つけて、2人のブラウザ同士を最初につなぐまでの案内役」(シグナリングと呼ばれる役割)だけです。
この案内役自体は、PHPと軽量なデータベース(SQLite。別途データベースサーバーを立てる必要のない、ファイル1つで完結する仕組み)だけで実現しています。案内が済んだ瞬間にサーバーの役目は終わり、その後の対戦中はサーバーに一切負荷がかかりません。「常に動き続けるプログラムを置けない」という共有サーバーの制約と、「最初の一瞬だけ案内できればよい」というPHPの得意分野が、ちょうどかみ合う設計です。
静的なホームページだけでなく、こうした小さな裏方の仕組み(バックエンド)を組み合わせることで、共有サーバーでもリアルタイム性のあるアプリケーションを公開できる、ということを示す一例です。
技術構成
最初の依頼は、次の4段階のフェーズに分けて進めました。
- Three.js(ブラウザで3Dグラフィックスを描画するライブラリ)による3D描画と物理演算(対CPU戦、ローカルだけで完結)
- PHP+SQLiteによるWebRTCのシグナリングAPI
- WebRTC DataChannelを使った、ホスト・ゲスト間のオンライン対戦
- アイテム・パワーアップ要素
加えて、遊び方パネルや複数のオプション設定、勝敗ルールなども後から追加しました。
オンライン対戦中は、パックの動き・得点の判定を、部屋を作った側(ホスト)のブラウザだけが計算し、その結果を1秒間に約20回、もう一方(ゲスト)へ送信する方式にしています。ゲスト側は自分では計算をせず、受け取った位置情報へなめらかに追従して表示するだけです。両者が同時に別々の計算をして結果がずれる、という同期の不整合が起こらない設計です。
もう一つ工夫されているのが、ゲスト側から見た画面の向きです。オンライン対戦では、自分が画面のどちら側にいてもゴールが手前に見えてほしいところですが(先ほどのホスト側・ゲスト側の画面を参照)、この実装では座標データ自体は一切変換せず、カメラの向きだけを180度反転させる方式を採っています。データは常にホスト基準の単一の座標系のままで済むため、「どちらの視点か」で座標変換にバグが入り込む余地がありません。
試行錯誤について
音楽と同期する背景演出をAIに設計してもらう
一通り遊べるようになった段階で、背景の演出について次のような依頼がありました。
AIへの指示の例
「音楽と同期して常に変化する背景システムにしたい」
この依頼には、世界観やビジュアルの方向性、レイヤー構成、欲しい提案の項目まで含めた詳しいブリーフが添えられていました。これを受けて、まずAIにアートディレクター・VFXデザイナーの視点で設計提案書を作成してもらい、そのうえで実装するという2段階の進め方を取りました。
提案の核となったコンセプトは、それまでの「都市の書き割り背景」を、**リーグが運営する巨大な演算基層(データそのものでできた空間)**という設定に置き換えるというものです。グリッド線はデータが流れる経路、遠くに見える構造物はサーバー筐体、漂う粒子は処理の中で漏れ出したデータの端数、というように、見た目の要素すべてに一貫した意味づけをしました。
提案をもとに実装を進める中では、実際に動かして初めて分かる不具合もいくつか見つかっています。
- 床のグリッド線が、地平線付近で1マスが極端に小さくなりチラついて見える(モアレ)現象が発生。輪郭のにじみ具合を距離に応じて調整する処理を加えて解消しました
- 画面の半分を覆うような巨大な白い円が突然現れる不具合が発生。カメラのすぐ近くに粒子が生成されると、遠近感の計算上その粒子の表示サイズが極端に大きくなってしまうことが原因でした。粒子が生成される範囲を制限し、表示サイズにも上限を設けることで解消しました
- ゴール時の演出が、まだ漂っている最中の粒子を無差別に巻き込んでしまう不具合が発生。演出の対象の選び方を見直して解消しました
一方で、提案に含まれていた項目のすべてが実装されたわけではありません。表示の分かりやすさに直結しないもの、既存の演出と役割が重なるもの、共有サーバー・モバイル環境で軽く動くことを優先すべきものは見送りました。提案されたものをすべて採り入れるのではなく、優先順位を付けて取捨選択する判断も行いました。
パックが動かなくなる不具合
対戦中、パックが壁とマレットの間に挟まって動かなくなり、試合が止まってしまう不具合が繰り返し報告されました。挟まりを検知して仕切り直す機能自体はすでにあったのですが、報告のたびに検知できていないパターンが見つかり、条件を何度か見直すことになりました。
途中では、次のような具体的な改善案も寄せられています。
AIへの指示の例
「壁とマレットとの反発が短時間で連発する時に仕切り直すロジックを入れられないか」
この過程で得られた大きな教訓は、「反発した回数」だけを見て仕切り直しを判定すると、パックが場内を広く動き回る通常の激しいラリーまで異常と誤判定してしまう、ということでした。反発の回数だけでなく、その反発が同じ場所で繰り返し起きているかという条件も加えることで、正常なプレーと本当の挟まりを見分けられるようになりました。あわせて、AIが操作するマレットが壁際でパックを押し込み続けてしまう動き方そのものにも原因の一端があることが分かり、そちらも修正しています。

対戦してみて見つかった不具合
2人で実際に対戦してみることでしか気づけない不具合も、いくつか見つかっています。
- 衝突の演出・音が、対戦相手(ゲスト)側だけ鳴らない: 衝突の判定そのものをホスト側でしか計算していなかったため、演出も音もゲスト側では一度も実行されていませんでした。ホストが検知した内容をゲスト側へ通知して再生する仕組みを、衝突にも追加しました
- パックの挟まりから仕切り直すと、スコアまでリセットされる: 仕切り直しの処理が、試合全体を初期化する処理と共用になっていたことが原因でした。パックとマレットの位置だけを戻す専用の処理に分けました
- タイトル画面でBGMが鳴らないことがある: 背景演出の音楽解析の仕組みが、ユーザーの操作より前にオーディオの仕組みを作ってしまっていたことが原因でした。ブラウザには、実際の操作より前に音声を自動再生させない制限があるため、この巻き込みで無音になっていました。オーディオの仕組みを作るタイミングを、実際に操作した瞬間だけに限定して直しました
アップロードスキルについて
このゲームは、さくらのレンタルサーバーの「アップロードスキル」を使って公開しています。AIエージェントに話しかけるだけで、ドメインの設定からファイルの転送までを進めることができます。
使い方は、次の記事で扱っています。
今後、AIでゲーム開発をする人に向けたポイント
- 常駐プロセスを持てない共有サーバーでも、負荷の少ない役割に絞ることでリアルタイム性のある機能を組み込むことができます。今回のPHP+SQLiteによるシグナリングのように、サーバー側の役割を「最初の案内だけ」に絞れば、共有サーバーの制約の中でも実現できます
- 不具合の再発防止は、「回数」だけでなく「場所」も見ることができます。同じ場所で繰り返し起きているのか、場内を広く動き回る中でたまたま重なっただけなのかを区別する条件を加えることで、誤検知を大きく減らすことができました
- 見た目に関する詳しいブリーフをAIに渡すと、実装だけでなく設計提案そのものを任せることができます。ただし提案された項目をすべて採用する必要はなく、性能や役割の重複を見ながら取捨選択することができます
- 2人で実際に対戦してみることでしか見つからない不具合があります。片方の環境だけでは発生しない、通信の両側の状態が絡む不具合は、実際に対戦してみて初めて見つかることがあります
公開デモ
「NEON AIR HOCKEY」のデモは、次のリンクから開くことができます。
ほかのゲームデモは、次の記事でまとめてご紹介しています。
まとめ
「NEON AIR HOCKEY」は、AIコーディングエージェントとの対話とテストを繰り返しながら仕上げたオンライン対戦ゲームです。パックが動かなくなる不具合のように、報告を受けては条件を見直す、という試行錯誤の積み重ねによって、安定して遊べる状態まで仕上げました。
同時にこのゲームは、さくらのレンタルサーバーのような共有サーバーでも、PHPとWebRTCを組み合わせることでリアルタイムのオンライン対戦を公開できることを示す一例でもあります。静的なホームページだけでなく、小さなバックエンドを持つアプリケーションも、共有サーバーの範囲内で実現することができます。




