Aframe + Vue を使ってOculusGoでWebVRを（なるべく簡単に）はじめる

## はじめに ##
[Oculus Rift Advent Calendar 2018](https://qiita.com/advent-calendar/2018/oculus-rift)の14日目の記事です。
OculusといえばUnityやUE4による開発がメジャーですが、中にはブラウザ上でVRをやってみたい！という~~変態~~人たちも結構います。
しかし、いざ [Aframe](https://aframe.io/) や [React360(元ReactVR)](https://facebook.github.io/react-360/docs/setup.html) のページを見ても「よくわからん・・・」という人が多く、断念している人が多いと思います。

## Aframeって何？ ##
ざっくり説明すると、**ブラウザ上でHMDやCardboard対応の3Dレンダリングを簡単に行えるフレームワーク(内部的には[three.js](https://threejs.org/)のVR向けラッパーライブラリ群)です。**　[公式サイトのデモ](https://aframe.io/examples/showcase/helloworld/)でブラウザ上で3Dモデルが表示されていると思います。
<img width="1126" src="https://qiita-image-store.s3.amazonaws.com/0/58376/21f31bfc-4eae-be41-b2ba-b6b3089cdbc8.png">
右下のアイコンをクリックすると
<img width="1552" src="https://qiita-image-store.s3.amazonaws.com/0/58376/893327ad-99d0-569e-aca3-b0c9b47e740b.png">
といったVRおなじみの画面が出てくると思います。

## Aframeを実際に使用するまでがわかりにくい ##
公式サイトの[インストール項目](https://aframe.io/docs/0.8.0/introduction/installation.html)を見ると、英語慣れしていないと何をすればよいのかわかりにくいです。
ここの項目は**開発スタイルごとのHowTo的な話**が盛り込んであり、Aframeを使うという意味では下の2項目になります。

### Include the JS Build ###
HTMLからビルド済みファイルを使用する方法です。scriptタグを使ってCDNからファイルをロードします。

```html
<head>
  <script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script>
</head>
```

### Install from npm ###
パッケージ管理ソフトのnpmからAframeを利用できるようにします。**単純にモジュールをローカルに持ってくるだけ**で実際に使用する時はnodeモジュール利用時の作法を守っていく必要があります。(本来 `npm init` を行ったり、コマンドも `npm install aframe --save` としてモジュールをdependenciesに追加する必要があるのですが、説明がありません)
さらに項目の続きに

* If you use npm, you can use angle, a command line interface for A-Frame. angle can initialize a scene template with a single command:

とあり、**angle自体は簡易的なジェネレータ**で、テンプレートのpackage.jsonに記述されている構成が古い(webpackが2.7で止まってたり、babelの設定が含まれていない、など)ため、これを使ってAframeの開発することはおすすめしません。

## Vue CLIを使ってみる ##
[Vue CLI](https://cli.vuejs.org/)はVueのためのCLIですが、ジェネレータが優秀で、Webpack導入からBabelの設定まで設定してくれるため、Aframeの開発にも利用すると、開発が非常に楽になります。
とりあえず、Vue CLIのインストールは省略し、ジェネレータから作成する場合に、どのようにAframeを含めていくかやっていきます。

### ジェネレータを使用してプロジェクト作成する ###
コンソールから以下のコマンドを打ちます。[任意のプロジェクト名]の箇所は自分の好きな名前をつけてください

```bash
vue create 任意のプロジェクト名
```

するとCLIの選択画面が出てくるので、下にカーソルをあわせてエンターキーを押します

```bash
Vue CLI v3.1.3
┌───────────────────────────┐
│  Update available: 3.2.1  │
└───────────────────────────┘
? Please pick a preset: (Use arrow keys)
   default (babel, eslint) 
❯  Manually select features 
```

とりあえずは`Babel` と `Linter / Formatter` にチェックがはいっていればOKです。エンターキーを押します

```bash
? Check the features needed for your project: 
❯◉ Babel
 ◯ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◯ Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing
```

`ESLint + Standard config `にカーソルを合わせてエンターキーを押します

```bash
? Pick a linter / formatter config: 
  ESLint with error prevention only 
  ESLint + Airbnb config 
❯ ESLint + Standard config 
  ESLint + Prettier 
```

`Lint on save` にチェックが入っていればOKです。エンターキーを押します

```bash
? Pick additional lint features: (Press <space> to select, <a> to toggle all,
 <i> to invert selection)
❯◉ Lint on save
 ◯ Lint and fix on commit
```

どちらでも良いのですが、個人的に `In dedicated config files`　のほうが管理しやすいです。エンターキーを押します

```bash
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? (Use a
rrow keys)
❯ In dedicated config files 
  In package.json 
```

これまでのプリセットを保存して別のプロジェクトで使い回すか？という内容が表示されます。 `N` をタイプしてエンターキーを押します

```bash
? Save this as a preset for future projects? (y/N) 
```

インストールが終了するまで待ちます。終わったらエディタでプロジェクトが入ったフォルダを開きます。以下の例ではvscodeを利用しています。

```bash
code 任意のプロジェクト名
```

### Aframeを追加する ###

`package.json` があるプロジェクトのルートディレクトリに移動し、以下のコマンドを利用します。　パッケージ管理ソフトは[yarn](https://yarnpkg.com/ja/)をおすすめします。

```bash
yarn add aframe
```

## HelloWorldのコードを書いていく ##

### (任意).eslintrc.js書き換え ###

Linterに手を加えます。好みの問題になるのですが、自分は普段このような設定を使っています。個々の設定の意味は[ESLintのルール](https://eslint.org/docs/rules/)を見ると良いでしょう

```js
module.exports = {
  root: true,
  env: {
    node: true,
  },
  'extends': [
    'plugin:vue/essential',
    '@vue/standard',
  ],
  rules: {
    'no-extra-semi': 'warn',
    'no-undef': 'warn',
    'quotes': ['error', 'single'],
    'space-before-function-paren': ['error', 'never'],
    'arrow-parens': 0,
    'generator-star-spacing': 'off',
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
  },
  parserOptions: {
    parser: 'babel-eslint',
  }
```

### main.js書き換え ###

エントリーポイントになっているmain.jsを書き換えます。ファイルはsrcフォルダ内に入っています。
`import 'aframe'`を追加します。

```js
import Vue from 'vue'
import App from './App.vue'

import 'aframe'

Vue.config.productionTip = false

new Vue({
  store,
  render: h => h(App)
}).$mount('#app')
```

### App.vue書き換え ###

App.vueはルートVueインスタンスになるもので、ここに子になるコンポーネントが追加されていきます。同じくsrcフォルダ内にあります。
.vueファイルはテンプレートとスクリプトに別れており、template(html)の記述とjsのスクリプトを一つにまとめて書くことができます。
以下のように書き換えます。

```vue

<template>
  <div id="app">
    <aframe-window></aframe-window>
  </div>
</template>

<script>
import AframeWindow from './components/AframeWindow'

export default {
  name: 'app',
  components: {
    AframeWindow
  }
}
</script>
```

### HelloWorld.vue削除、AframeWindow.vueを新規作成 ###

プロジェクトのルートディレクトリ以下から、 `/src/components/HelloWorld.vue` を削除、同じディレクトリに`AframeWindow.vue` を追加し、以下のよう内容にします

```vue

<template>
  <div id="aframe-window">
    <a-scene background="color: #ECECEC" embedded>
      <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-scene>
  </div>
</template>

<style>
@media only screen and (min-width: 640px) {
  #aframe-window {
    width: 640px;
    height: 320px;
    margin-left: auto;
    margin-right: auto;
  }
}

@media only screen and (max-width: 640px) {
  #aframe-window {
    width: 320px;
    height: 320px;
    margin-left: auto;
    margin-right: auto;
  }
}
</style>

<script>
export default {
  name: 'AframeWindow'
}
</script>
```

これだけでAframeで開発を行う準備ができました。ここからVueやAframeの世界がまっています！

## ローカルサーバーを起動する ##

上記ファイルを保存後、以下のコマンドを実行します

```bash
yarn run serve
```

起動に成功すれば以下のようなメッセージが出ます。
PCとOculusGoが同じルーターに繋がっている場合は `Network` の箇所のアドレスをOculus Goのブラウザで開くことができます。
スクリプトのファイルを保存するたび、自動ビルド＋ブラウザリロードがかかるので、すばやく開発をしていくことができます。

```
 DONE  Compiled successfully in 1179ms

  App running at:
  - Local:   http://localhost:8080/
  - Network: http://192.168.***.***:8080/
```

## Aframeの開発にVueを使うメリットは？ ##
今回は時間がなくてあまり説明できませんでしたが、Aframeの開発にVueを用いる理由として、

* AframeはDOM操作を意識しないとオブジェクト間の連携ができない。が、Aframe自体はDOM操作が得意ではない
* VueはDOM操作を意識しなくてもDOM構造をいじることができるので、よりプログラマブルに記述できる
* AframeとVueどちらもコンポーネント指向のため、書いたコードがどのように動くのか把握しやすい
* Vue CLIがめんどくさい設定を殆どやってくれるので、初期設定で悩むことが少ない

など、お互いのメリットを生かして利用できるため、それぞれ別の目的のフレームワークでありながら、共存させることができ、とても強力な組み合わせになっています。
また、AframeもVueもドキュメントが豊富で、リファレンスに困ることが少ないというメリットもあります。

Vueについては[こちら](https://jp.vuejs.org/v2/guide/index.html#Vue-js-%E3%81%A8%E3%81%AF%EF%BC%9F)を参照するとわかりやすいかもしれません。