Agent Skills by ALSEL
Anthropic Claudeソフトウェア開発⭐ リポ 0品質スコア 50/100

aframe-webxr

HTMLとエンティティコンポーネントアーキテクチャを使用して、ブラウザベースの3D・VR・ARエクスペリエンスを構築するための宣言的Webフレームワークです。WebXRアプリケーション、VR/ARエクスペリエンス、360度メディアビューアー、最小限のJavaScriptで没入型Webコンテンツを作成する際に活用してください。A-Frame、WebXR、VR/AR開発、エンティティコンポーネントシステム、宣言的3D、HTMLベースの3Dシーンに関するタスクで起動し、Three.jsを基盤としたHTMLファーストのアプローチで直感的に開発できます。

description の原文を見る

Declarative web framework for building browser-based 3D, VR, and AR experiences using HTML and entity-component architecture. Use this skill when creating WebXR applications, VR experiences, AR experiences, 360-degree media viewers, or immersive web content with minimal JavaScript. Triggers on tasks involving A-Frame, WebXR, VR development, AR development, entity-component-system, declarative 3D, or HTML-based 3D scenes. Built on Three.js with accessible HTML-first approach.

SKILL.md 本文

A-Frame WebXR スキル

このスキルを使用する場合

  • 最小限のJavaScriptでVR/AR体験を構築する
  • クロスプラットフォームのWebXRアプリケーション(デスクトップ、モバイル、ヘッドセット)を作成する
  • HTMLプリミティブを使用して3Dシーンを素早くプロトタイプする
  • VRコントローラーの相互作用を実装する
  • 3Dコンテンツを宣言的にWebページに追加する
  • 360°画像/ビデオ体験を構築する
  • ヒットテストを使用したAR体験を開発する

コア概念

1. エンティティ・コンポーネント・システム(ECS)

A-Frameはエンティティ・コンポーネント・システムアーキテクチャを使用します:

  • エンティティはコンテナ(HTMLの<div>のような)です
  • コンポーネントはエンティティに機能と外観を追加します
  • システムはグローバル機能を提供します
<!-- コンポーネント付きエンティティ -->
<a-entity
  geometry="primitive: box; width: 2"
  material="color: red; metalness: 0.5"
  position="0 1.5 -3"
  rotation="0 45 0">
</a-entity>

プリミティブは一般的なエンティティ+コンポーネントの組み合わせのショートカットです:

<!-- プリミティブ(短縮形) -->
<a-box color="red" position="0 1.5 -3" rotation="0 45 0" width="2"></a-box>

<!-- 同等のエンティティ・コンポーネント形式 -->
<a-entity
  geometry="primitive: box; width: 2"
  material="color: red"
  position="0 1.5 -3"
  rotation="0 45 0">
</a-entity>

2. シーンセットアップ

すべてのA-Frameアプリは<a-scene>で始まります:

<!DOCTYPE html>
<html>
  <head>
    <script src="https://aframe.io/releases/1.7.1/aframe.min.js"></script>
  </head>
  <body>
    <a-scene>
      <!-- エンティティはここに配置します -->
      <a-box position="-1 0.5 -3" color="#4CC3D9"></a-box>
      <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
      <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
      <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
      <a-sky color="#ECECEC"></a-sky>
    </a-scene>
  </body>
</html>

シーンは自動的に以下を注入します:

  • デフォルトカメラ(位置: 0 1.6 0
  • ルックコントロール(マウスドラッグ)
  • WASDコントロール(キーボード移動)

3. カメラシステム

デフォルトカメラ(指定されていない場合は自動注入):

<a-entity camera="active: true" look-controls wasd-controls position="0 1.6 0"></a-entity>

カスタムカメラ

<a-camera position="0 2 5" look-controls wasd-controls="acceleration: 50"></a-camera>

カメラリグ(独立した移動と回転):

<a-entity id="rig" position="0 0 0">
  <!-- ヘッドトラッキング用カメラ -->
  <a-camera look-controls></a-camera>

  <!-- 移動はリグに適用され、カメラには適用されません -->
</a-entity>

VRカメラリグとコントローラー

<a-entity id="rig" position="0 0 0">
  <!-- 眼球レベルのカメラ -->
  <a-camera position="0 1.6 0"></a-camera>

  <!-- 左手コントローラー -->
  <a-entity
    hand-controls="hand: left"
    laser-controls="hand: left">
  </a-entity>

  <!-- 右手コントローラー -->
  <a-entity
    hand-controls="hand: right"
    laser-controls="hand: right">
  </a-entity>
</a-entity>

4. ライティング

アンビエントライト(グローバル照度):

<a-entity light="type: ambient; color: #BBB; intensity: 0.5"></a-entity>

ディレクショナルライト(太陽光のような):

<a-entity light="type: directional; color: #FFF; intensity: 0.8" position="1 2 1"></a-entity>

ポイントライト(すべての方向に放射):

<a-entity light="type: point; color: #F00; intensity: 2; distance: 50" position="0 3 0"></a-entity>

スポットライト(円錐形のビーム):

<a-entity light="type: spot; angle: 45; intensity: 1.5" position="0 5 0" rotation="-90 0 0"></a-entity>

5. マテリアルとテクスチャ

標準マテリアル

<a-sphere
  material="color: #FF0000; metalness: 0.5; roughness: 0.3"
  position="0 1 -3">
</a-sphere>

テクスチャマテリアル

<a-assets>
  <img id="woodTexture" src="wood.jpg">
</a-assets>

<a-box material="src: #woodTexture" position="0 1 -3"></a-box>

フラットシェーディング(ライティングなし):

<a-plane material="shader: flat; color: #4CC3D9"></a-plane>

6. アニメーション

プロパティアニメーション

<a-box
  position="0 1 -3"
  animation="property: rotation; to: 0 360 0; loop: true; dur: 5000">
</a-box>

複数のアニメーションanimation__*命名を使用):

<a-sphere
  position="0 1 -3"
  animation__position="property: position; to: 0 3 -3; dir: alternate; loop: true; dur: 2000"
  animation__rotation="property: rotation; to: 360 360 0; loop: true; dur: 4000"
  animation__scale="property: scale; to: 1.5 1.5 1.5; dir: alternate; loop: true; dur: 1000">
</a-sphere>

イベントベースのアニメーション

<a-box
  color="blue"
  animation__mouseenter="property: scale; to: 1.2 1.2 1.2; startEvents: mouseenter"
  animation__mouseleave="property: scale; to: 1 1 1; startEvents: mouseleave"
  animation__click="property: rotation; from: 0 0 0; to: 0 360 0; startEvents: click">
</a-box>

7. アセット管理

パフォーマンスを向上させるためにアセットをプリロードします:

<a-scene>
  <a-assets>
    <!-- 画像 -->
    <img id="texture1" src="texture.jpg">
    <img id="skyTexture" src="sky.jpg">

    <!-- ビデオ -->
    <video id="video360" src="360video.mp4" autoplay loop></video>

    <!-- オーディオ -->
    <audio id="bgMusic" src="music.mp3" preload="auto"></audio>

    <!-- モデル -->
    <a-asset-item id="tree" src="tree.gltf"></a-asset-item>

    <!-- ミックスイン(再利用可能なコンポーネントセット) -->
    <a-mixin id="redMaterial" material="color: red; metalness: 0.7"></a-mixin>
  </a-assets>

  <!-- アセットを使用 -->
  <a-entity gltf-model="#tree" position="2 0 -5"></a-entity>
  <a-sphere mixin="redMaterial" position="0 1 -3"></a-sphere>
  <a-sky src="#skyTexture"></a-sky>
</a-scene>

8. カスタムコンポーネント

ロジックをカプセル化するカスタムコンポーネントを登録します:

AFRAME.registerComponent('rotate-on-click', {
  // コンポーネントスキーマ(設定)
  schema: {
    speed: {type: 'number', default: 1}
  },

  // ライフサイクル:コンポーネント取り付け時に1回呼ばれる
  init: function() {
    this.el.addEventListener('click', () => {
      this.rotating = !this.rotating;
    });
  },

  // ライフサイクル:毎フレーム呼ばれる
  tick: function(time, timeDelta) {
    if (this.rotating) {
      var rotation = this.el.getAttribute('rotation');
      rotation.y += this.data.speed;
      this.el.setAttribute('rotation', rotation);
    }
  }
});
<a-box rotate-on-click="speed: 2" position="0 1 -3"></a-box>

よくあるパターン

パターン1:VRコントローラーとの相互作用

問題:VRでオブジェクトの掴みと操作を有効にする

解決策:hand-controlsとカスタムgrabコンポーネントを使用

<a-scene>
  <!-- VRカメラリグ -->
  <a-entity id="rig">
    <a-camera position="0 1.6 0"></a-camera>

    <a-entity
      id="leftHand"
      hand-controls="hand: left"
      laser-controls="hand: left">
    </a-entity>

    <a-entity
      id="rightHand"
      hand-controls="hand: right"
      laser-controls="hand: right">
    </a-entity>
  </a-entity>

  <!-- つかむことができるオブジェクト -->
  <a-box class="grabbable" position="-1 1.5 -3" color="#4CC3D9"></a-box>
  <a-sphere class="grabbable" position="1 1.5 -3" color="#EF2D5E"></a-sphere>
</a-scene>

<script>
AFRAME.registerComponent('grabbable', {
  init: function() {
    var el = this.el;

    el.addEventListener('triggerdown', function(evt) {
      console.log('つかまれた:', evt.detail.hand);
      el.setAttribute('color', 'green');
    });

    el.addEventListener('triggerup', function(evt) {
      el.setAttribute('color', 'blue');
    });

    el.addEventListener('gripdown', function(evt) {
      // オブジェクトをコントローラーに取り付ける
      var controllerEl = evt.detail.controller;
      controllerEl.object3D.attach(el.object3D);
    });

    el.addEventListener('gripup', function(evt) {
      // コントローラーから取り外す
      var sceneEl = el.sceneEl.object3D;
      sceneEl.attach(el.object3D);
    });
  }
});

// つかむことができるコンポーネントを適用
document.querySelectorAll('.grabbable').forEach(el => {
  el.setAttribute('grabbable', '');
});
</script>

パターン2:360°画像ギャラリー

問題:インタラクティブな360°写真ビューアを作成する

解決策:skyプリミティブとクリック可能なサムネイルを使用

<a-scene>
  <a-assets>
    <img id="city" src="city.jpg">
    <img id="forest" src="forest.jpg">
    <img id="beach" src="beach.jpg">
    <img id="city-thumb" src="city-thumb.jpg">
    <img id="forest-thumb" src="forest-thumb.jpg">
    <img id="beach-thumb" src="beach-thumb.jpg">
    <audio id="click-sound" src="click.mp3"></audio>
  </a-assets>

  <!-- 360画像スフィア -->
  <a-sky id="image-360" src="#city" rotation="0 -130 0"></a-sky>

  <!-- サムネイルメニュー -->
  <a-entity id="menu" position="0 1.6 -2">
    <a-entity class="link"
      geometry="primitive: plane; width: 0.7; height: 0.7"
      material="shader: flat; src: #city-thumb"
      position="-1 0 0"
      sound="on: click; src: #click-sound"
      event-set__mouseenter="scale: 1.2 1.2 1"
      event-set__mouseleave="scale: 1 1 1"
      event-set__click="_target: #image-360; material.src: #city">
    </a-entity>

    <a-entity class="link"
      geometry="primitive: plane; width: 0.7; height: 0.7"
      material="shader: flat; src: #forest-thumb"
      position="0 0 0"
      sound="on: click; src: #click-sound"
      event-set__mouseenter="scale: 1.2 1.2 1"
      event-set__mouseleave="scale: 1 1 1"
      event-set__click="_target: #image-360; material.src: #forest">
    </a-entity>

    <a-entity class="link"
      geometry="primitive: plane; width: 0.7; height: 0.7"
      material="shader: flat; src: #beach-thumb"
      position="1 0 0"
      sound="on: click; src: #click-sound"
      event-set__mouseenter="scale: 1.2 1.2 1"
      event-set__mouseleave="scale: 1 1 1"
      event-set__click="_target: #image-360; material.src: #beach">
    </a-entity>
  </a-entity>

  <!-- ガズインタラクション用カーソル付きカメラ -->
  <a-camera>
    <a-cursor raycaster="objects: .link"></a-cursor>
  </a-camera>
</a-scene>

パターン3:ARヒットテスト(現実世界にオブジェクトを配置)

問題:検出された現実世界のサーフェスに仮想オブジェクトを配置する

解決策:ar-hit-testコンポーネントを使用

<a-scene
  webxr="optionalFeatures: hit-test, dom-overlay; overlayElement: #overlay"
  ar-hit-test="target: #furniture; type: footprint">

  <a-assets>
    <a-asset-item id="chair" src="chair.gltf"></a-asset-item>
  </a-assets>

  <!-- 配置するオブジェクト -->
  <a-entity id="furniture" gltf-model="#chair" scale="0.5 0.5 0.5"></a-entity>

  <!-- AR指示オーバーレイ -->
  <div id="overlay" style="position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
                           background: rgba(0,0,0,0.7); color: white; padding: 15px;
                           border-radius: 8px; font-family: sans-serif;">
    <p id="message">タップしてARモードに入る</p>
  </div>
</a-scene>

<script>
const sceneEl = document.querySelector('a-scene');
const message = document.getElementById('message');

sceneEl.addEventListener('enter-vr', function() {
  if (this.is('ar-mode')) {
    message.textContent = '';

    this.addEventListener('ar-hit-test-start', function() {
      message.innerHTML = '環境をスキャン中、サーフェスを探しています。';
    }, { once: true });

    this.addEventListener('ar-hit-test-achieved', function() {
      message.innerHTML = '画面をタップしてオブジェクトを配置します。';
    }, { once: true });

    this.addEventListener('ar-hit-test-select', function() {
      message.textContent = 'オブジェクトを配置しました!';
      setTimeout(() => message.textContent = '', 2000);
    }, { once: true });
  }
});

sceneEl.addEventListener('exit-vr', function() {
  message.textContent = 'タップしてARモードに入る';
});
</script>

パターン4:マウス/ガズインタラクション

問題:デスクトップマウスまたはVRガズでクリック相互作用を有効にする

解決策:cursorコンポーネントとraycasterを使用

<a-scene>
  <!-- インタラクティブオブジェクト -->
  <a-box
    class="interactive"
    position="-1 1.5 -3"
    color="#4CC3D9"
    event-set__mouseenter="color: yellow"
    event-set__mouseleave="color: #4CC3D9"
    event-set__click="scale: 1.5 1.5 1.5">
  </a-box>

  <a-sphere
    class="interactive"
    position="1 1.5 -3"
    color="#EF2D5E"
    event-set__click="color: orange; scale: 2 2 2">
  </a-sphere>

  <a-plane position="0 0 -4" rotation="-90 0 0" width="10" height="10" color="#7BC8A4"></a-plane>

  <!-- カーソル付きカメラ -->
  <a-camera position="0 1.6 0">
    <!-- .interactiveクラスをターゲットにします -->
    <a-cursor
      raycaster="objects: .interactive"
      fuse="true"
      fuse-timeout="1500">
    </a-cursor>
  </a-camera>
</a-scene>

<script>
// JavaScriptを使用した高度なクリック処理
document.querySelectorAll('.interactive').forEach(el => {
  el.addEventListener('click', function(evt) {
    console.log('クリック:', this.id || this.tagName);
    console.log('交差ポイント:', evt.detail.intersection.point);
  });
});
</script>

パターン5:動的シーン生成

問題:プログラムでエンティティを作成・操作する

解決策:JavaScript DOMの操作を使用

<a-scene>
  <a-camera position="0 1.6 5"></a-camera>
  <a-entity light="type: ambient; color: #888"></a-entity>
  <a-entity light="type: directional; color: #FFF" position="1 2 1"></a-entity>
</a-scene>

<script>
const scene = document.querySelector('a-scene');

// スフィアを作成
function createSphere(x, y, z, color) {
  const entity = document.createElement('a-entity');

  entity.setAttribute('geometry', {
    primitive: 'sphere',
    radius: 0.5
  });

  entity.setAttribute('material', {
    color: color,
    metalness: 0.5,
    roughness: 0.3
  });

  entity.setAttribute('position', {x, y, z});

  // アニメーションを追加
  entity.setAttribute('animation', {
    property: 'position',
    to: `${x} ${y + 1} ${z}`,
    dir: 'alternate',
    loop: true,
    dur: 2000
  });

  scene.appendChild(entity);
  return entity;
}

// スフィアのグリッドを生成
for (let x = -3; x <= 3; x += 1.5) {
  for (let z = -5; z <= -2; z += 1.5) {
    const color = `#${Math.floor(Math.random()*16777215).toString(16)}`;
    createSphere(x, 1, z, color);
  }
}

// コンポーネント変更をリッスン
scene.addEventListener('componentchanged', function(evt) {
  console.log('コンポーネント変更:', evt.detail.name);
});

// Three.jsオブジェクトに直接アクセス
setTimeout(() => {
  const entities = document.querySelectorAll('a-entity[geometry]');
  entities.forEach(el => {
    el.object3D.visible = true; // Three.jsの直接操作
  });
}, 1000);
</script>

パターン6:環境とスカイボックス

問題:イマーシブな環境を素早く作成する

解決策:コミュニティコンポーネントと360度画像を使用

<html>
  <head>
    <script src="https://aframe.io/releases/1.7.1/aframe.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@fern-solutions/aframe-sky-background/dist/sky-background.umd.min.js"></script>
    <script src="https://cdn.jsdelivr.net/gh/c-frame/aframe-extras@7.5.0/dist/aframe-extras.min.js"></script>
  </head>
  <body>
    <a-scene>
      <!-- グラデーション空 -->
      <a-sky-background
        top-color="#4A90E2"
        bottom-color="#87CEEB">
      </a-sky-background>

      <!-- またはテクスチャ付き空 -->
      <!-- <a-sky src="sky.jpg" rotation="0 -130 0"></a-sky> -->

      <!-- 海 -->
      <a-entity
        ocean="density: 20; width: 50; depth: 50; speed: 4"
        material="color: #9CE3F9; opacity: 0.75; metalness: 0; roughness: 1"
        rotation="-90 0 0">
      </a-entity>

      <!-- 大気用パーティクルシステム -->
      <a-entity
        particle-system="preset: snow; particleCount: 2000; color: #FFF">
      </a-entity>

      <a-entity light="type: ambient; color: #888"></a-entity>
      <a-entity light="type: directional; color: #FFF; intensity: 0.7" position="1 2 1"></a-entity>
    </a-scene>
  </body>
</html>

パターン7:GLTFモデルの読み込み

問題:3Dモデルを読み込んで表示する

解決策:gltf-modelコンポーネントとアセット管理を使用

<a-scene>
  <a-assets>
    <a-asset-item id="robot" src="robot.gltf"></a-asset-item>
    <a-asset-item id="building" src="building.glb"></a-asset-item>
  </a-assets>

  <!-- モデルを読み込む -->
  <a-entity
    gltf-model="#robot"
    position="0 0 -3"
    scale="0.5 0.5 0.5"
    animation="property: rotation; to: 0 360 0; loop: true; dur: 10000">
  </a-entity>

  <!-- 拡張機能付きで読み込む(アニメーション) -->
  <a-entity
    gltf-model="#building"
    position="5 0 -10"
    animation-mixer="clip: *; loop: repeat">
  </a-entity>

  <a-camera position="0 1.6 5"></a-camera>
  <a-entity light="type: ambient; intensity: 0.5"></a-entity>
  <a-entity light="type: directional; intensity: 0.8" position="2 4 2"></a-entity>
</a-scene>

<script>
// モデル読み込みイベントの処理
document.querySelector('[gltf-model="#robot"]').addEventListener('model-loaded', (evt) => {
  console.log('モデル読み込み:', evt.detail.model);

  // Three.jsオブジェクトにアクセス
  const model = evt.detail.model;
  model.traverse(node => {
    if (node.isMesh) {
      console.log('メッシュ発見:', node.name);
    }
  });
});

document.querySelector('[gltf-model="#robot"]').addEventListener('model-error', (evt) => {
  console.error('モデル読み込みエラー:', evt.detail);
});
</script>

統合パターン

Three.jsとの連携

基礎となるThree.jsオブジェクトにアクセス:

// Three.jsシーンを取得
const scene = document.querySelector('a-scene').object3D;

// エンティティのThree.jsオブジェクトを取得
const box = document.querySelector('a-box');
const threeObject = box.object3D;

// Three.jsの直接操作
threeObject.position.set(1, 2, 3);
threeObject.rotation.y = Math.PI / 4;

// カスタムThree.jsオブジェクトを追加
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

GSAP(アニメーション)との連携

GSAPでA-Frameエンティティをアニメーション:

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script>
const box = document.querySelector('a-box');

// 位置をアニメーション
gsap.to(box.object3D.position, {
  x: 3,
  y: 2,
  z: -5,
  duration: 2,
  ease: 'power2.inOut'
});

// 回転をアニメーション
gsap.to(box.object3D.rotation, {
  y: Math.PI * 2,
  duration: 3,
  repeat: -1,
  ease: 'none'
});

// 属性をアニメーション
gsap.to(box.components.material.material, {
  opacity: 0.5,
  duration: 1
});
</script>

Reactとの連携

ReactコンポーネントにA-Frameを統合:

import React, { useEffect, useRef } from 'react';
import 'aframe';

function VRScene() {
  const sceneRef = useRef(null);

  useEffect(() => {
    const scene = sceneRef.current;

    // エンティティを動的に作成
    const entity = document.createElement('a-sphere');
    entity.setAttribute('position', '0 1.5 -3');
    entity.setAttribute('color', '#EF2D5E');
    scene.appendChild(entity);

    // イベントをリッスン
    scene.addEventListener('enter-vr', () => {
      console.log('VRモードに入りました');
    });
  }, []);

  return (
    <a-scene ref={sceneRef}>
      <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" />
      <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E" />
      <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D" />
      <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" />
      <a-sky color="#ECECEC" />
    </a-scene>
  );
}

export default VRScene;

パフォーマンスのベストプラクティス

1. アセット管理を使用

アセットをプリロードしてブロッキングを回避:

<a-assets>
  <img id="texture1" src="large-texture.jpg">
  <video id="video360" src="360video.mp4" preload="auto"></video>
  <a-asset-item id="model" src="complex-model.gltf"></a-asset-item>
</a-assets>

2. エンティティプーリング

エンティティを作成・破棄の代わりに再利用:

AFRAME.registerComponent('bullet-pool', {
  init: function() {
    this.pool = [];
    this.used = [];

    // 弾をプリ作成
    for (let i = 0; i < 20; i++) {
      const bullet = document.createElement('a-sphere');
      bullet.setAttribute('radius', 0.1);
      bullet.setAttribute('visible', false);
      this.el.sceneEl.appendChild(bullet);
      this.pool.push(bullet);
    }
  },

  getBullet: function() {
    if (this.pool.length > 0) {
      const bullet = this.pool.pop();
      bullet.setAttribute('visible', true);
      this.used.push(bullet);
      return bullet;
    }
  },

  returnBullet: function(bullet) {
    bullet.setAttribute('visible', false);
    const index = this.used.indexOf(bullet);
    if (index > -1) {
      this.used.splice(index, 1);
      this.pool.push(bullet);
    }
  }
});

3. ジオメトリを最適化

低ポリゴンモデルとLODを使用:

<!-- 遠い物体用の低ポリ -->
<a-sphere radius="1" segments-width="8" segments-height="6"></a-sphere>

<!-- 近い物体用の高ポリ -->
<a-sphere radius="1" segments-width="32" segments-height="32"></a-sphere>

4. ドローコールを制限

繰り返すオブジェクトにインスタンシングを使用:

AFRAME.registerComponent('instanced-trees', {
  init: function() {
    // 繰り返しジオメトリにThree.jsのInstancedMeshを使用
    const scene = this.el.sceneEl.object3D;
    const geometry = new THREE.ConeGeometry(0.5, 2, 8);
    const material = new THREE.MeshStandardMaterial({ color: 0x228B22 });
    const mesh = new THREE.InstancedMesh(geometry, material, 100);

    // インスタンスを配置
    for (let i = 0; i < 100; i++) {
      const matrix = new THREE.Matrix4();
      matrix.setPosition(
        Math.random() * 20 - 10,
        0,
        Math.random() * 20 - 10
      );
      mesh.setMatrixAt(i, matrix);
    }

    scene.add(mesh);
  }
});

5. tick()関数をスロットル

必要ない場合は毎フレーム更新しない:

AFRAME.registerComponent('throttled-update', {
  init: function() {
    this.lastUpdate = 0;
    this.updateInterval = 100; // ms
  },

  tick: function(time, timeDelta) {
    if (time - this.lastUpdate >= this.updateInterval) {
      // 重い処理をここに
      this.lastUpdate = time;
    }
  }
});

6. パフォーマンス監視にStatsコンポーネントを使用

<a-scene stats>
  <!-- FPSとパフォーマンスメトリクスを表示 -->
</a-scene>

よくある落とし穴と解決策

落とし穴1:エンティティが表示されない

問題:エンティティを追加したが見えない

原因

  • エンティティがカメラの背後に配置されている
  • スケールが0または非常に小さい
  • マテリアルのopacityが0
  • エンティティがカメラフラスタムの外

解決策

// シーン読み込みを待つ
const scene = document.querySelector('a-scene');
scene.addEventListener('loaded', () => {
  const entity = document.createElement('a-box');
  entity.setAttribute('position', '0 1.5 -3'); // カメラの前に配置
  entity.setAttribute('color', 'red');
  scene.appendChild(entity);
});

// デバッグ:エンティティ位置を確認
console.log(entity.getAttribute('position'));

// デバッグ:エンティティがシーンに入っているか確認
console.log(entity.parentNode); // <a-scene>であるべき

落とし穴2:イベントが発動しない

問題:クリック/mouseenterイベントがトリガーされない

原因:raycasterまたはカーソルがない

解決策

<!-- カメラにカーソルを追加 -->
<a-camera>
  <a-cursor raycaster="objects: .interactive"></a-cursor>
</a-camera>

<!-- インタラクティブオブジェクトにクラスを追加 -->
<a-box class="interactive" position="0 1 -3"></a-box>

<!-- またはraycasterを直接使用 -->
<a-entity raycaster="objects: [geometry]" cursor></a-entity>

落とし穴3:パフォーマンス低下

問題:多数のエンティティでFPSが低い

原因

  • ドローコールが多すぎる
  • ジオメトリが複雑
  • テクスチャが最適化されていない
  • tick()の更新が多すぎる

解決策

// 1. オブジェクトプーリングを使用(パフォーマンスセクション参照)
// 2. ジオメトリを単純化
// 3. テクスチャを最適化(サイズを減らす、圧縮を使用)
// 4. 更新をスロットル

AFRAME.registerComponent('optimize-far-entities', {
  tick: function() {
    const camera = this.el.sceneEl.camera;
    const entities = document.querySelectorAll('[geometry]');

    entities.forEach(el => {
      const distance = el.object3D.position.distanceTo(camera.position);

      // 遠くのエンティティを非表示に
      el.object3D.visible = distance < 50;
    });
  }
});

落とし穴4:Zファイティング(サーフェスの重なり)

問題:サーフェスが重なるとちらつく

原因:同じ位置に2つのサーフェスがある

解決策

<!-- サーフェスをわずかにオフセット -->
<a-plane position="0 0.01 0" rotation="-90 0 0"></a-plane>
<a-plane position="0 0.02 0" rotation="-90 0 0"></a-plane>

<!-- またはrenderOrderを使用 -->
<a-entity
  geometry="primitive: plane"
  material="src: #texture1; transparent: true"
  class="has-render-order">
</a-entity>

<script>
document.querySelector('.has-render-order').object3D.renderOrder = 1;
</script>

落とし穴5:モバイルVRパフォーマンス

問題:モバイルVRのパフォーマンスが低い

解決策

<!-- レンダラーの最大キャンバスサイズを削減 -->
<a-scene renderer="maxCanvasWidth: 1920; maxCanvasHeight: 1920">

<!-- 低ポリゴンモデルを使用 -->
<a-sphere radius="1" segments-width="8" segments-height="6"></a-sphere>

<!-- ライトを制限(モバイルで高い負荷) -->
<a-entity light="type: ambient; intensity: 0.6"></a-entity>
<a-entity light="type: directional; intensity: 0.4" position="1 2 1"></a-entity>

<!-- 必要に応じてアンチエイリアシングを無効化 -->
<a-scene renderer="antialias: false">
</a-scene>

落とし穴6:アセット読み込みの問題

問題:アセットが読み込まれないまたはCORSエラー

解決策

<!-- crossorigin属性を使用 -->
<a-assets>
  <img id="texture" src="https://example.com/texture.jpg" crossorigin="anonymous">
</a-assets>

<!-- アセットの読み込みを待つ -->
<script>
const assets = document.querySelector('a-assets');
assets.addEventListener('loaded', () => {
  console.log('すべてのアセットが読み込まれました');
  // アセットを使用しても安全
});

assets.addEventListener('timeout', () => {
  console.error('アセット読み込みタイムアウト');
});
</script>

<!-- 読み込みエラーを処理 -->
<script>
const img = document.querySelector('img#texture');
img.addEventListener('error', () => {
  console.error('テクスチャの読み込みに失敗');
  // フォールバックを使用
  img.src = 'fallback-texture.jpg';
});
</script>

リソース

関連スキル

  • threejs-webgl:A-Frameの宣言的APIを超えた高度なThree.js制御
  • babylonjs-engine:異なるアーキテクチャの代替3Dエンジン
  • gsap-scrolltrigger:GSAPでA-Frameエンティティをアニメーション
  • react-three-fiber:Three.jsへのReactアプローチ(A-FrameのHTMLアプローチと比較)

ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ

詳細情報

作者
freshtechbro
リポジトリ
freshtechbro/claudedesignskills
ライセンス
MIT
最終更新
不明

Source: https://github.com/freshtechbro/claudedesignskills / ライセンス: MIT

関連スキル

汎用ソフトウェア開発⭐ リポ 39,967

doubt-driven-development

重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 1,175

apprun-skills

TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。

by yysun
OpenAIソフトウェア開発⭐ リポ 797

desloppify

コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。

by Git-on-my-level
汎用ソフトウェア開発⭐ リポ 39,967

debugging-and-error-recovery

テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

test-driven-development

テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

incremental-implementation

変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。

by addyosmani
本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: freshtechbro · freshtechbro/claudedesignskills · ライセンス: MIT