Oculus Questで部屋内を動くアプリ作成してみた

#はじめに
大学を卒業したばかりの社会人です。
卒業研究でアプリを作ったので何かのお役に立てれば幸いです。
メモ書き感覚なので理解しづらい部分もありますが大目に見てください。
コントローラでの移動や部屋への画面遷移などVRのアプリを作成したい方の手助けができたらなと思います。
初期設定とビルド方法は他の記事を参考にしているのでそちらをご覧ください。（かなり重要です。）
Qiitaに投稿するの初めてなんで優しくしてください。。


##環境
・Unity 2018.2.5f1
・Oculus Quest
・Windows 10 Home 

##作成したアプリの概要
start画面からコントローラのポインタを表示し部屋の雰囲気を選択する画面に遷移、選択した雰囲気の部屋を表示しコントローラでの移動を可能とする。
##参考にした記事など
初期設定について
[UnityでOculusQuestの開発環境構築](https://qiita.com/Satoshi_Takahama/items/0b3e13d8976b7f57e4a8)

ビルド方法について
[Oculus Quest に Unity で開発したアプリをいれる方法（Mac/Win）](https://qiita.com/Tomoyuki_Mikami/items/be9b3bf605a5d953be1b)

機能の追加方法や導入方法
追加方法
※youtubeでPart1~5までありコントローラの表示や動かし方を参考にしました。
英語ですが、Unityでやっていることを見られます。
[How to make a VR game in Unity - Part 1 - Setup, Hand presence, Grabbing object](https://www.youtube.com/watch?v=sKQOlqNe_WY)
他にも
[Oculus QuestでuGUIを操作する](https://qiita.com/nkjzm/items/8a62cfab348eacff9167)


##作成目次
1.初期設定
2.画面作成
3.カメラ設定
4.コントローラ設定
5.入力ボタンと画面遷移
6.部屋作成



###1.初期設定
Unityで作成したアプリをOculus Questで操作するための初期設定を行います。
上に参考にした記事などがあるので多く省きますが、以下の過程で必要な箇所を説明します。
・Oculus Integrationのインポート
　これはUnityでOculus Questの機能の導入します。
　中にあるOVRPlayerControllerでコントローラの設定など行います。
・UIhelpersの追加
 コントローラからレーザを表示し、入力を反映するのに利用します。
　他の記事を参考に作成してください。
###2.画面作成
初期画面を作成します。
ここでは2次元での選択画面を作成します。
Sceneを作成し任意のタイトルを付けます。
ゲームオブジェクトからUIのキャンバスを選択し、表示させます。
レンダーモードをワールド空間に変更し、イベントカメラをOVRPlayerController‣OVRCameraRig‣CenterEyeAnchorに指定
![スクリーンショット (250).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/0e917cdd-f402-b78a-c8b7-4075379782d9.png)
これにより2次元での画面を作成し表示させることができる。
テキストやボタンをcanvas内を指定し作る事によりその2次元平面で表示できます。


###3.カメラ設定
このアプリはOculus questに作成した画面を表示させます。
初期設定ではMain Cameraがあるためそれを削除します。
OVRPlayerController‣OVRCameraRigがカメラに設定されます。
Scene内で座標や角度を変えることができ表示させる画面に合わせて変更してください。

###4.コントローラ設定
ここでは移動方法やポインタ（レーザー）の表示について解説します。
OVRPlayerControllerでは操作している人の動きに合わせて角度が変わり、初付属するスクリプトを設定すれば地面が定められた場所でスティック移動が可能になります。
![スクリーンショット (268).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/ecc8f338-756c-1eb6-ccb1-d3092a574da7.png)

ポインタを表示方法は上で紹介している動画を参考にしてください。（そっちの方が分かりやすい、、、文章力無くて書くの難しい）


###5.入力ボタンと画面遷移
ここでの入力ボタンはcanvas内に二次元のボタンを作成します。
![スクリーンショット (253).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/00f45b72-af03-8394-f12b-93e58dd602c7.png)

ゲームオブジェクト‣UI‣ボタンを選択し配置したい位置に置きます。
ボタンをポインタ上でクリックした際の動作を付けるにはスクリプトをアタッチします。
ボタンをクリックし別のシーンに移行するスクリプトはこのようになります。
クラス名や読み込むシーンは指定してください。
ここではボタンをクリックするとInputScreenのシーンを読み込む（遷移する）ようにしています。

```C#

using UnityEngine;
using UnityEngine.SceneManagement;

public class GameStart : MonoBehaviour
{
    public void OnStartButtonClicked()
    {
        SceneManager.LoadScene("InputScreen");
    }
}
```

InputScreenでは部屋の印象を選択し、その印象内のイメージを選択して部屋のシーンに遷移します。
印象とイメージはカラーイメージスケールから選定しており、同じ印象では部屋の作りは同一だが、イメージによって色が変わるようになっています。
![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/f64a23cb-f0a2-8c97-c256-ea6e64bdf16d.png)
![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/b10afebb-05f9-f1b4-fc4b-0cdf5d3cb1cf.png)

印象選択画面のスクリプトは

```C#
using UnityEngine;

public class UIManager : MonoBehaviour
{
    //17つのPanelを格納する変数
    //インスペクターウィンドウからゲームオブジェクトを設定する
    [SerializeField] GameObject Select1Panel; //main
    [SerializeField] GameObject Select2Panel; //Casual
    [SerializeField] GameObject Select3Panel; //Natural
    [SerializeField] GameObject Select4Panel; //Dynamic
    [SerializeField] GameObject Select5Panel; //Elegant
    [SerializeField] GameObject Select6Panel; //Chic
    [SerializeField] GameObject Select7Panel; //Pretty
    [SerializeField] GameObject Select8Panel; //Romantic
    [SerializeField] GameObject Select9Panel; //GorGeous
    [SerializeField] GameObject Select10Panel; //Wild
    [SerializeField] GameObject Select11Panel; //Clasic
    [SerializeField] GameObject Select12Panel; //Clear
    [SerializeField] GameObject Select13Panel; //Cool・Casual
    [SerializeField] GameObject Select14Panel; //Modern
    [SerializeField] GameObject Select15Panel; //Dandy
    [SerializeField] GameObject Select16Panel; //Clasic&Dandy
    [SerializeField] GameObject Select17Panel; //Formal

    // Start is called before the first frame update
    void Start()
    {
        //BackToMenuメソッドを呼び出す
        BackToMenu();
    }


    //Select1PanelでButtonaが押されたときの処理
    //Select2Panelをアクティブにする
    //Casual
    public void SelectCasualButton()
    {
        Select1Panel.SetActive(false);
        Select2Panel.SetActive(true);
    }

    //Natural
    public void SelectNaturalButton()
    {
        Select1Panel.SetActive(false);
        Select3Panel.SetActive(true);
    }

    //Dynamic
    public void SelectDynamicButton()
    {
        Select1Panel.SetActive(false);
        Select4Panel.SetActive(true);
    }

    //Elegant
    public void SelectElegantButton()
    {
        Select1Panel.SetActive(false);
        Select5Panel.SetActive(true);
    }

    //Chic
    public void SelectChicButton()
    {
        Select1Panel.SetActive(false);
        Select6Panel.SetActive(true);
    }

    //Pretty
    public void SelectPrettyButton()
    {
        Select1Panel.SetActive(false);
        Select7Panel.SetActive(true);
    }

    //Romantic
    public void SelectRomanticButton()
    {
        Select1Panel.SetActive(false);
        Select8Panel.SetActive(true);
    }

    //GorGeous
    public void SelectGorGeousButton()
    {
        Select1Panel.SetActive(false);
        Select9Panel.SetActive(true);
    }

    //Wild
    public void SelectWildButton()
    {
        Select1Panel.SetActive(false);
        Select10Panel.SetActive(true);
    }

    //Clasic
    public void SelectClasicButton()
    {
        Select1Panel.SetActive(false);
        Select11Panel.SetActive(true);
    }

    //Clear
    public void SelectClearButton()
    {
        Select1Panel.SetActive(false);
        Select12Panel.SetActive(true);
    }

    //Cool
    public void SelectCoolButton()
    {
        Select1Panel.SetActive(false);
        Select13Panel.SetActive(true);
    }

    //Modern
    public void SelectModernButton()
    {
        Select1Panel.SetActive(false);
        Select14Panel.SetActive(true);
    }

    //Dandy
    public void SelectDandyButton()
    {
        Select1Panel.SetActive(false);
        Select15Panel.SetActive(true);
    }

    //Clasic&Dandy
    public void SelectClasicDandyButton()
    {
        Select1Panel.SetActive(false);
        Select16Panel.SetActive(true);
    }

    //Formal
    public void SelectFormalButton()
    {
        Select1Panel.SetActive(false);
        Select17Panel.SetActive(true);
    }

    //BackButtonが押されたときの処理
    //MenuPanelをアクティブにする
    public void BackToMenu()
    {
        Select1Panel.SetActive(true);
        Select2Panel.SetActive(false);
        Select3Panel.SetActive(false);
        Select4Panel.SetActive(false);
        Select5Panel.SetActive(false);
        Select6Panel.SetActive(false);
        Select7Panel.SetActive(false);
        Select8Panel.SetActive(false);
        Select9Panel.SetActive(false);
        Select10Panel.SetActive(false);
        Select11Panel.SetActive(false);
        Select12Panel.SetActive(false);
        Select13Panel.SetActive(false);
        Select14Panel.SetActive(false);
        Select15Panel.SetActive(false);
        Select16Panel.SetActive(false);
        Select17Panel.SetActive(false);
    }
}
```
このスクリプトではキャンバス内に設定する印象と同じ数のパネルを用意し、選択した印象をクリックすると印象内のイメージを選択する画面に変更します。ここではアクティブの表示設定を変えることにより、画面変更しています。
スクリプトをcanvas内に置き、表示させるパネルを指定します。
各ボタンにクリック時に指定のパネルが表示するようにします。
![スクリーンショット (257).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/a59e75d6-c8ad-26a1-ef0f-6fefdd659a6c.png)
![スクリーンショット (259).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/94bb5f59-c012-b2f7-a1ac-b551fea0a81f.png)

イメージ選択画面では選択するボタンをクリックすると、部屋の空間に遷移するようにしました。
backボタンを押すと印象選択画面に戻ります。これは上のスクリプトを使っています。
選択したイメージによって同じ部屋でもカラーを変更するためにグローバル変数で数値を定義し、別のシーンでも値を取れるようにしています。
※case1～9まで用意していますが省略しています。
※RGBで数値を変更しますがイメージカラースケールに書いてあるRGBは本などを購入して確認してください。（ここではすべて0の部分）

``` C#
using UnityEngine;
using UnityEngine.SceneManagement;

public class ChangeCasual : MonoBehaviour
{
    public static byte a1, a2, a3, b1, b2, b3, c1, c2, c3;
    public void ButtonClicked()
    {
        switch (transform.name)
        {
            case "Button1": //派手な 74
                a1 = 0;
                b1 = 0;
                c1 = 0;
                a2 = 0;
                b2 = 0;
                c2 = 0;
                a3 = 0;
                b3 = 0; 
                c3 = 0;
                SceneManager.LoadScene("CasualScene");
                break;
            case "Button9": //気楽な 27
                a1 = 0;
                b1 = 0;
                c1 = 0;
                a2 = 0;
                b2 = 0;
                c2 = 0;
                a3 = 0;
                b3 = 0;
                c3 = 0;
                SceneManager.LoadScene("CasualScene");
                break;
        }
    }
}
```
![スクリーンショット (261).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/5a895398-a622-c066-45ae-5c8bd12154c6.png)

![スクリーンショット (264).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/640b70db-bd46-a876-ffd7-095115123ecb.png)
![スクリーンショット (265).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/1d4c8dbc-46a7-fc12-341e-3109e7b92d82.png)
同じ部屋の構造でもカラーが異なる。

###6.部屋作成
部屋の作成はやり方たくさんあるのですが、アセットストアから持ってきたりもできます。
その場合だとサイズとかバラバラなので動作が重くなることもあります。
自分はplaneで床を作って箱みたいな状態にしています。
家具などはアセットストアから持ってきています。
アセットストア以外にも[3D Warehouse](https://3dwarehouse.sketchup.com/)でダウンロードしたりしました。
友人はBlenderで扇風機や電気ストーブを作成してました。（余談ですがをこの友人が突っ張り棒を[BOOTH](https://booth.pm/ja/items/1519367)で売ってます）

作成した部屋の例を挙げるとこのようになっています。
![スクリーンショット (274).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/d8c94338-1c3c-c49c-1c4d-d537af226f65.png)
サイズなどを見やすいようにするとよいと思います。
選択したイメージよって色の変更をするには変更したいオブジェクトにスクリプトをアタッチしました。

```C#
using UnityEngine;

public class CasualChange1 : MonoBehaviour {

    void Update () {
        {
            //オブジェクトの色をRGBA値を用いて変更する
            GetComponent<Renderer>().material.color = new Color32(ChangeCasual.a1, ChangeCasual.b1, ChangeCasual.c1, 1);
        }

    }
}
```
ChangeCasualのスクリプトのa1,b1,c1をつかって色を指定しています。
ここでは一つのスクリプトをですが、印象とイメージにあわせてたくさんのスクリプトを作成しています。

ライトなどの設定は他の記事を見てください。。。（難しいし重くなったりした）

##さいごに
読みづらい記事ですね。
動画上げたいんですが、ツイッターやってないので画像いっぱいはりました。
書いた方法以外にもやり方たくさんありますので、試してください。
![com.kanade.vrroom-20200529-141210.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/facf24d0-0065-1a12-2c7a-dac7fe2abb93.jpeg)
![com.kanade.vrroom-20200529-141225.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/ed46f028-c948-16b3-6d40-4ee4f42ed6a5.jpeg)
![com.kanade.vrroom-20200529-141240.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/b6161757-80b3-9acd-9d3a-076f85eecbd2.jpeg)
![com.kanade.sample5-20200529-141418.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/64bbcdf1-54ff-60e4-28f1-1f35bba3ca23.jpeg)
![com.kanade.sample5-20200529-141452.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/66cc6f54-6a45-cbde-ade6-8b7fb9d4b2ff.jpeg)
![com.kanade.vrroom-20200529-141305.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570847/aaf9f55d-3a7a-2ca2-4aa1-f3e186a88fa6.jpeg)


大変な時期ですが頑張りましょう。