mcpField
T
threejs-game

threejs-game

github.com/natea開発4415 回インストール53 週間前に更新
リポジトリ

threejs-game について

ブラウザー向け3DゲームをThree.jsで開発するためのスキルです。レンダリングやゲームシステム、アセット読み込み、最適化を扱い、実装パターンとトピック別資料を提供します。

主な機能

  1. 013Dシーンとゲームループの構築
  2. 02プレイヤー操作と入力処理
  3. 03衝突・物理・体力システム
  4. 04モデル読み込みとアニメーション
  5. 05レンダリングと性能最適化

活用シーン

  1. 01ブラウザー向け3Dゲームの開発
  2. 02移動や衝突処理の実装
  3. 03ゲームキャラクターの読み込みとアニメーション
  4. 04レンダリング性能の改善

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/natea/fitfinder --skill threejs-game

インストール後、Claude Code で/threejs-gameを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
animation.md
cameras.md
effects.md
game_development.md
geometry.md
getting_started.md
index.md
interactivity.md
lighting.md
loading.md
materials.md
scene_graph.md
textures.md

詳細

Three.js ゲーム開発スキル

WebGL を使用した Three.js のゲーム開発を包括的に支援します。3D レンダリング、ゲームの仕組み、物理演算、アニメーション、インタラクティブなブラウザベースのゲームを扱います。

このスキルを使用するタイミング

次のような場合に、このスキルを有効にしてください。

  • Three.js で 3D ウェブゲームを開発する
  • ゲームの仕組み(プレイヤーの移動、衝突、スコアリング)を実装する
  • カメラ、ライティング、シーン管理を設定する
  • 3D モデル(GLTF、OBJ、FBX)を読み込む
  • ユーザー入力(キーボード、マウス、タッチ、ゲームパッド)を処理する
  • アニメーションやキャラクターコントローラーを作成する
  • 物理エンジン(Cannon.js、Ammo.js)を統合する
  • 3D ゲームのパフォーマンスを最適化する
  • ゲームのビジュアル用にシェーダーやマテリアルを扱う

クイックリファレンス

基本的なゲームのセットアップ
import * as THREE from 'three';

// Create scene, camera, renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// Game loop
function animate(time) {
  requestAnimationFrame(animate);

  // Update game logic here
  updatePlayer(time);
  updateEnemies(time);
  checkCollisions();

  renderer.render(scene, camera);
}

animate();
プレイヤーコントローラー(サードパーソン)
class PlayerController {
  constructor(camera, target) {
    this.camera = camera;
    this.target = target;
    this.distance = 10;
    this.height = 5;
    this.rotationSpeed = 0.005;
    this.moveSpeed = 0.1;
  }

  update(input) {
    // Movement
    const forward = new THREE.Vector3(0, 0, -1).applyQuaternion(this.target.quaternion);
    const right = new THREE.Vector3(1, 0, 0).applyQuaternion(this.target.quaternion);

    if (input.forward) this.target.position.add(forward.multiplyScalar(this.moveSpeed));
    if (input.backward) this.target.position.add(forward.multiplyScalar(-this.moveSpeed));
    if (input.left) this.target.position.add(right.multiplyScalar(-this.moveSpeed));
    if (input.right) this.target.position.add(right.multiplyScalar(this.moveSpeed));

    // Rotation
    if (input.rotateLeft) this.target.rotation.y += this.rotationSpeed;
    if (input.rotateRight) this.target.rotation.y -= this.rotationSpeed;

    // Update camera position
    const offset = new THREE.Vector3(0, this.height, this.distance);
    offset.applyQuaternion(this.target.quaternion);
    this.camera.position.copy(this.target.position).add(offset);
    this.camera.lookAt(this.target.position);
  }
}
入力処理
class InputManager {
  constructor() {
    this.keys = {};
    this.mouse = { x: 0, y: 0, buttons: {} };

    window.addEventListener('keydown', (e) => this.keys[e.code] = true);
    window.addEventListener('keyup', (e) => this.keys[e.code] = false);
    window.addEventListener('mousemove', (e) => {
      this.mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
      this.mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
    });
  }

  getInput() {
    return {
      forward: this.keys['KeyW'] || this.keys['ArrowUp'],
      backward: this.keys['KeyS'] || this.keys['ArrowDown'],
      left: this.keys['KeyA'] || this.keys['ArrowLeft'],
      right: this.keys['KeyD'] || this.keys['ArrowRight'],
      jump: this.keys['Space'],
      action: this.keys['KeyE'],
      rotateLeft: this.keys['KeyQ'],
      rotateRight: this.keys['KeyE']
    };
  }
}
衝突検出(レイキャスティング)
function checkCollisions(player, obstacles) {
  const raycaster = new THREE.Raycaster();
  const directions = [
    new THREE.Vector3(1, 0, 0),   // right
    new THREE.Vector3(-1, 0, 0),  // left
    new THREE.Vector3(0, 0, 1),   // forward
    new THREE.Vector3(0, 0, -1),  // backward
  ];

  for (const direction of directions) {
    raycaster.set(player.position, direction);
    const intersects = raycaster.intersectObjects(obstacles);

    if (intersects.length > 0 && intersects[0].distance < 1.0) {
      return {
        collision: true,
        object: intersects[0].object,
        distance: intersects[0].distance,
        point: intersects[0].point
      };
    }
  }

  return { collision: false };
}
3D モデルの読み込み(GLTF)
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

const loader = new GLTFLoader();

function loadCharacter(path) {
  return new Promise((resolve, reject) => {
    loader.load(
      path,
      (gltf) => {
        const model = gltf.scene;
        model.scale.set(1, 1, 1);
        scene.add(model);

        // Setup animations if available
        const mixer = new THREE.AnimationMixer(model);
        const animations = {};
        gltf.animations.forEach(clip => {
          animations[clip.name] = mixer.clipAction(clip);
        });

        resolve({ model, mixer, animations });
      },
      (progress) => {
        console.log(`Loading: ${(progress.loaded / progress.total * 100).toFixed(2)}%`);
      },
      (error) => reject(error)
    );
  });
}

// Usage
const character = await loadCharacter('/models/character.glb');
character.animations.idle.play();
基本的な物理演算(重力とジャンプ)
class PhysicsBody {
  constructor(mesh) {
    this.mesh = mesh;
    this.velocity = new THREE.Vector3();
    this.onGround = false;
    this.gravity = -9.8;
    this.jumpPower = 5;
  }

  update(deltaTime) {
    // Apply gravity
    if (!this.onGround) {
      this.velocity.y += this.gravity * deltaTime;
    }

    // Apply velocity
    this.mesh.position.add(this.velocity.clone().multiplyScalar(deltaTime));

    // Ground check
    if (this.mesh.position.y <= 0) {
      this.mesh.position.y = 0;
      this.velocity.y = 0;
      this.onGround = true;
    }
  }

  jump() {
    if (this.onGround) {
      this.velocity.y = this.jumpPower;
      this.onGround = false;
    }
  }
}
インタラクティブなオブジェクト(ピッキング)
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

function onMouseClick(event) {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

  raycaster.setFromCamera(mouse, camera);
  const intersects = raycaster.intersectObjects(interactableObjects);

  if (intersects.length > 0) {
    const object = intersects[0].object;
    object.userData.onInteract?.();
  }
}

window.addEventListener('click', onMouseClick);
体力とダメージのシステム
class Entity {
  constructor(mesh, maxHealth) {
    this.mesh = mesh;
    this.maxHealth = maxHealth;
    this.health = maxHealth;
    this.isDead = false;
  }

  takeDamage(amount) {
    if (this.isDead) return;

    this.health = Math.max(0, this.health - amount);

    if (this.health === 0) {
      this.die();
    }

    return this.health;
  }

  heal(amount) {
    this.health = Math.min(this.maxHealth, this.health + amount);
    return this.health;
  }

  die() {
    this.isDead = true;
    this.mesh.visible = false;
    // Trigger death animation, effects, etc.
  }
}

主要な概念

シーングラフ
  • ゲームオブジェクトを階層的に整理します
  • 複雑なオブジェクトにはグループを使用します
  • 親子間の変換を扱います
ゲームループ
  • 60fps で動作させるには requestAnimationFrame を使用します
  • フレームレートに依存しない移動を実現するため、デルタタイムを計算します
  • 更新ロジックとレンダリングを分離します
カメラシステム
  • PerspectiveCamera:一人称/三人称ゲーム向け
  • OrthographicCamera:2D/アイソメトリックゲーム向け
  • カメラの追従とスムーズな遷移を実装します
ライティング
  • AmbientLight:基本の照明
  • DirectionalLight:影を伴う太陽光/月光
  • PointLight:松明、爆発
  • SpotLight:懐中電灯、ステージライト
パフォーマンスの最適化
  • 繰り返し使うオブジェクトにはインスタンシングを使用します
  • 視錐台カリングを実装します
  • 遠くのオブジェクトには LOD(詳細度)を使用します
  • ドローコールを最小限に抑えます
  • テクスチャアトラスを使用します
  • シャドウマップを最適化します
アセットの読み込み
  • ゲーム開始前にすべてのアセットを事前読み込みします
  • 読み込みの進行状況バーを表示します
  • 読み込みを調整するには LoadingManager を使用します
  • 読み込んだアセットをキャッシュします

よく使われるゲームのパターン

ステートマシン(ゲーム状態)
class GameStateMachine {
  constructor() {
    this.states = {
      menu: new MenuState(),
      playing: new PlayingState(),
      paused: new PausedState(),
      gameOver: new GameOverState()
    };
    this.currentState = this.states.menu;
  }

  changeState(stateName) {
    this.currentState.exit();
    this.currentState = this.states[stateName];
    this.currentState.enter();
  }

  update(deltaTime) {
    this.currentState.update(deltaTime);
  }
}
オブジェクトプーリング
class ObjectPool {
  constructor(factory, initialSize = 10) {
    this.factory = factory;
    this.available = [];
    this.inUse = [];

    for (let i = 0; i < initialSize; i++) {
      this.available.push(factory());
    }
  }

  acquire() {
    let obj = this.available.pop();
    if (!obj) obj = this.factory();
    this.inUse.push(obj);
    return obj;
  }

  release(obj) {
    const index = this.inUse.indexOf(obj);
    if (index > -1) {
      this.inUse.splice(index, 1);
      this.available.push(obj);
    }
  }
}

// Usage
const bulletPool = new ObjectPool(() => createBullet(), 20);
const bullet = bulletPool.acquire();
// ... use bullet
bulletPool.release(bullet);

リファレンスファイル

トピック別に整理された詳細なドキュメント:

  • getting_started.md - Three.js の基礎、セットアップ、基本概念
  • game_development.md - ゲームループ、プレイヤーコントローラー、ゲームの仕組み
  • scene_graph.md - シーンの構成、階層、変換
  • materials.md - マテリアルの種類、シェーダー、視覚効果
  • textures.md - テクスチャの読み込み、UV マッピング、アトラス
  • lighting.md - ライトの種類、影、HDR
  • cameras.md - カメラの種類、コントロール、ビューポート管理
  • geometry.md - 組み込みジオメトリ、カスタムジオメトリ、バッファー
  • loading.md - アセットの読み込み(モデル、テクスチャ、オーディオ)
  • animation.md - アニメーションシステム、スケルタルアニメーション、トゥイーン
  • interactivity.md - レイキャスティング、ピッキング、UI 統合
  • effects.md - ポストプロセッシング、パーティクル、フォグ

リソース

公式ドキュメント
物理演算の統合
  • Cannon.js:軽量な 3D 物理演算
  • Ammo.js:Bullet 物理エンジンの完全移植版
  • Rapier:高性能な物理演算
便利なライブラリ
  • three-mesh-bvh:高速なレイキャスティング
  • three-pathfinding:ナビゲーションメッシュ
  • postprocessing:高度なエフェクト

このスキルの活用

初心者向け
  1. 基本的なシーンのセットアップから始めます
  2. 座標系を学びます
  3. ゲームループを理解します
  4. モデルを使う前に、シンプルな形状で練習します
ゲーム開発向け
  1. ゲームのアーキテクチャを計画します
  2. まず入力処理を実装します
  3. シンプルなプレイヤーコントローラーを作成します
  4. ゲームプレイの仕組みを段階的に追加します
  5. 開発を通してパフォーマンスを最適化します
高度な機能向け
  1. 物理エンジンを統合します
  2. 高度なシェーダーを実装します
  3. ポストプロセッシング効果を追加します
  4. マルチプレイヤー向けのネットワーク機能を構築します

注意事項

  • Three.js は右手座標系を使用します(X は右、Y は上、Z は手前です)
  • 早い段階から最適化します。定期的にプロファイリングし、ドローコールを最小限に抑えてください
  • デバッグには開発用ビルドを、リリースには本番用ビルドを使用してください
  • 最新のブラウザーでは WebGL 2 の機能を検討してください
  • モバイルでのパフォーマンスには慎重な最適化が必要です

コメント