ブラウザで3Dゲームを1本作り切るまで — 構成の選び方と、つまずいた3つの不具合
「A旋状のバリア」は、HTMLファイル1枚で動くブラウザゲームです。ゲームエンジンは使わず、3D描画にthree.js、音はすべてブラウザの音声機能で合成しています。この記事では、個人で1本のブラウザゲームを作り切るまでに何を選び、どこでつまずいたかを書きます。
なぜブラウザゲームにしたか
選択肢としては、UnityでPC向けに出す、スマホアプリにする、ブラウザで出す、の3つがありました。ブラウザを選んだ理由は単純で、URLを送るだけで遊んでもらえるからです。インストールも審査もなく、思いついた改善をその日のうちに反映できます。個人開発で一番失われやすいのは勢いなので、反映までの距離が短いことを最優先にしました。
構成
| 役割 | 使ったもの |
|---|---|
| 3D描画 | three.js(WebGL) |
| 音 | Web Audio API(すべてコードで合成) |
| 保存 | localStorage(ベストスコアと設定) |
| 配信 | Cloudflare Workers の静的アセット |
| ランキング | Cloudflare D1(SQLiteベースのデータベース) |
| オンライン対戦 | WebRTC(PeerJS) |
ビルド工程はありません。HTMLファイルを書き換えてアップロードすれば、それが本番です。開発中はローカルでファイルを開くだけで動きます。
円形の盤面をどう作るか
ブロック崩しを円形にする場合、ブロックは「扇形の板」になります。three.jsには扇形のジオメトリがないので、自分で頂点を並べて作ります。半径の内側と外側、開始角度と終了角度を受け取り、厚みを持たせた立体にする関数を1つ用意すれば、あとは角度を変えて並べるだけです。
当たり判定は3Dの物理計算ではなく、極座標で判定しています。ボールの位置から中心までの距離と角度を求め、その距離がブロックの輪の内側と外側の間にあり、角度がブロックの占める範囲に入っていれば衝突、という単純な判定です。
const r = Math.hypot(ball.x, ball.y);
const th = Math.atan2(ball.y, ball.x);
// 輪の内外に入っているか → 角度が一致するか
if (r > ring.r1 && r < ring.r2) {
for (const brick of ring.bricks) {
if (Math.abs(wrap(th - ring.rot - brick.tc)) <= brick.hw) hit(brick);
}
}
この方式の利点は、ブロックの輪が回転していても計算が変わらないことです。回転角を引くだけで判定できます。
一番つまずいたところ
ボールが止まる
公開後に報告をもらった不具合です。キャッチでボールをバリアに貼り付けた状態でマルチボールを取ると、増えたボールが「止まっているボールのコピー」として生まれ、速度ゼロのまま盤面に居座りました。動かないので落ちることもなく、残機も減らず、進行が止まります。
直し方は2つです。コピー元が止まっている場合は中心方向へ新しい速度を与えること、そして保険として、速度がほぼゼロのボールや外周を延々と周回しているボールを検出して中心側へ向け直すことです。「ありえない状態」を検出して勝手に直す仕組みを入れておくと、想定外の経路で同じ症状が出ても進行不能になりません。
iOSの長押しで拡大鏡が出る
スマホで操作していると、長押ししたときに文字選択の拡大鏡が出てしまいました。ページ全体に選択無効の指定をしていたのですが、iOSではボタンやキャンバスがその指定を継承しません。すべての要素に対して個別に指定し、あわせて触れた瞬間の既定動作を止める必要がありました。ただし止めすぎるとボタンが反応しなくなるので、対象を絞る判断が要ります。
メニューの文字がホイールに重なる
画面の高さから逆算して文字サイズを決めていたのですが、項目が増えると計算より実際の高さが上回り、操作用のホイールに重なりました。最終的に、表示した直後に実際の高さを測って、収まらなければ収まるまで縮める方式に変えました。計算で予測するより、描画後に実測するほうが確実です。
音をすべてコードで作る
音源ファイルは1つも使っていません。効果音もBGMも、Web Audio APIで波形を組み立てています。ファイルサイズが増えず、著作権の心配もなく、曲の途中で転調させるといった操作も自由にできます。
BGMは10曲入っていますが、データとしては音符の配列と、それを音に変える関数の組み合わせです。たとえばパイプオルガンの音は、基音に加えてオクターブ上と12度上の倍音を重ねることで作っています。実際の楽器の構造をまねると、それらしい音になります。
作り終えてから気づいたこと
公開して人に触ってもらうと、自分では気づかない問題が次々に出ます。操作方法が伝わらない、最初の画面が静かすぎて何のゲームか分からない、序盤が難しくてやめてしまう。どれもコードの問題ではなく、最初の30秒の問題でした。
結局、タイトル画面で自動的にゲームが動くデモを流し、操作方法を図で示し、序盤を易しくするという改修を後から入れました。最初からそうしておけばよかった、というのが一番の学びです。
これから作る人へ
- 反映までの距離が短い構成を選ぶ。勢いが消えないうちに改善を回せます。
- おかしな状態を自動で直す仕組みを入れる。進行不能は、難しさとは別の理由で人が離れます。
- レイアウトは計算より実測。端末の種類は思ったより多様です。
- 完成してから、知らない人に30秒だけ触ってもらう。そこで出た反応が、一番価値のある情報です。