VR開発初心者がクロスプラットフォーム意識して開発してみた

#はじめに
OculusRiftAdventCalender2017　24日担当の最上土川です！
なんかクリスマスイブというアドベントカレンダーでも結構重要そうな？ポジションに予約しちゃい、しかも次の日はGOROman大先生が担当なので、めちゃくちゃ緊張しながらこの記事を書いていますw
初心者の書く記事なので、温かい目を持って見てもらえると嬉しいです。
#VRのクロスプラットフォーム
VRと一言で言っても色々とプラットフォームありますよね
(たぶんこの記事を読んでいるような人は、すべて知っていると思いますが、一応書きますね。)
・PCVR系統
OculusRift、HTC Vive、WindowsMR、OSVR...
・コンシューマーゲーム機系統
PSVR、VIRTUAL BOY...
・スマホVR系統
GoogleCardBoard、DayDream、GearVR、Links...
・一体型
OculusGO、ViveWave...

等々、色々ありますが今回はPCVR系統でのクロスプラットフォームをやっていきます。
対象はOculusRift、HTC Vive、WindowsMRの3種類。
Oculus向け、HTC Vive向けならそれぞれのSDKを使えば簡単に実装できますが、今回は使いません。PCVRのクロスプラットフォーム実装で有名なVRTKもWindowsMR非対応なので使いません。
ヘッドトラッキングやハンドコントローラーの情報はUnityの標準機能で取得していきます。
#UnityEngine.XRを使う
UnityEngine.XRでヘッドトラッキングやハンドコントローラーのトラッキングなどが行えるので、使っていきます。

###使い方はすごく簡単
使い方自体はとても簡単です。
Edit→Projectsettings→PlayerでVirtualRealitySupportedにチェックをします。（標準ではOculusが設定に入っています。）

![1.PNG](https://qiita-image-store.s3.amazonaws.com/0/87699/075ede5c-effb-4b35-ec97-44cd99535850.png)
VirtualRealitySupportedにチェック
![2.PNG](https://qiita-image-store.s3.amazonaws.com/0/87699/b0a0c9bc-1075-22bf-f549-377b108d7ce6.png)
これだけで、何もコードを書いていないのにいつものメインカメラがVR対応（しかもヘッドトラッキングまで対応）になります。
###ハンドルコントローラの位置や傾きを取得する
まずは、サンプルを。

```C#:Rhand.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;

public class Rhand : MonoBehaviour {

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
        transform.position =
            GameObject.Find("Main Camera").transform.position 
            + InputTracking.GetLocalPosition(XRNode.RightHand) 
            - InputTracking.GetLocalPosition(XRNode.Head);
        transform.eulerAngles = 
            InputTracking.GetLocalRotation(XRNode.RightHand).eulerAngles;
    }
}
```

こんな感じで行けます。
数行のコードで作れるので簡単ですね。
###ハマる
少し古い記事やリファレンスを参考にしてしまったせいで、しばらくUnityEngine.VRという古い方で書いてしまいました。(XRだと気づくのに半日を要しました)

###なんか、position変な書き方なのだが
`MainCamera`と`InputTracking.GetLocalPosition(XRNode.Head);`の値ってどうやら違うみたいで、手として表示したいオブジェクトの位置にそのまま
`InputTracking.GetLocalPosition(XRNode.RightHand);`
を使用すると、位置が思ったのと違うところに表示されてしまいます(´・ω・｀)
それを防ぐために`Head`からの`RightHand`の相対位置を出して`MainCamera`に足します。
もし、よりよい方法を知っている方いましたら教えていただけるとうれしいです。


で、どんな感じになるかは下記動画を参考にしてもらえると。

[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/NLgpTHK5y2o/0.jpg)](http://www.youtube.com/watch?v=NLgpTHK5y2o)

##コントローラーのボタンとかを使う
まず始めに、各プラットフォームのハンドコントローラーを比較します

###OculusTouch
![ot_02.jpg](https://qiita-image-store.s3.amazonaws.com/0/87699/e64041c2-58c0-5f0d-97cc-93e70a8ad511.jpeg)

###HTC vive
![guid-2d5454b7-1225-449c-b5e5-50a5ea4184d6-web.png](https://qiita-image-store.s3.amazonaws.com/0/87699/98ba97b7-5aba-14cb-b7f5-b0f4053debb0.png)

###WindowsMR
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3135323534382f62343266363938322d636162342d326135662d323463372d3538313237663332333730312e706e67.png](https://qiita-image-store.s3.amazonaws.com/0/87699/fe45a0e5-bdf7-f61f-506f-cbca2ed61ed1.png)

###共通点
・トリガーボタンとサイドにボタンが有る
・JoyStickやJoyStickに似た機能が使える

↑なので、クロスプラットフォームにする場合は共通している機能しか使わないほうが良さそうですね。

###ボタン入力

ゲームコントローラーを使うのとほぼ同じ。
以下のコードは自分が開発中のゲームのやつです。
人差し指トリガーを引いたら弾が発射されるというシンプルなものです。

```C#:HandControler.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;

public class HandControler : MonoBehaviour {
    public GameObject bullet;
    // 弾丸発射点
    public Transform muzzle;

    // 弾丸の速度
    public float speed = 1000;
    public double time = 0;
    public AudioSource cannon2;
    // Use this for initialization
    void Start () {
        
	}
	
	// Update is called once per frame
	void Update () {
        time += Time.deltaTime;
        if(Input.GetAxis("10th axis") == 1F && time > 1)
        {
            Debug.Log("Fire");
            // 弾丸の複製
            GameObject bullets = GameObject.Instantiate(bullet) as GameObject;
            // 弾丸の位置を調整
            bullets.transform.position = muzzle.position;
            bullets.GetComponent<Rigidbody>().AddForce(gameObject.transform.forward * speed);
            cannon2 = GetComponent<AudioSource>();
            cannon2.PlayOneShot(cannon2.clip);
            time = 0;
            
        }
        
        transform.position = 
            GameObject.Find("Main Camera").transform.position + 
            InputTracking.GetLocalPosition(XRNode.RightHand)- 
            InputTracking.GetLocalPosition(XRNode.Head);
        transform.eulerAngles = InputTracking.GetLocalRotation(XRNode.RightHand).eulerAngles;
    }
}
```

10th axisが右人差し指トリガーボタンになっています。
他のボタンも書いときますね

右人差し指 : 10th axis
左人差し指 : 9th axis
右中指 : 12th axis
左中指 : 11th axis

で、上記コードを利用したやつがこちら

[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/jZ_HcNfQs1w/0.jpg)](http://www.youtube.com/watch?v=jZ_HcNfQs1w)

この試作品のゲームが気になる方は下の方で紹介しているので見てください。

##今度はWindowsMRでも動くようにする
現状、上記の手順でSteamVR経由でWindowsMRヘッドセットで動かすことができます。
が、やっぱりUWPでもビルドしないとクロスプラットフォ―ム感ないのでやります。
###Unityの機能だけではWinMRは使えない
~~UnityのVRサポートではWindowsMRは使えないので、MixedRealityToolkitを使っていきます。~~
https://github.com/Microsoft/MixedRealityToolkit-Unity

UnityのPlayerSettingsの中のUWP用設定の中にXRのサポートチェックボックスがありました。
これでUnityの機能だけでWinMRを実装できます。（まだ良くわからないところがあるので調査中）

#最後に
自分はマツハマProjectという高校の友だちと作ったサークルで、現在VR対応アクション＆萌ゲームを開発中です！！
酔いにくい移動方法（通称：水上スキー）や可愛いLive2Dキャラと濃い？ストーリーで、これまでにはなかったVRゲームを目指しています。
是非、そちらの方も見てもらえるとうれしいです。
中身はこの記事通りクロスプラットフォームを意識した構造になっています。（HTC Viveは持っていないので確認していませんが...）

[マツハマProject](http://matsuhama-project.com/)