[JavaScript] Three.jsを使った3Dゲーム開発の基本: カメラ制御と衝突判定

はじめに

canvas の 2D でゲーム作ってましたが、一か月を超えて少し飽きてきたので、気分転換に3D のThree.jsを触ってみました。

FPSゲームのように「W」「A」「S」「D」キーでキャラクター移動、mouseでカメラの回転制御と、スクロールで拡大縮小という、3Dゲームにありがちな実装をやってみたので、記事に簡単にまとめました。

物理判定処理まで、作成しましたが、Three.jsを使うと驚くほど簡単にできます。

ただ、2Dに比べカメラの制御や視点など、やるべきことが多く、大変なのも確かですが、少し頑張ればちょっとしたゲームなどは作れそうな気がします。

1. Three.jsのセットアップ

Three.jsを使うための最初のステップは、シーン、カメラ、レンダラーを設定することです。これにより、3Dオブジェクトを画面に表示する準備が整います。

const scene = new THREE.Scene(); // シーンの作成
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); // カメラの作成
const renderer = new THREE.WebGLRenderer(); // レンダラーの作成
renderer.setSize(window.innerWidth, window.innerHeight); // レンダラーのサイズ設定
document.body.appendChild(renderer.domElement); // レンダラーをDOMに追加

解説:

  • シーン(scene): 3Dオブジェクトを配置する「舞台」。
  • カメラ(camera): 3Dシーンをどのように映すかを決定します。PerspectiveCameraは、視点からの距離を基に遠近感を表現するカメラです。
  • レンダラー(renderer): シーンを描画するために使用されます。WebGLをバックエンドとして使い、描画結果をHTMLの canvas に表示します。

2. カメラ操作: PointerLockControls

ゲームにおいて、プレイヤーが視点を操作するのは重要な要素です。Three.jsでは、マウスの動きでカメラを制御するためにPointerLockControlsを使います。このコントロールにより、マウスの動きでカメラを自由に回転させることができます。

const controls = new THREE.PointerLockControls(camera, renderer.domElement);

// マウスクリックでロック
document.addEventListener("click", () => {
  controls.lock(); // クリックでマウスをロック
});

解説:

  • PointerLockControls: マウスの移動をゲームの視点操作に使用するためのコントロールです。ロックされた状態でマウスの動きを無限に追跡し、視点を動かし続けることができます。
  • lock(): クリックでマウスロックを開始し、マウスの動きがカメラの回転に反映されるようにします。

3. キャラクターの移動と衝突判定

ゲームの基本的な要素として、キャラクターの移動や衝突判定が重要です。ここでは、Box3を使って衝突判定を行い、キャラクターが壁に当たった場合の処理を実装します。

// キャラクターの移動
function moveCharacter() {
  if (keyState["ArrowLeft"]) player.position.x -= 0.1;
  if (keyState["ArrowRight"]) player.position.x += 0.1;
}

// 衝突判定
function checkCollisions() {
  const characterBox = new THREE.Box3().setFromObject(player);
  const wallBox = new THREE.Box3().setFromObject(wall);

  if (characterBox.intersectsBox(wallBox)) {
    console.log("衝突しました!");
  }
}

解説:

  • Box3: 3D空間における軸平行なバウンディングボックス(軸に沿った立方体)を作成するためのクラスです。setFromObjectメソッドでオブジェクトの位置とサイズに基づいてボックスを定義します。
  • 衝突判定: 2つのBox3オブジェクトが重なったかをintersectsBoxメソッドで判定します。これを使ってキャラクターと壁が衝突しているかをチェックします。

4. イベントリスナーでの入力管理

キーボードやマウスでの操作をeventListenerを使って管理します。今回は、移動操作をキーボードの矢印キーやWASDキーで行い、マウス移動でカメラ回転を行います。

document.addEventListener("keydown", (event) => {
  keyState[event.code] = true;
});
document.addEventListener("keyup", (event) => {
  keyState[event.code] = false;
});

解説:

  • keydown: キーが押されたときに呼び出されるイベント。押されたキーの状態をkeyStateで管理します。
  • keyup: キーが離されたときに呼び出されるイベント。これを使ってキー入力の解除を管理します。

5. マウスのスクロールでズーム

ゲームでのズームは、プレイヤーがカメラを前後に動かすことで視野を広げたり、狭めたりする効果があります。マウスのスクロールを使って、ズームイン/アウトを実装します。

document.addEventListener("wheel", (event) => {
  if (event.deltaY > 0) {
    camera.position.z += 0.1; // 下にスクロール(遠ざかる)
  } else {
    camera.position.z -= 0.1; // 上にスクロール(近づく)
  }
});

解説:

  • wheelイベント: マウスのスクロールホイールを使ってカメラを前後に動かすことができます。deltaYを使用してスクロール方向を判別し、カメラの位置を変更します。

まとめ

  • カメラ制御: PointerLockControlsでマウスによる視点操作を実現。
  • キャラクター移動: キーボード入力でプレイヤーキャラクターを移動。
  • 衝突判定: Box3を使った3Dオブジェクト間の衝突判定を実装。
  • イベントリスナー: マウスとキーボード入力を使って、ゲーム内のインタラクションを管理。