# Three.jsでスマホ対応3Dゲームを作る｜フロントエンドだけで動くWebアプリ実装ガイド｜テクノスフィア

> サーバー不要・インストール不要、ブラウザだけで動く3Dドローンゲームを実際に公開しながら実装を解説。Three.jsのシーン構築、スマホのバーチャルスティック実装、AABB衝突判定、AI画像生成(gpt-5.5)によるテクスチャ制作、モバイル60fps最適化まで。実際に遊べるデモ付き。

URL: https://technosphere.co.jp/blog/threejs-mobile-3d-game

** 目次
1. まず遊んでみてください（デモ公開中）
2. なぜ「フロントエンドだけ」で3Dゲームが動くのか
3. 技術スタック：Three.js + ES Modules、依存はそれだけ
4. 「リアルで迫力のある」絵作り：夕暮れ・フォグ・トーンマッピング
5. スマホ対応の肝：バーチャルスティックをPointer Eventsで自作する
6. ゲームループと軽量物理（慣性・AABB衝突）
7. ゲーム素材はAI画像生成で作る（プロンプト付き）
8. スマホで60fpsを出す5つの最適化
9. よくある質問（FAQ）
10. まとめ：この技術で作れる「業務アプリ」

## まず遊んでみてください（デモ公開中）

 論より証拠。この記事で解説する3Dゲーム「**ドローンインスペクター3D**」は、実際にこのサイトで公開しています。スマホでもPCでも、リンクを開けば**その場で**始まります。

 [▶ ドローンインスペクター3D をプレイ](https://technosphere.co.jp/games/drone-inspector/)
 インストール不要・無料・通信量はページ読込の一度きり

 夕暮れの工業地帯をドローンで飛行し、制限時間120秒でシアンに光る検査リング10箇所を通過するゲームです。App Store も Google Play も経由していません。**PHPすら実行していない、ただの静的ファイル**がこの3D空間を動かしています——これが本記事のテーマです。

## なぜ「フロントエンドだけ」で3Dゲームが動くのか

 現代のブラウザには **WebGL**（GPUを直接叩く3D描画API）が標準搭載されています。スマホの Safari / Chrome も例外ではありません。つまり描画・物理・入力・スコア管理のすべてをJavaScriptで書けば、**サーバーは「ファイルを配るだけ」**でよくなります。
- **ホスティングコスト**：静的配信のみ（当サイトはS3+CloudFront）。アクセスが増えてもサーバー負荷という概念自体がない
- **配布**：URLひとつ。ストア審査もアップデート配信もなし
- **オフライン**：一度読み込めば通信ゼロで動作（PWA化すればホーム画面アイコンからの起動も可能）

 「アプリを作りたいがストア公開はハードルが高い」という場合、まずこの**Webアプリ（ブラウザアプリ）**形態で出すのは実務でも有力な選択肢です。

## 技術スタック：Three.js + ES Modules、依存はそれだけ

 使ったライブラリは **Three.js（r160）** ただひとつ。ビルドツールも npm も使わず、ブラウザ標準の **importmap** で読み込みます。ゲーム本体は素のJavaScript約400行です。

```
<script type="importmap">
{ "imports": { "three": "/assets/js/vendor/three.module.min.js" } }
</script>
<script type="module" src="game.js"></script>
```

 この構成の利点は**ビルド環境の寿命に縛られない**ことです。node_modules が壊れて動かない——という数年後のリスクがなく、ファイルを置けば10年後も動きます。企業サイトに置く技術デモとしては重要な性質です。

## 「リアルで迫力のある」絵作り：夕暮れ・フォグ・トーンマッピング

 ローポリでもリアルに見せる鍵は、ジオメトリの細かさではなく**光と空気**です。今回のデモでは次の4点を組み合わせています。
1. **ACESトーンマッピング**：映画で使われる色変換。白飛びが減り、夕陽のオレンジが「写真の色」になる
2. **フォグ（霧）**：遠景を夕焼け色に溶かす。距離感と巨大感が生まれ、同時に遠景の描画も省ける
3. **低い太陽光＋長い影**：DirectionalLight を低角に置き、PCFソフトシャドウで長い影を落とす
4. **equirectangular スカイボックス**：360度パノラマ1枚を背景と環境光（IBL）の両方に使う

```
renderer.toneMapping = THREE.ACESFilmicToneMapping;
scene.fog = new THREE.Fog(0xc98a5a, 90, 520);   // 夕焼け色の霧

const sun = new THREE.DirectionalLight(0xffb877, 2.1);
sun.position.set(-340, 150, 90);                 // 低い夕陽
sun.castShadow = true;

texLoader.load('tex/sky.jpg', t => {
  t.mapping = THREE.EquirectangularReflectionMapping;
  scene.background = t;    // 背景
  scene.environment = t;   // 映り込み・環境光にも同じ画像を使う
});
```

 さらに、速度に応じてカメラのFOV（視野角）を70°→82°まで動的に広げています。加速時に視界が「引き伸ばされる」ことで、数値以上のスピード感＝迫力が出ます。

## スマホ対応の肝：バーチャルスティックをPointer Eventsで自作する

 スマホゲームの操作性はここで決まります。ライブラリに頼らなくても、**Pointer Events** を使えば約20行で「ゲームパッドの左右スティック」を実装できます。

```
function stick(el, cb) {           // el: スティックのDOM, cb(x,y): -1〜1
  const knob = el.firstElementChild;
  let id = null, cx = 0, cy = 0;
  const R = 44;                    // 可動半径(px)
  el.addEventListener('pointerdown', e => {
    id = e.pointerId; cx = e.clientX; cy = e.clientY;
    el.setPointerCapture(id);      // 指が枠を出ても追跡し続ける
  });
  el.addEventListener('pointermove', e => {
    if (e.pointerId !== id) return;
    let dx = e.clientX - cx, dy = e.clientY - cy;
    const d = Math.hypot(dx, dy);
    if (d > R) { dx *= R / d; dy *= R / d; }     // 半径でクランプ
    knob.style.transform = `translate(${dx}px,${dy}px)`;
    cb(dx / R, dy / R);
  });
  const end = e => {
    if (e.pointerId !== id) return;
    id = null;
    knob.style.transform = '';
    cb(0, 0);                      // ニュートラルに戻す
  };
  el.addEventListener('pointerup', end);
  el.addEventListener('pointercancel', end);
}
```

実装のポイントは3つあります。
- **setPointerCapture**：指がスティックの枠からはみ出しても追跡が切れない。これがないと激しい操作で必ず破綻します
- **pointerId の照合**：左右のスティックを別の指で同時に操作できる（マルチタッチ）
- **CSSに touch-action:none**：ブラウザのスクロール・ピンチ操作を無効化し、ゲーム操作と衝突させない

 PC では同じ入力値を WASD キーから合成しているため、ゲームロジック側はデバイスの違いを一切意識しません。

## ゲームループと軽量物理（慣性・AABB衝突）

 物理エンジンは使っていません。ドローンの「ふわっとした」挙動は、**加速度＋空気抵抗**の2行で表現できます。

```
// 毎フレーム (dt: 前フレームからの秒数)
vel.addScaledVector(inputDir, ACC * dt);      // 入力方向へ加速
vel.multiplyScalar(1 - Math.min(1, 2.2 * dt)); // 空気抵抗（自然に減速）
drone.position.addScaledVector(vel, dt);       // 位置を更新
```

 建物との衝突は、全建物を**AABB（軸に平行な直方体）**として持ち、めり込んだら浅い軸方向に押し戻して速度を反転させるだけの簡易実装です。64棟＋コンテナ60基でも判定は毎フレーム1ms未満。「正確な物理」より「軽くて壊れない物理」がモバイルでは正義です。

```
for (const c of colliders) {
  if (p.x > c.min.x && p.x < c.max.x &&
      p.z > c.min.z && p.z < c.max.z && p.y < c.max.y) {
    const dx = Math.min(p.x - c.min.x, c.max.x - p.x);
    const dz = Math.min(p.z - c.min.z, c.max.z - p.z);
    if (dx < dz) { p.x += sign(p.x - center.x) * dx; vel.x *= -0.35; }
    else         { p.z += sign(p.z - center.z) * dz; vel.z *= -0.35; }
  }
}
```

## ゲーム素材はAI画像生成で作る（プロンプト付き）

 スカイボックス・地面・建物・コンテナのテクスチャは、すべて**AI画像生成（gpt-5.5系の画像モデル）**で制作しました。デザイナー0人でここまで作れる時代です。3Dエンジンでそのまま使える画像を出すコツは、プロンプトに「用途の専門用語」を入れることです。
- **タイル化テクスチャ**：「*seamless tileable texture, top-down view, flat even lighting, edges must tile seamlessly in all directions*」——繋ぎ目なく敷き詰められる画像になる
- **スカイボックス**：「*seamless equirectangular 360 degree sky panorama, the left and right edges must connect seamlessly*」——球体に貼れる360度画像になる
- **PBR風の質感**：「*photorealistic PBR albedo style, no shadows of objects*」——ライティング情報を含まない素材向きの画像になる

 生成画像はJPEG圧縮して1枚100〜300KB程度に抑えます。ゲーム全体でもThree.js込み**約1.5MB**——Webページ1枚分の転送量で3Dゲームが配れる計算です。

## スマホで60fpsを出す5つの最適化
1. **devicePixelRatio を2でクランプ**：`renderer.setPixelRatio(Math.min(devicePixelRatio, 2))`。DPR 3のスマホで描画ピクセル数が半分以下になる。体感画質はほぼ変わらない
2. **フォグと描画距離の連動**：霧で見えなくなる距離より遠くは描かない。カメラの far を絞れば頂点処理が減る
3. **ジオメトリ・マテリアルの使い回し**：建物64棟でもジオメトリは1種類×スケール違い。GPUへの状態変更が激減する
4. **シャドウマップの出し分け**：タッチデバイス判定（`matchMedia('(pointer: coarse)')`）で 2048→1024 に解像度を落とす
5. **パーティクルは Points で**：塵700粒・煙240粒もドローコールは各1回。Mesh を700個並べたら即死する場面

 この5点だけで、ミドルレンジのAndroidでも安定して滑らかに動きます。逆に言えば、**これを外すとハイエンドiPhone以外はカクつく**のがモバイルWebGLの現実です。

## よくある質問（FAQ）

### Q. サーバーなしでどうやって3Dゲームが動くのですか？

 描画・物理・入力・スコア管理をすべてブラウザ内のJavaScript（Three.js/WebGL）で処理しているためです。ホスティングは静的ファイル配信だけでよく、プレイ中のサーバー通信はゼロです。仕組みはこちらで解説しています。

### Q. スマホのブラウザでも本当に60fpsで動きますか？

 動きます。5つの最適化——DPRクランプ・フォグ連動カリング・ドローコール削減・シャドウ出し分け・Pointsパーティクル——が前提条件です。

### Q. ゲームのテクスチャや背景はどうやって用意しましたか？

 AI画像生成で制作しました。「seamless tileable」「equirectangular panorama」など用途を指定するプロンプトで、3Dエンジンにそのまま貼れる素材が生成できます。

### Q. この技術はゲーム以外にも使えますか？

 使えます。製品3Dビューア、設備のデジタルツイン可視化、建築ウォークスルー、教育シミュレーターなど。まとめで具体例を挙げています。

## まとめ：この技術で作れる「業務アプリ」

###  この記事のまとめ
- WebGL＋Three.jsなら、サーバーもストアも不要でURLひとつで配れる3Dアプリが作れる
- リアルさはポリゴン数ではなく「光と空気」——ACESトーンマッピング・フォグ・低い太陽・IBLで作る
- バーチャルスティックはPointer Events＋setPointerCaptureで約20行
- 物理は「加速度＋空気抵抗＋AABB」の軽量実装で十分。モバイルでは軽さが正義
- テクスチャ・スカイボックスはAI画像生成で内製可能。全体1.5MBに収まる

 同じ技術スタックは、ゲームに限らず**製品の3Dビューア・工場設備のデジタルツイン・建築ウォークスルー・操作トレーニングシミュレーター**にそのまま応用できます。「アプリを作りたいが、ストア公開や専用端末まではハードルが高い」——そんな案件こそ、ブラウザだけで動くWebアプリの出番です。

 テクノスフィアは、こうしたフロントエンド技術に加えて、スマホアプリ（iOS/Android）、画像認識AI、組込みシステムまで一気通貫で開発しています。プロトタイプのご相談からお気軽にどうぞ。

 [▶ もう一度デモを遊ぶ](https://technosphere.co.jp/games/drone-inspector/) ｜ [スマホアプリ開発サービスを見る](https://technosphere.co.jp/app-development) ｜ [開発の相談をする](https://technosphere.co.jp/contact)

 業務で使うスマホアプリの方式選定は[ネイティブ・クロスプラットフォーム・PWAの選び方](https://technosphere.co.jp/blog/native-vs-pwa-app-choice)で解説しています。開発のご相談は[スマホアプリ開発サービス](https://technosphere.co.jp/app-development)へ。   [技術コラム一覧に戻る](https://technosphere.co.jp/blog/)
