Open10

【Webフレームワーク / 学習メモ】A-Frame

noknok

A-Frameとは

  • WebXRに対応した、オープンソースのWebフレームワーク
  • HTMLで3Dシーンの作成ができる
  • A-Frame要素も、DOM操作が可能
  • Unityでも採用されている、ECS(Entity Component System)が導入されている
  • three.jsに基づくフレームワークのため、three.js API への完全なアクセスが可能

公式サイト

https://aframe.io/

noknok

A-Frame導入方法

  • HTMLファイルのヘッダー要素に、CDNでA-Frameの参照を追加することで導入可能
index.html
<head>
    <script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
</head>

https://aframe.io/docs/1.4.0/introduction/#getting-started

noknok

A-Frameのシーン定義とPrimitive要素

index.html
<html>
  <head>
    <script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
  </head>
  <body>
    <a-scene>
      <a-box position="-1 0.5 -3" rotation="0 45 0" 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>
Primitive要素 概要
<a-scene> canvasにレンダリングするシーン(3D空間)。
sceneタグの中に追加した内容がシーンに反映される
<a-box> 立方体
<a-sphere> 球体
<a-cylinder> 円柱
<a-plane> 平面
<a-sky> 背景
noknok

ECS(Entity-Component-System)

A-Frameは、ECSのアーキテクチャを備えている

https://aframe.io/docs/1.4.0/introduction/entity-component-system.html

ECSの要素

要素 概要
Entities
(エンティティ)
・コンポーネントをアタッチできる、コンテナオブジェクト
・Unityで言うと「空のGameObject」に相当するもの
・タグ要素だと「<a-entity>」が相当する
Components
(コンポーネント)
・再利用可能な部品
・エンティティにアタッチすることで外観・動作・機能を提供できる
・Unityで言うと「AddComponent」して機能を与えるイメージ
・「AFRAME.registerComponent」でコンポーネントの定義が可能
Systems
(システム)
・コンポーネントのクラスに対してグローバルなスコープ、管理、およびサービスを提供するもの
・タグ要素だと「<a-scene>」が相当する
noknok

ECS:コンポーネントの基本構成

index.html
AFRAME.registerComponent('foo', {
  schema: {
    bar: {type: 'number'},
    hoge: {type: 'string'}
  },

  init: function () {
  },

  update: function () {
  },

  remove: function () {
  },

  tick: function (time, timeDelta) {
  }
});
項目 概要
registerComponentの第一引数 コンポーネントの名称
registerComponentの第二引数 コンポーネントの定義
schema ・コンポーネントのプロパティを定義する部分
init ・コンポーネントをエンティティに追加した際に、1回だけ呼ばれる
update ・コンポーネントのプロパティが更新された際に呼ばれる
remove ・コンポーネントが、エンティティから外された際に呼ばれる
tick ・毎フレーム呼ばれる

参考URL

https://aframe.io/docs/1.4.0/introduction/entity-component-system.html#extensibility
https://aframe.io/docs/1.4.0/introduction/writing-a-component.html

noknok

ECS:JavaScriptからのDOM操作

A-Frame要素も、HTML要素と同様にDOM操作が可能


要素の作成と追加(<a-entity>の場合)

const entityEL = document.createElement('a-entity');

// 例:<a-scene>要素の子要素に、<a-entity>を追加するサンプル
const sceneEl = document.querySelector("a-scene");
const entityEL = document.createElement('a-entity');
sceneEl.appendChild(entityEL);

要素の削除(<a-entity>の場合)

// 構文:削除する要素の親要素.removeChild(削除する子要素);

// 例:<a-scene>要素から、<a-entity>を削除するサンプル
const sceneEl = document.querySelector("a-scene");
const entityEl = document.querySelector("a-entity");

sceneEl.removeChild(entityEL);

querySelectorによる、要素の取得

// シーンとボックス要素を取得
const sceneEl = document.querySelector('a-scene');
const boxEl = document.querySelector('a-box');

querySelectorAllによる、要素のグループの取得

// シーン要素から、ボックス要素のグループを取得する
var sceneEl = document.querySelector('a-scene');
console.log(sceneEl.querySelectorAll('a-box'));

// ログ出力例(box要素が2つ存在した場合)
// 出力: NodeList(2) [a-box, a-box]

コンポーネントの追加・更新

追加も更新も「setAttribute」を使う

entityEl.setAttribute('コンポーネント名', 設定するプロパティ値);

// 例:ジオメトリの種類、高さ、幅 を指定する
entityEl.setAttribute('geometry', {
  primitive: 'box',
  height: 3,
  width: 1
});

コンポーネントの削除

entityEl.removeAttribute('コンポーネント名');

参考URL

https://aframe.io/docs/1.4.0/introduction/javascript-events-dom-apis.html#with-queryselector

noknok

ECS:コンポーネント内でのプロパティ参照

プロパティ 概要
this.data コンポーネントのプロパティ
this.el HTML 要素としてのエンティティへの参照
this.el.sceneEl HTML 要素としてのシーンへの参照
this.id コンポーネントの個々のインスタンスの ID(コンポーネントが複数のインスタンスを持つことができる場合)

「this.data」の参照例

「this.data.〜」という形でプロパティ参照する

AFRAME.registerComponent('foo', {
    schema: {
        my_val_1: {type: 'number', default: 12345},
        my_val_2: {type: 'string', default: 'あいうえお'},
    },
  init: function () {
        console.log(this.data.my_val_1);
        console.log(this.data.my_val_2);
  }
});

参考URL

https://aframe.io/docs/1.4.0/core/component.html#component-prototype-properties

noknok

ECS:コンポーネントの参照

AFRAME.registerComponent('foo', {
  init: function () {
  }
});
var fooComponent = document.querySelector('[foo]').components.foo;
console.log(fooComponent);

参考URL

https://aframe.io/docs/1.4.0/core/component.html#accessing-a-component’s-members-and-methods

noknok

ECS:コンポーネントのメンバー変数、メソッドへのアクセス

AFRAME.registerComponent('foo', {
    schema: {
        my_data: {type: 'string', default: 'あいうえお'},
    },
    // LifeCycle Methods
    init: function() {
        this.bar = 12345;
    },
    // Original Methods
    message: function() {
    console.log(`message : ${this.data.my_data}`);
    },
});
var fooComponent = document.querySelector('[foo]').components.foo;
console.log(fooComponent.bar);
console.log(fooComponent.message());

参考URL

https://aframe.io/docs/1.4.0/core/component.html#accessing-a-component’s-members-and-methods