React VR CLI で init コマンドを実行してみた

いよいよ終盤 [Classi Advent Calendar 2017](https://qiita.com/advent-calendar/2017/classi) 24 日目の記事です。
さて、今回は React VR について軽く見てみたので、そのお話です。

## [React VR](https://facebook.github.io/react-vr/index.html)
* JavaScript で VR アプリケーションを作ることができるライブラリ
* Oculus と Facebook で開発している
* React と同様にコンポーネントベースであるため React 経験者は触りやすい印象
* React VR CLI というツールが提供されており、コマンド一つでプロジェクトを作成することができる

## React VR CLI を使ったプロジェクト作成
* React VR CLI という CLI ツールが用意されているので、それを使えば簡単に作れる
    * CLI ツールを yarn または npm でインストールし、インストールされたら init コマンドを実行

```
$ yarn global add react-vr-cli
$ react-vr # ← バージョンの確認ができる
$ react-vr init WelcomeToVR
```
* yarn start で起動するので http://localhost:8081/vr/ にアクセスするとサンプルが確認できる

```
$ cd WelcomeToVR
$ yarn start
```

## React VR を構成するもの
* 作成したプロジェクトの package.json を見ると何が使われているかわかる
    * WelcomeToVR/package.json
* React 関連を除くと OVRUI と three.js
* three.js
    * JS 用の 3D ライブラリ
* OVRUI
    * Oculus VR UI

## ソースコードの見方
* entry point は WelcomToVR/vr/index.html

```html:index.html
<html>
  <head>
    <title>WelcomeToVR</title>
    <style>body { margin: 0; }</style>
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
  </head>
  <body>
    <!-- When you're ready to deploy your app, update this line to point to your compiled client.bundle.js -->
    <script src="./client.bundle?platform=vr"></script>
    <script>
      // Initialize the React VR application
      ReactVR.init(
        // When you're ready to deploy your app, update this line to point to
        // your compiled index.bundle.js
        '../index.vr.bundle?platform=vr&dev=true',
        // Attach it to the body tag
        document.body
      );
    </script>
  </body>
</html>
```

* 9 行目を見ると client.js(vr/client.js) を読み込んでいることがわかる

```javascript:client.js
// Auto-generated content.
// This file contains the boilerplate to set up your React app.
// If you want to modify your application, start in "index.vr.js"

// Auto-generated content.
import {VRInstance} from 'react-vr-web';

function init(bundle, parent, options) {
  const vr = new VRInstance(bundle, 'WelcomeToVR', parent, {
    // Add custom options here
    ...options,
  });
  vr.render = function() {
    // Any custom behavior you want to perform on each frame goes here
  };
  // Begin the animation loop
  vr.start();
  return vr;
}

window.ReactVR = {init};
```

* client.js を見ると JS の window オブジェクトに `ReactVR` を追加しているので、これで `ReactVR.init()` が使える
* 再度 index.html を見ると `ReactVR.init()` を使っており、その中で index.vr.js を読み込んでいる
* main の処理はこの index.vr.js が担っている

```javascript:index.vr.js
import React from 'react';
import {
  AppRegistry,
  asset,
  Pano,
  Text,
  View,
} from 'react-vr';

export default class WelcomeToVR extends React.Component {
  render() {
    return (
      <View>
        <Pano source={asset('chess-world.jpg')}/>
        <Text
          style={{
            backgroundColor: '#777879',
            fontSize: 0.8,
            fontWeight: '400',
            layoutOrigin: [0.5, 0.5],
            paddingLeft: 0.2,
            paddingRight: 0.2,
            textAlign: 'center',
            textAlignVertical: 'center',
            transform: [{translate: [0, 0, -3]}],
          }}>
          hello
        </Text>
      </View>
    );
  }
};
```
* これを見ると一番外側に View Component が存在し、その中で Pano Component と Text Component が呼ばれている

**View Component**

* UI を作るための最も基本的なコンポーネント
* View Component は他のコンポーネントを内包するように設計されている
    * 今回は Pano と Text を持っている

**Pano Component**

* 指定された画像を球面に表示するコンポーネント(指定の仕方は下記の 2 パターン)
    * 360 度カメラで撮影したような画像を 1 枚配置
    * 6 枚の正方形の画像を right, left, top, bottom, back, front の順に配置

**Text Component**

* テキストを表示するコンポーネント
    * 配置場所や色、サイズなどが指定できる

## Dev tools
* プロジェクトには react-devtools も付属している
* (クエリパラメータに devtools をつけた) http://localhost:8081/vr/?devtools をブラウズした状態で下記コマンドを実行すると React Developer tools が立ち上がる

```
$ yarn devtools
```
<img width="798" alt="devtools.png" src="https://qiita-image-store.s3.amazonaws.com/0/28573/d30b994c-a9b0-9c8e-b488-eb087c58d787.png">


* クエリパラメータをつけていないと下記メッセージが出たまま動かない

<img width="799" alt="devtools_disconnect.png" src="https://qiita-image-store.s3.amazonaws.com/0/28573/71b517ae-632f-b822-caca-7a912b552b8f.png">


## テスト
* 下記コマンドで Jest 経由のテストが走る

```
$ yarn test
```

* 初めから下記のようなテストファイル(index-test.js)がある
    * テスト対象は index.vr.js

```javascript:__test__/index-test.js
import 'react-native';
import 'react-vr';
import React from 'react';
import Index from '../index.vr.js';

// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';

it('renders correctly', () => {
  const tree = renderer.create(
    <Index />
  );
});
```

* 例えば WelcomeToVR の一番外側は View Component なのでテストに下記を追記すれば、追加のテストも実行できる

```diff
diff --git a/__tests__/index-test.js b/__tests__/index-test.js
index 8ffc333..27de2b0 100644
--- a/__tests__/index-test.js
+++ b/__tests__/index-test.js
@@ -11,3 +11,12 @@ it('renders correctly', () => {
     <Index />
   );
 });
+
+it('外側のコンポーネントは View', () => {
+  const tree = renderer.create(
+    <Index />
+  );
+
+  const instance = tree.toJSON();
+  expect(instance.type).toBe('View');
+});
```

## まとめ
VR と聞くとすごい小難しそうなことをやる必要があると思ってましたが、React VR を使えば立ち上げはすぐにできそうで、Dev tools やテストもついているので導入の敷居は低そうだなと思いました。

今回はほとんどソースコードをいじっていないので、もうすこし触ったら楽しそうですね。

明日は弊社 CTO [からあげエンジニア](https://qiita.com/sasata299)の登場です！お見逃しなく！


# 参考文献
* https://github.com/facebook/react-vr
* https://facebook.github.io/react-vr/docs/dev-tools.html
* https://reactjs.org/docs/test-renderer.html