A-FrameでWebVRのカメラをトラッキング

#A-Frame
バーチャルリアリティ体験を構築するためのオープンソースのWebフレームワークの一つです。
https://aframe.io

#Entity Component System
<img width="545" alt="スクリーンショット 2020-03-20 0.58.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255581/53fb699c-36d4-0e40-edc5-0f3583b6df22.png">
>A-FrameのEntity Component SystemのComponentは外観・動作・機能を構築するためにEntity上で組み合わせ・照合・および構成できるJavascriptモジュールです。
JavaScriptでComponentを登録し、DOMから宣言的に使用できます。
Componentは構成可能・再利用可能・および共有可能です。

>A-FrameアプリケーションのほとんどのコードはComponent内に存在する必要があります。

[*A-Frameから*](https://aframe.io/docs/master/introduction/writing-a-component.html)

ECS(エンティティ・コンポーネント・システム)とは、主にゲーム開発で使用されているソフトウェアアーキテクチャパターンです。ECSは継承よりコンポジションの原則に従うことで、より柔軟にエンティティを定義することを可能にしています。

#カメラをトラッキングするコンポーネントの作成
常にカメラの正面に移動させ、カメラの方向を向かせる機能のコンポーネント(camera-tracking)を作成し、文字とボックスのエンティティに組み込みます。
また、camera-trackingのプロパティとしてカメラからの「半径・X距離・Y距離」を与えることで画面レイアウトをしやすくします。

<img width="678" alt="スクリーンショット 2020-03-20 1.25.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255581/3f93b519-41d2-a274-2ebe-7c302917e4d8.png">

<img width="678" alt="スクリーンショット 2020-03-20 1.25.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255581/647bd984-a93d-6f71-b59f-d57801508991.png">


#コード
###HTML
```html:index.html
<html>
 <head>
  <script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
  <script src="tracking.js"></script>
 </head>

 <body>
  <a-scene>
   <!--Camera-->
   <a-entity id="camera" camera></a-entity>
   <!--Field-->
   <a-sky color="#44AAFF"></a-sky>
   <a-plane position="0 0 -5" rotation="-90 0 0" width="6" height="6" color="#BB9944" shadow></a-plane>
   <!--Camera-tracking Text-->
   <a-entity id="text" camera-tracking="circleR: 2; shiftX: 1; shiftY: 0.1"
             text="font: mozillavr; value: Tracking test.; color: black" 
             scale="3 3 1">
   </a-entity>
   <!--Camera-tracking Box-->
   <a-entity id="box" camera-tracking="circleR: 3; shiftX: 0; shiftY: -0.5"
             geometry="primitive: box"
             scale="0.8 0.3 0.2" material="color: red" shadow>
        </a-entity>
  </a-scene>
 </body>
</html>
```
###JavaScript
```javascript:tracking.js
AFRAME.registerComponent('camera-tracking', {

  schema: {
    circleR: {type: 'float', default: '1'},
    shiftX: {type: 'float', default: '0'},
    shiftY: {type: 'float', default: '0'}
  },

  tick: function () {
    var camera = document.querySelector('#camera');

    var cameraPosition = camera.object3D.position;
    var cameraRotation = camera.object3D.rotation;
    
    // center position
    var cameraX = cameraPosition.x;
    var cameraY = cameraPosition.y;
    var cameraZ = cameraPosition.z;

    // rotation degree
    var degreePit = (cameraRotation.x * 180 / Math.PI);
    var degreeYaw = (cameraRotation.y * 180 / Math.PI);

    // circle size
    var r = this.data.circleR;

    // rotation point
    var positionY = Math.sin(degreePit / 180 * Math.PI) * r;
    var xzr = Math.cos(degreePit / 180 * Math.PI) * r;
    var positionX = Math.cos((degreeYaw + 90) / 180 * Math.PI) * xzr;
    var positionZ = Math.sin((degreeYaw + 90) / 180 * Math.PI) * -1 * xzr;

    // shift x value
    var addX = this.data.shiftX;
    var addXPosX = Math.sin((degreeYaw + 90) / 180 * Math.PI) * addX;
    var addXPosZ = Math.cos((degreeYaw + 90) / 180 * Math.PI) * addX;

    // shift y value
    var addY = this.data.shiftY;
    var addYPosX = Math.sin(degreePit / 180 * Math.PI) * Math.cos((degreeYaw + 90) / 180 * Math.PI) * -1 * addY;
    var addYPosY = Math.sin((degreePit + 90) / 180 * Math.PI) * addY;
    var addYPosZ = Math.sin(degreePit / 180 * Math.PI) * Math.sin((degreeYaw + 90) / 180 * Math.PI) * addY;

    // set properties
    this.el.setAttribute('rotation', degreePit +' '+ degreeYaw +'  0');
    this.el.setAttribute('position', (positionX + cameraX + addXPosX + addYPosX)+' '+ (positionY + cameraY + addYPosY) +' '+ (positionZ + cameraZ + addXPosZ + addYPosZ));
    
  }
});
```

#解説