A-FrameでOculus Questのコントローラを使う

A-Frameで手っ取り早くOculus Questの入力を取得するサンプルです。
本記事では、getElementByIdで各コントローラの情報を取得したあとに入力イベントを追加する方法(sample1)と、AFRAME.registerComponentを使って入力情報を取得するコンポーネントを自作する方法(sample2)の2通りを掲載しています。どちらも同じような結果が得られますので、わかりやすい方を参考にしてください。

なおsample1のコードの概要はこちらのブログ[[LINK]](http://tks-yoshinaga.hatenablog.com/entry/oculus_quest_aframe)にて解説しています。
また、公式サイト[[LINK]](https://aframe.io/docs/0.9.0/introduction/writing-a-component.html#registering-the-component-with-aframe-registercomponent)でsampl2で用いているcomponent追加方法の詳細が記載されています。
詳しいことを知りたい方ははぜひこれらをご一読ください。
#1. 体験
Oculus Questを装着しWebブラウザでご覧ください。
https://quest-test.glitch.me

Videoデモ
[![](https://img.youtube.com/vi/vOsfX_jxClY/0.jpg)](https://www.youtube.com/watch?v=vOsfX_jxClY)


#2. サンプルコード
##sample1
getElementByIdでコントローラを取得してアクションリスナを追加。

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Oculus Quest Input</title>
    <meta name="description" content="Oculus Quest Input">
    <script src="https://aframe.io/releases/0.9.2/aframe.min.js"></script>
  </head>
  <body>
    <a-scene background="color: #FAFAFA">
      <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" shadow></a-box>
      <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E" shadow></a-sphere>
      <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D" shadow></a-cylinder>
      <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" shadow></a-plane>
      <a-entity id="ctlL" laser-controls="hand: left"></a-entity>
      <a-entity id="ctlR" laser-controls="hand: right"></a-entity>
      <a-entity camera look-controls position="0 1.6 0">
        <a-text id="txt" value="" position="0 0 -1" scale="0.4 0.4 0.4" align="center" color="#000000"></a-text>
        <a-text id="txt2" value="" position="0 -0.15 -1" scale="0.4 0.4 0.4" align="center" color="#000000"></a-text>
      </a-entity>
    </a-scene>
 
    <script>
      const ctlL = document.getElementById("ctlL");
      const ctlR = document.getElementById("ctlR");
      const txt = document.getElementById("txt");
      const txt2 = document.getElementById("txt2");
     
      //Getting Position of Right Controller.
      const timer = setInterval(() => {     
        var p=ctlR.object3D.position;
        txt2.setAttribute("value","R-Position: "+ p.x.toFixed(2)+", "+p.y.toFixed(2)+", "+p.z.toFixed(2));
      }, 100);
      
      //Stick Moved
      ctlL.addEventListener('axismove',function(event){
        txt.setAttribute("value", "L Stick  x:"+event.detail.axis[0].toFixed(2)+", y:"+event.detail.axis[1].toFixed(2));
      });
      ctlR.addEventListener('axismove',function(event){
        txt.setAttribute("value", "R Stick  x:"+event.detail.axis[0].toFixed(2)+", y:"+event.detail.axis[1].toFixed(2));
      });
      
      //Trigger Touch Started
      ctlL.addEventListener('triggertouchstart', function (event) {
        txt.setAttribute("value","Left touch started ");
      });
      ctlR.addEventListener('triggertouchstart', function (event) {
        txt.setAttribute("value","Right touch started");
      });
      
      //Trigger Touch Ended
      ctlL.addEventListener('triggertouchend', function (event) {
        txt.setAttribute("value","Left touch ended ");
      });
      ctlR.addEventListener('triggertouchend', function (event) {
        txt.setAttribute("value","Right touch ended");
      });
           
      //Trigger Pressed
      ctlL.addEventListener('triggerdown', function (event) {
        txt.setAttribute("value","Left trigger down");
      });
      
      ctlR.addEventListener('triggerdown', function (event) {
        txt.setAttribute("value","Right trigger down");
      });
      
      //Trigger Released
      ctlL.addEventListener('triggerup', function (event) {
        txt.setAttribute("value","Left trigger up");
      });
      ctlR.addEventListener('triggerup', function (event) {
        txt.setAttribute("value","Right trigger up");
      });
      
      //Grip Pressed
      ctlL.addEventListener('gripdown', function (event) {
        txt.setAttribute("value","Left gripdown down");
      });
      ctlR.addEventListener('gripdown', function (event) {
        txt.setAttribute("value","Right gripdown down");
      });
      
      //Grip Released
      ctlL.addEventListener('gripup', function (event) {
        txt.setAttribute("value","Left gripdown up");
      });
      ctlR.addEventListener('gripup', function (event) {
        txt.setAttribute("value","Right gripdown up");
      });
      
      
      //A-buttorn Pressed 
      ctlR.addEventListener('abuttondown', function (event) {
        txt.setAttribute("value","Right A-button down");
      });
      
      //A-buttorn Released 
      ctlR.addEventListener('abuttonup', function (event) {
        txt.setAttribute("value","Right A-button up");
      });
      
      //B-buttorn Pressed 
      ctlR.addEventListener('bbuttondown', function (event) {
        txt.setAttribute("value","Right B-button down");
      });
            
      //B-buttorn Released 
      ctlR.addEventListener('bbuttonup', function (event) {
        txt.setAttribute("value","Right B-button up");
      });
           
      //Y-buttorn Pressed 
      ctlL.addEventListener('ybuttondown', function (event) {
        txt.setAttribute("value","Left Y-button down");
      });
            
      //Y-buttorn Released 
      ctlL.addEventListener('ybuttonup', function (event) {
        txt.setAttribute("value","Left Y-button up");
      });
      
      //X-buttorn Pressed 
      ctlL.addEventListener('xbuttondown', function (event) {
        txt.setAttribute("value","Left X-button down");
      });
            
      //X-buttorn Released 
      ctlL.addEventListener('xbuttonup', function (event) {
        txt.setAttribute("value","Left X-button up");
      });
    </script>
  </body>
</html>
```
##sample2
コントローラにinput-listenerコンポーネントを追加。

```html
<!DOCTYPE html>
<html>
  <!--https://github.com/TakashiYoshinaga/Oculus-Quest-Input-Sample-->
  <head>
    <meta charset="utf-8">
    <title>Oculus Quest Input</title>
    <meta name="description" content="Oculus Quest Input">
    <script src="https://aframe.io/releases/0.9.2/aframe.min.js"></script>
  </head>
  <body>
    <script>  
      AFRAME.registerComponent('input-listener', {
        //Definition of right or left hand as a controller's property.
        schema: { 
            hand: {　type: "string", default: "" }
        },
        //Initialization
        init:function () {
          this.el.txt = document.getElementById("txt");
          this.el.txt2 = document.getElementById("txt2");
          //Stick Moved
          this.el.addEventListener('axismove',function(event){
            this.txt.setAttribute("value", "Stick  x:"+event.detail.axis[0].toFixed(2)+", y:"+event.detail.axis[1].toFixed(2));
          }); 
          //Trigger Touch Started
          this.el.addEventListener('triggertouchstart', function (event) {
            this.txt.setAttribute("value","Trigger touch started ");
          });
          //Trigger Touch Ended
          this.el.addEventListener('triggertouchend', function (event) {
            this.txt.setAttribute("value","Trigger touch ended");
          });
          //Trigger Pressed
          this.el.addEventListener('triggerdown', function (event) {
            this.txt.setAttribute("value","Trigger down");
          });
          //Trigger Released
          this.el.addEventListener('triggerup', function (event) {
            this.txt.setAttribute("value","Trigger up");
          });
          //Grip Pressed
          this.el.addEventListener('gripdown', function (event) {
            this.txt.setAttribute("value","Gipdown down");
          }); 
          //Grip Released
          this.el.addEventListener('gripup', function (event) {
            this.txt.setAttribute("value","Gripdown up");
          });
          //A-buttorn Pressed 
          this.el.addEventListener('abuttondown', function (event) {
            this.txt.setAttribute("value","A-button down");
          });
          //A-buttorn Released
          this.el.addEventListener('abuttonup', function (event) {
            this.txt.setAttribute("value","A-button up");
          });
          //B-buttorn Pressed
          this.el.addEventListener('bbuttondown', function (event) {
            this.txt.setAttribute("value","B-button down");
          });
          //B-buttorn Released
          this.el.addEventListener('bbuttonup', function (event) {
            this.txt.setAttribute("value","B-button up");
          });
          //Y-buttorn Pressed 
          this.el.addEventListener('ybuttondown', function (event) {
            this.txt.setAttribute("value","Y-button down");
          });
          //Y-buttorn Released
          this.el.addEventListener('ybuttonup', function (event) {
            this.txt.setAttribute("value","Y-button up");
          });
          //X-buttorn Pressed
          this.el.addEventListener('xbuttondown', function (event) {
            this.txt.setAttribute("value","X-button down");
          });
          //X-buttorn Released
          this.el.addEventListener('xbuttonup', function (event) {
            this.txt.setAttribute("value","X-button up");
          });
        },
        //called evry frame
        tick: function () {
          //Position of righ-hand controller is shown in real-time.
          if(this.data.hand=="right"){
            var p=this.el.object3D.position;
            this.el.txt2.setAttribute("value","R-Position: "+ p.x.toFixed(2)+", "+p.y.toFixed(2)+", "+p.z.toFixed(2));
          }
        }
    });
            
</script>
    
    <a-scene background="color: #FAFAFA">
      <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" shadow></a-box>
      <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E" shadow></a-sphere>
      <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D" shadow></a-cylinder>
      <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" shadow></a-plane>
      <a-entity id="ctlL" laser-controls="hand: left" input-listener="hand: left"></a-entity>
      <a-entity id="ctlR" laser-controls="hand: right" input-listener="hand: right"></a-entity>
      <a-entity camera look-controls position="0 1.6 0">
        <a-text id="txt" value="" position="0 0 -1" scale="0.4 0.4 0.4" align="center" color="#000000"></a-text>
        <a-text id="txt2" value="" position="0 -0.15 -1" scale="0.4 0.4 0.4" align="center" color="#000000"></a-text>
      </a-entity>
    </a-scene>   
  </body>
</html>
```
#3. CGとのインタラクション
コントローラを用いてテレポートやマニピュレーション、弾の発射、物理演算の適用などVRに必要そうな要素を盛り込んだサンプルをGitHubやGlitchで公開しています。ご興味があれば是非こちらもご活用ください。

Qiita
https://qiita.com/Tks_Yoshinaga/items/bd7f85a2eada4f6a9113

GitHub
https://github.com/TakashiYoshinaga/Oculus-Quest-Interaction-Sample

体験
https://quest-demo.glitch.me

Videoデモ
[![](https://img.youtube.com/vi/J-AQpvOawJ0/0.jpg)](https://www.youtube.com/watch?v=J-AQpvOawJ0)