# A-Frame

A-Frame の中心を構成するものは entity-component (ECS) です。アプリケーションコードをA-Frameコンポーネントの中に配置することで、何度も利用することができ、モジュール化することができ、組み立てることができ、連携を切ることができ、カプセル化し、宣言することができます。

これはしないでください:

<a-scene>
  <a-box></a-box>
  <!-- ... -->
</a-scene>

<script>
  // My JavaScript code here!
  // ... NO!
</script>

A-Frame コンポーネントの中にコードを配置することで、正しいタイミングで、カプセル化された再利用可能なコードを実行できます。 これがA-Frameを利用する目的です。

このようにしてください:

<script>
  AFRAME.registerComponent('code-that-does-this', {
    init: function () {
      // Code here.
      console.log(this.el);
    }
  });

  AFRAME.registerComponent('code-to-attach-to-box', {
    init: function () {
      // Code here.
      console.log(this.el);
    }
  });
</script>

<a-scene code-that-does-this>
  <a-box code-to-attach-to-box></a-box>
  <!-- ... -->
</a-scene>

# パフォーマンス

VRにおいてパフォーマンスはとても大切です。人々に快適な体験を与えるために高いフレームレートを維持するべきです。 A-Frameのシーンでパフォーマンスを維持する方法を紹介します。

  • [素性されるハードウェアスペック][vr-headsets-and-webvr-browsers]を用いること.

  • [統計コンポーネント][stats] を用いて、様々な指標(フレームレートや頂点数/面数の総数、ジオメトリやマテリアルの数、描画の呼び出し数、エンティティの数など) フレームレートを最大化し、それ以外を最小化したいところです。

  • 描画の呼び出しを出来る限り制限すること。最適化されていないジオメトリ、オブジェクト、モデルは基本的に描画処理を行います。経験則では、300以下に抑えるべきです。  可能な限り性的なメッシュを[統合][geometrymerger]してください。そしてthree.jsのマテリアルを利用して、頂点の色を有効化してください。three.jsのジオメトリは頂点ごとの色やUVSのデータを保有し、統合した後のジオメトリを操作するために使われます。

  • [アセットマネジメントシステム][asm] を活用して、キャッシュとアセットのプリロードを行うこと

  • もしモデルを使っているなら、リアルタイムの反射やシャドーを使わずに、テクスチャにベークすること

  • 一般的にはエンティティと光源の数が少ないほどパフォーマンスは良くなります

  • テクスチャの解像度は、倍数で収まるように(例として256x256, 512x1024という組み合わせ)することで、レンダラーがランタイムでリサイズすることを避けることができます。

  • モデルの持つ面と頂点の数を制限すること

  • ジオメトリーインスタンシングや[ジオメトリーマージング][geometrymerger]、詳細度レベルなど上級のテクニックを用いること

  • レイキャスターやコライダーなどを使った場合、適用する対象を指定すること

  • 継続的に実行されるビヘイビアを用いる場合、A-Frameコンポーネントのtickを用いて、グローバルレンダーループに関連付けてください。  AFRAME.utils.throttleTickのようなユーティリティを用いて、tickハンドラーが適切に実行されるようにの回数を制限してください

  • a-skyの代わりに**[バックグラウンドコンポーネント][background]**を用いて、色を定義し背景色としてください。不必要なジオメトリの作成を防ぎます。

  • three.jsの level (el.object3D.position, el.object3D.rotation, el.object3D.scale, el.object3D.visible を活用して、 position, rotation, scale, visibleを更新するようにしてください。 .setAttributeのオーバーヘッドを防ぎます。

  • もし同種のエンティティを生成したり削除、再生成する場合には、 [pool component][pool] を用いて、事前生成と再利用を行うようにしてください。 これはエンティティを生成する手間を飛躍的に抑え、ゴミファイルの生成を抑制します。

  • アニメーションコンポーネントを使う際は[アニメーション値を直接指定してください][animation]。これによって.setAttribute をスキップし、JSで直接アニメーションできます。 例えばmaterial.opacity の代わりに components.material.material.opacityを使ってアニメーションします。

# GPUによるテクスチャのプリローディング

ノンブロックテクスチャがGPUにアップロードされて利用可能になるまでは、マテリアルとテクスチャが一番手前になるように描画してください。マテリアルとテクスチャが最初に描画されると、ブラウザはハングし、GPUへのアップロードが妨害されます。手動でテクスチャをプリアップロードするには以下の方法で呼び出します。

document.querySelector('a-scene').renderer.setTexture2D(ourTexture, 0);

A-Frameの中の便利なAPIを利用して自動的に実行することもできます。

たとえば、これは360° イメージギャラリー (opens new window)で明らかです。 ブラウザパフォーマンスツールを見ると、最初のお世話になりますARCHITの安藤です。イメージの描画時にコマ落ちが発生します。2回目以降にはスムーズな移行が行われます。 マテリアルとテクスチャを可能な限り再利用してくださいユニークなマテリアルの数を減らすためです。テクスチャマッピングはマテリアルを再利用し、より多くのバリエーションを表現するために有効です。 MeshLambertMaterialMeshBasicMaterialといったシンプルなthree.jsのマテリアルはより快適に動作し、ポリゴンの少ないシーンにおいて有効です。

特に、事前にベークされたマテリアルはパフォーマンスを劇的に改善させます。 A-FrameのデフォルトであるPBRベースの(標準的な)マテリアルはより現実味がありますが、負荷が高く、シンプルなシーンでは不要です。

# JavaScriptのガーベージを最小化する

ガーベージ生成を避け、JavaScriptのオブジェクトや配列、文字列、そして関数をインスタンス化することに最大限努めましょう。 2DWebでは、JavaScriptのオブジェクトを生成することはたいした問題ではありません。ガーベージコレクターを実行するための時間があるからです。 VRの場合には、ガーベージコレクションはコマ落ちを発生させ、メモリーを綺麗にするのに時間を取ります。 これを避けるためには、メモリアロケーションを最小化し、オブジェクトをガーベージに投入されることを防ぐ必要があります。

アロケーションとガーベージコレクションの探知については Firefox (opens new window)Chrome (opens new window) のパフォーマンスツールを活用してください。

Object.keys(obj).forEach(function () { });,のようなパターンは避けるようにしてください。 新しい配列や関数やコールバックとfor (key in obj)を退治させるような方法です。

配列のイテレーションについては.forEachを用いるのではなく単純な forループを代わりに使ってください。

不必要なオブジェクトのコピーを防ぐために、 utils.clone.sliceではなくutils.extend(target, source)を使うようにしてください。

イベントを放出する際には、イベント詳細で利用しているオブジェクトを再利用してください。

AFRAME.registerComponent('foo', {
  init: function () {
    this.someData = [];
    this.evtDetail = {someData: this.someData};
  },

  tick: function (time) {
    this.someData.push(time);
    this.el.emit('bar', this.evtDetail);
  }
});

これらの提案は特に tick() ハンドラーを用いる際に有用です。全てのフレームで実行されるからです。

以下の記事でさらなるガーベージコレクション削減方法を知ることができます。

# tick ハンドラー

コンポーネント tickハンドラーの中では、シンプルに新規オブジェクトを生成してください。オブジェクトは再利用するべきです。

再利用可能な独自の補助変数を作成するパターンとしては、以下のようなものがあります。

以下では、ヘルパーベクトルとクォータニオンを作成し、フレームごとに新しいものを作成するのではなく、それらをフレームの間で使用します。 これらの変数はすべてのインスタンスで共有されるため、状態を保持しないということに注意してください。 コンポーネントの こうすることで、メモリ使用量とガベージコレクションを減らすことができます。

AFRAME.registerComponent('foo', {
  tick: function () {
    this.doSomething();
  },

  doSomething: (function () {
    var helperVector = new THREE.Vector3();
    var helperQuaternion = new THREE.Quaternion();

    return function () {
      helperVector.copy(this.el.object3D.position);
      helperQuaternion.copy(this.el.object3D.quaternion);
    };
  })()
});

また、tick内のコンポーネントを継続的に変更する場合は、プロパティを更新するために同じオブジェクトを使用するようにしてください。 A-Frameは最新のオブジェクトの状態を記録しており、それ以降の呼び出しで型チェックを無効にすることで、さらに高速化されます。

以下は、1目盛りごとに回転を行う際に推奨されるされるティック関数の例です。

AFRAME.registerComponent('foo', {
  tick: function () {
    var el = this.el;
    var rotationTmp = this.rotationTmp = this.rotationTmp || {x: 0, y: 0, z: 0};
    var rotation = el.getAttribute('rotation');
    rotationTmp.x = rotation.x + 0.1;
    rotationTmp.y = rotation.y + 0.1;
    rotationTmp.z = rotation.z + 0.1;
    el.setAttribute('rotation', rotationTmp);
  }
});

ティック関数で何を行うかに気を使ってください。 1秒間に90回実行されるため重要なパフォーマンスコードとして扱うようにしてください。

は、重要な関数として扱われます。 1秒間に90回実行されるため、パフォーマンスコードとなります。 より少ない間隔でコードを実行するためにutils.throttleTick を検討してください。

# VR デザイン

VRのエクスペリエンスデザインは、フラットな画面におけるエクスペリエンスデザインとは異なります。 この新しいメディアのために、特に快適さと表現において新たなプラクティスが必要です。

ユーザーエクスペリエンスについては、すでに多くの文献があるので、ここでは割愛しますが、 VRインタラクションデザインはかなり新しいものであり、まだ何も確率されていないことを記憶にとどめておいてください。

いくつか注意点を。

  • 一般的な黄金律は、不用意にユーザーの視点制御を奪わないことです。
  • 単位(位置など)はメートル法を用いてください。WebVR APIは、ほとんどのカメラコントロールの姿勢にメートル単位で値を返すからです。メートルで考えることで 期待通りのスケールを実現します。

手やコントローラーを活用してください。最高のエクスペリエンスのために、そのアプリケーションごとに特定の動作を割り当て全てのプラットフォームに溶け込ませてください。