THETAとUnityでVR英語教材を作ってみる

# きっかけ

どうも、やのせん[@yanosen_jp](https://twitter.com/yanosen_jp)です。私は職場(大学）の社会貢献活動の一環としてデジタル教育の普及活動をしており、小学生向けに算数や英語の学習アプリを作って、イベントで公開してきました。

![umekita.jpeg](https://qiita-image-store.s3.amazonaws.com/0/58503/304cf197-73af-3c09-60df-349fbd53c3e1.jpeg)

写真のように、いままではiPad用アプリを作っていたのですが、スマホが普及してきてインパクトが薄れてきたので、最近はハコスコを使ったVR英語学習アプリも作ってます。

![141232.jpg](https://qiita-image-store.s3.amazonaws.com/0/58503/ea2ddd4d-6655-2e33-87c4-b4e83b3ca21d.jpeg)

VRで英語の教材を作るなら、なるべくリアルな「そこにいる」の雰囲気のあるシーンを作りたいのですが、私と学生さんの技術力ではなかなかむずかしく、、。そこでTHETA Sで撮った全天球画像をベースにして英語学習アプリを試作してみることにしました。動画でもよいのですが、画質や容量の問題と作りにくさ（Unityだと、動画をEditor上でプレビューできない）などから、静止画を使っています。紹介する方法のほとんどは他記事で既出のものですが、まとめとしてご覧いただければ幸いです。

## つくるもの

VRjamで賞を取ったMaxim Miheyenko氏の["House of Language"](http://devpost.com/software/house-of-languages)の簡易バージョン？をつくります。House of Languageでは、シーン内の色々なものを見ると、その英語の名前と音が出てきます。

![HouseOfLanguage.PNG](https://qiita-image-store.s3.amazonaws.com/0/58503/5555caef-5b17-4602-3e0e-867b8e08b51b.png)

そこの部分を少し変えた形で、以下のようなアプリをTHETA画像とUnityで作ります。

* THETAで撮影した全天球画像の中心に自分がいて、HMDを動かすと周りが見れる。
* シーン内には視るべき対象物があり、そこに視線を合わせればその対象物の英単語が表示されてクリア。
* ヒントとして、その対象物の単語の発音が、３Dオーディオとして対象物から聞こえてくる。音で場所を推測しながら、対象物を探す。

完成すると、[こちらの動画](https://youtu.be/kZLgAGumrgU)のようになります。ヘッドホンと一緒にご覧ください。


## 開発環境

開発環境はUnityです。今回は5.3.0f4を使いました。
VRカメラ用にGoogle Cardboard SDK for Unity 0.5.2、3Dオーディオ用にOculus Audio SDK 1.0.1を使っています。

## 素材の準備

* THETAでとったEquirectangular型の画像

こういう感じのですね。

![R0010023.JPG](https://qiita-image-store.s3.amazonaws.com/0/58503/2d7ea2bd-ce8e-c666-32f2-8dab43584a4f.jpeg)

* 画像を張るための天球モデル。

Unityの伊藤さん@warapuriが配布していらっしゃる[Sphere100.fbx](https://dl.dropboxusercontent.com/u/5911974/Sphere100.fbx)を使ってます。

## とりあえず、Unity Editor内で全天球画像を視る

Unityで新しいプロジェクトを作り、Assetフォルダ内にThetaからの画像とSphere100.fbxを入れます。Sphere100.fbxのマテリアルをUnlit/Textureに変更しましょう。すると、テクスチャを選べるようになるので、そちらへTHETAからの画像をDrag&Dropします。

<img width="420" alt="texture.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/58503/58f8e1d3-96b6-9c9d-2944-9db8a627167f.png">

Sphere100をシーンヒエラルキーにD&Dすると、Theta画像が張られた天球が作られます。Sphere100のTransformを(0,0,0)、Scaleはおおきめに(100,100,100)に、Main Cameraの座標も(0,0,0)にして、カメラを天球内部に置きましょう。カメラはProjectionをPerspectiveにすると、gameウィンドウで天球内部がみえるはずです。

<img width="411" alt="Game.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/58503/1bf79cd2-0458-5686-8974-f76db510a96d.png">

しかし、sceneウィンドウでは、天球内部ではなく天球外からの視点になっているかもしれません。その場合は、Sphere100をヒエラルキーで選択して、**GameObject/AlignViewToSelected**を使うと、Sphere100のど真ん中にsceneのビューが移動します。この状態で**右クリックしながらカーソルを動かす**と、Sphere100の真ん中を中心にビューが回転します。これを使って視点を回転させつつ、ゲームオブジェクトを配置しましょう。

ちなみに、現在の状態だと画像は左右逆に表示されています。私はこれで特に問題なかったので放置していますが、困る場合はxのscaleを負の値、例えば(-100,100,100)にしてください。左右が反転します。

## Google Cardboard SDK for UnityのVRカメラを設置する

Unity用のVRカメラアセットは幾つかありますが、現状ではGoogle Cardboard SDK for Unityが一番使いやすいと思います。SDKのUnityPackageは[こちら](https://developers.google.com/cardboard/unity/download?hl=ja)からダウンロードしてください（執筆時0.5.2）。ダウンロードして、パッケージをインポートしましょう。

つぎに、Cardboard/PrefabsのCardboardMainというプレハブをヒエラルキーにD&Dしましょう。座標が(0,0,0)である事を確認してください。これでシーンを再生すれば、以下のような画面が出るはずです。

<img width="415" alt="cardboard.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/58503/c00a76f7-8f35-7c1c-9ea5-d9e6ee34eb63.png">

## Editor上で、Cardboardのカメラをスマホから制御する。

この状態でも、CardboardMainのrotationを変えれば視点を動かせますが、面倒臭いですね。そこで、スマホ(iPhone/Android)用のテストアプリUnity Remote 4で制御することにします。

まず、Edit/Project settings/EditorでUnity RemoteのDeviceをiPhoneにしておきます。次に、[こちら](http://answers.unity3d.com/questions/985989/google-cardboard-not-move-the-camera-when-im-using.html)の方法に従い、GyroscopeEnable.csをつくり、適当なゲームオブジェクトにアタッチします。そして、Cardboard/Scripts/VRDevices/UnityEditorDevice.csで、74行目の    
```if (Cardboard.SDK.UseUnityRemoteInput && RemoteCommunicating) {```
を
```if (RemoteCommunicating) {```
に変更します。

すると、Unity Remote 4を起動しているスマホで、ジャイロによる視点の移動が可能になります（ネット上にはほかの方法も紹介されていますが、なぜか私の環境では動作しませんでした）。

## 視る対象物を設定する。

このアプリでは、何か対象物を見て、名前の英単語を表示させたいので、その対象物を設定します。

対象物は、THETA画像に映り込んでいるものを使います。ここでは、画面中央の、テーブルの上のピザをつかいます。


<img width="402" alt="pizza.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/58503/be3c794c-1aab-24c1-a161-87af27f48154.png">

こちらにコライダーを設定して、後で説明するVRカーソルを向けたときに反応するようにします。コライダーは、本当はピザの形に合わせるのが良いですが、めんどうくさいのでCapsuleのコライダをつかいます。Sphere100の子オブジェクトとしてCapsuleゲームオブジェクトを作って、シーン上で、ピザに重ねるように置いてください。

<img width="401" alt="pizza2.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/58503/713b01d8-5518-fe06-fcdc-ef9579ce7f0f.png">

ここで、ゲームオブジェクトとカメラ（原点）の距離が、実際の距離（この場合、Thetaカメラとビザの距離）とだいたい同じになるようにしてください（Unityでは1unit = 1m）。あとで、このオブジェクトに音源を置くときに効いてきます。距離をとろうと思うとSphere100の壁にめり込んでしまう場合は、Sphere100のスケールを大きくしてください。良い感じにおけたら、Mesh Renderコンポーネントのチェックをはずせば、Capsuleのコライダだけ残ります。

視線を合わせたときに表示される英単語の表示も作りましょう。3DテキストオブジェクトTextをSphere100の子オブジェクトとして作ります。これは、大きめにした方が見やすくてよいですね。

<img width="397" alt="pizzaWord.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/58503/557f2aa6-8f8f-4ace-e498-81d700532769.png">

##　対象物から音を出す。

ここでは、対象物に３Dオーディオ音源を設定し、音の聞こえ方から対象物の位置がわかるようにします。そこで、OculusのAudio SDKを用いて３Dオーディオを実装します。Audio SDKの詳しいことは、[私のOculus Advent Calendarの記事](http://qiita.com/yanosen_jp/items/03de6ad02a05493f343b)をご覧ください。

まず、OculusのAudio SDKを[こちらから](https://developer.oculus.com/downloads/audio/1.0.1/Oculus_Audio_SDK_Plugins/)ダウンロード（私は1.0.1を使用）し、audiosdk_1.0.1\AudioSDK\Plugins\UnityのOculusNativeSpatializer.unitypackageをインポートします。つぎに、Editor/Project Settings/AudioでSpatializer PluginをOculusSpatializerにし、DSP Buffer SizeをBest Latencyにします。

つぎに、CapsuleオブジェクトにONSP Audio SourceコンポーネントとAudio Sourceコンポーネントをアタッチします。
ONSP Audio Sourceは、Inverse Square Attenuationをオンにし、Falloff rangeは、Farの値が対象物の位置までの距離より大きくなるようにしてください。

AudioSourceに用いるAudioClipは、対象物の英単語（このばあいPizza）の音を入れます。[こちらの](http://www.fromtexttospeech.com/)無料のTextToSpeechサービスで"Pizza"の発音のmp3を作成、ダウンロードしてください。Audio SDK用の音源はmonoである必要がありますが、この音源はもともとmonoなので、特別な処理は不要です。そのままmp3をAssetフォルダにD&Dしてください。それをAudioSourceのAudioClipとして設定します。さらに、OutputのAudioMixerをSpatializerMixer/Masterにします。最後に、Play On Awake、Loopはオンにしておきます。

<img width="375" alt="ONSP.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/58503/0324743b-c5a3-001a-1ed3-4ecc4552af1a.png">

次に、AudioMixerの設定をします。Assets/OSPNative/scenes/mixersのSpatializerMixerの中に"Master"があるので、そちらの"OculusSpatializerReflection"の設定を変えます。Room Dimensionsを部屋のサイズに合わせて設定し、反響の強い部屋ならReverb onもチェックしてください。

<img width="509" alt="Mixer.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/58503/4c410a81-ad94-b803-d1b9-26e53500ea75.png">

ここまで設定したら、シーンを実行して動作を確認してください。Unity Remote4に接続した状態でスマホを動かせば、音がカメラ方向によって変化することがわかるはずです。

## 視線マーカーを設定する

対象物に視線が合ったことを判定し、マーカーを表示するようにします。

まず、マーカーとしてつかうスプライトを設定します。私は、下のようなものをPowerPointで作り、PNGで保存したものを[こちら](http://www.peko-step.com/tool/alphachannel.html)で透過させました。

<img width="126" alt="marker - コピー.png" src="https://qiita-image-store.s3.amazonaws.com/0/58503/9fe98221-c3f0-ca0d-8b91-2894f1e3c991.png">

それを、AssetsフォルダにD&Dしてスプライトとしてとりこみます。名前はmarkerにします。
取り込んだスプライトはCardboardMain/Head/MainCameraの下におきます。位置は(0,0,0.3)くらいにします。ここで**Scaleではなく**、**Import SettingのPixels Per Unit**を調節し、(500px*500pxのスプライトで5000くらい）サイズを変えてください。

<img width="734" alt="SpriteSetting.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/58503/1e8f721a-b500-85c1-ba8a-29a2544218ac.png">

次に、ヒエラルキー上にUI/Event Systemゲームオブジェクトを作ります。Standalone Input Moduleは要らないので消し、代わりにCardboard/Scripts/GazeInputModule.csをアタッチしてください。さらに、Cursorのところに先ほど作ったmarkerオブジェクトをD&Dし、Show Cursor, Show Cursor Sizeをチェックしてください。

最後にCardboardMain/Head/MainCameraにPhysics Raycasterコンポーネントをつけてください。

これで、コライダーのあるものを見ると、markerが表示されて、イベントが起こるようになります。

## コライダーとイベントの設定。

見る方のマーカーはできたので、次は見られる方の設定です。

先に、以下のQuestion.csというコードを追加してください。

```csharp:Question.cs
using UnityEngine;
using System.Collections;
public class Question : MonoBehaviour
{
    GameObject wordText;
    void Start()
    {
        wordText = GameObject.Find("Text");
        wordText.SetActive(false);
    }
    public void OnPointerEnter()
    {
        Invoke("ShowAnswer", 1);
    }
    public void OnPointerExit()
    {
        CancelInvoke("ShowAnswer");
    }
    void ShowAnswer()
    {
        wordText.SetActive(true);
    }
}
```

ここでは、マーカーがコライダーに入ったらOnPointEnter()が呼び出され、一秒後にShowAnswer()でTextを表示するようにします。

先ほどコライダーを設定したCapsuleオブジェクトにこのスクリプトをアタッチし、さらにEvent Triggerコンポーネントを加えます。Add New Event TypeでPointer Enter, Pointer Exitイベントを追加します。Pointer Enterのほうは、ObjectとしてCapsuleを選んで、Functionとして、その中のQuestion/OnPointEnterを選択します。Pointer Exitのほうは、同じくObjectとしてCapsuleを選んで、Functionとして、その中のQuestion/OnPointExitを選択します。

<img width="361" alt="EventTrigger.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/58503/674c475d-2021-f675-b993-423ebafdccce.png">


これで、markerとコライダが画面上であたるとPizzaが表示されます。

## アプリ化する

UnityでのAndroid、iOS向けのビルドは、ほかの文献を参考にしてください。一応、Galaxy S6 Edgeで動作確認済みです。


# おわりに

今回はUnityでアプリを作りましたが、次はMozillaのA-Frameで作ってみたいですね。スマホブラウザで簡単に使えるようなので、アプリの配布が簡単になりそうです。

次回は最終回、GOROmanさんの「THETAの未来に期待すること」です。おたのしみに！