宮崎 IT 関連勉強会 Advent Calendar 2019 23日目の記事です。

UnityではVRは簡単に実装できますが、VRとノーマルモードの切り替えがちょっとややこしいです。アプリ起動時はノーマルモードで、ゲーム開始後はVRモードにしたいという場面はあると思います。もしくは、VRがメインだけど、ノーマルモードでも遊べますよ、といったケースですね。

また、VRモードではタッチでの操作が出来ないため、視線イベントが必要となります。 ボタンを3秒見つめたら、ボタンタッチとするという仕組みです。コントローラが有れば、そういったことも必要ではなくなるのですが、ユーザが必ずしもコントローラを持っているとは限りません。

そして、厄介なことにUnityやGoogleVRのバージョンが上がるたびに仕様が変わっていくので、 備忘録の意味も込めて現時点のバージョンの実装方法を記載します。

説明で用いるプロジェクトを下記リポジトリに用意しているので、良ければ参考にしてください。

https://github.com/sear-azazel/SampleVR.git

前置き

Google VR SDK for Unityのgithubリポジトリは現在アーカイブされています。

Cardboardのソフトウェア関連技術がオープンソースとなり、またDaydreamの販売終了とニュースもありますが、Googleが完全にVR手放したとしても、ソースコードは生き続けるようです。

目標

UnityでVRを実装し、iOS or Androidに出力するまでを行います。

また、アプリ起動時はノーマルモードの状態で、ゲーム開始時にVRモードになるように組み込みたいと思います。

前提条件

必要なもの

  • スマートフォン
    • iOS or Android
  • 開発環境/ツール(Unity以外)
    • Android SDK
      • Android NDKもあれば良い(必須ではない)
    • Xcode
      • iPhone用にビルドする場合は必須
    • Visual Studio (for Mac)
      • コーディングで利用する
      • Unityと併せてインストールできる(はず)
      • コードエディタは何でも良いが、利便性を考えるとVisual Studio一択
      • おすすめはVisual Studio 2019 (Windows)
        • 以前のバージョンに比べて3倍速い
    • (補足)Cloud Build を契約すると、ビルドが自動化できて便利

Unity Hubのバージョン

  • 2.2.1 2.2.2 (執筆中にバージョンが上がりました)
    • Unity Hub ← ここからダウンロード
    • (補足) Unityをプロジェクトごとにバージョン指定して利用するためのHubツール
      • 複数のバージョンのUnityをディレクトリを分けてインストール出来る
        • 複数プロジェクトを扱う時に便利
      • 利用しなくても問題ない

Unityのバージョン

google VRのバージョン

  • v1.200.1
    • Google VR SDK for Unity ← ここからダウンロード
      • GoogleVRForUnity_1.200.1.unitypackageをダウンロードする

開発前補足

VRとそうでない状態の言葉を使い分けるために、VRモード、ノーマルモードという言葉を使います。

開発手順

Unityでプロジェクトを作る

メニューシーンとゲームシーンの2つを用意して、VRモードとノーマルモードを切り替えるための準備をします。 今回は簡単に、メニューシーンに「開始」ボタン、ゲームシーンに「終了」ボタンを設けて実現したいと思います。

プロジェクト共通

  1. Unity Hub (もしくはUnity)を起動して、プロジェクトを作成します。
    • テンプレートは3Dを選択してください。
  2. Unityエディタ上に複数タブが表示されますが、下記タブが常時表示されていると開発しやすいと思います。タブの説明を併せて簡単に行います。
    • Hierarchy
      • 3D空間上に配置するオブジェクトが階層状態で表示されます。子階層のオブジェクトは位置/回転、表示状態に依存します。
    • Project
      • Unityプロジェクトで用いるファイルが表示されます。プラグインやスクリプト、3Dオブジェクトモデル、画像、音源などの実ファイルです。Explorer/Finderのファイルがそのまま表示されています。(.metaデータ除く)
    • Inspector
      • Hierarchyタブで選択したオブジェクトのコンポーネントが表示されます。コンポーネントは自由に追加/削除出来ます。全てのオブジェクトはTransformコンポーネントがアタッチされており、このコンポーネントが位置/回転/スケール情報を持っています。
    • Scene
      • 3D空間です。実際に配置したオブジェクトが表示されます。この画面でもオブジェクトを直接移動/回転/リスケール出来ます。
    • Game
      • アプリとして起動する状態が確認できます。

プラグインのインポート

Google VR SDK for Unityプラグインのインポートを行います。

Unityエディタのメニュー→Assets→Import Package→Custom Package…と選択し、ダウンロードしたGoogle VR SDK(GoogleVRForUnity_1.200.1.unitypackage)を選択します。

インポートには少々時間がかかります。

メニューシーン

  1. シーンファイルを作成します。
    • デフォルトで作成されている「SampleScene」をリネームして「Menu」とします。
  2. ゲームシーンへ遷移するための「開始」ボタンを配置します。
    1. Hierarchyタブで右クリック→UI→Buttonと選択してください。
      • Gameタブの真ん中にボタンが配置されます。
    2. HierarchyタブでTextオブジェクトを選択し、Text (Script) コンポーネントでボタンのラベルを「開始」に変更してください。
    • (補足)ボタンが小さい or 大きい場合は、Canvasの設定で適宜キャンバスサイズを調整してください。
      1. HierarchyタブのCanvasを選択し、Canvas Scaler (Script) コンポーネントのUI Scale ModeScale With Screen Sizeに変更します。
      2. Reference Resolutionを X: 1280、Y: 720にすると丁度良いかもしれません。
    • (補足の補足)上記設定はキャンバスのサイズを固定化することが出来ます。UnityエディタのGameタブや実機の解像度に関係なくそのサイズでキャンバス内のオブジェクトをサイジングします。
  3. 「開始」ボタンにシーン遷移スクリプトを追加します。
    1. Projectタブで右クリック→Create→C# Scriptと選択し、「Menu」のスクリプトを作成してください。
    2. Unityエディタのメニュー→Assets→Open C# Projectと選択し、Visual Studioを起動します。
    3. 下記のように実装します。
      // Menu.cs
      using UnityEngine;
      using UnityEngine.SceneManagement;
      
      public class Menu : MonoBehaviour {
          public void PlayGame() {
              SceneManager.LoadScene("Game"); // 注1
          }
      }
      
      • 注1: “Game"の文字列は作成したシーン名と同じにしてください。
    4. スクリプトをアタッチするためのGameObjectを作成します。
      • Hierarycyタブ内の空きスペースで右クリック→GameObjectと選択してください。
        • 別のオブジェクトの配下にGameObjectが追加されても問題はありませんが、気になる場合はDrag&Dropで適宜移動させてください。
    5. 作成したGameObjectを選択した状態で、InspectorタブにあるAdd Compornentを選択し、作成したMenu.csを追加します。
    6. Buttonのクリックイベントに作成したスクリプトのメソッドを紐づけます。
      1. HierarchyタブでButtonオブジェクトを選択し、Button (Script)On Click () 右下の"+”(プラスボタン)をクリックしてください。
      2. On Click () 枠内に左下にあるNone (Object) の部分に作成したGameObjectをDrag&Dropします。
      3. On Click () 枠内に右上にあるNo Function をリストダウンしてMenu→PlayGameと選択してください。(作成したMenu.csのメソッドです。)
    7. 最終的にHierarchyタブはこんな感じになります。
      [Heirarchy]
          - Main Camera
          - Directional Light
          - EventSystem
          - Canvas
              - Button
                  - Text
          - GameObject
      

Utility作成

ゲームシーンを作成する前にVRモードとノーマルモードを切り替えるUtilityを作成します。

Unityが保持するクラス(XRSettings)では単純な切り替えが出来ないため、視線イベントのためのオブジェクトの設定も併せて実装します。

新規にスクリプトを作成して、下記のように実装します。名前はXRUtilityとします。

// XRUtility.cs
using System.Collections;
using UnityEngine;
using UnityEngine.XR;

public class XRUtility : MonoBehaviour {
    private const string DeviceNone = "None";
    private const string DeviceCardboard = "Cardboard";

    /// <summary>視線イベントのためのポインタ</summary>
    [SerializeField] private GvrReticlePointer reticlePointer;
    private static XRUtility instance;

    /// <summary>VRモードの切り替え</summary>
    public static bool IsEnabled {
        get => XRSettings.enabled;
        set => instance.StartCoroutine(instance.ChangeEnable(value));
    }

    private void Awake() {
        instance = this;
    }

    private void Start() {
        this.reticlePointer.gameObject.SetActive(false);
    }

    private IEnumerator ChangeEnable(bool enabled) {
        if (XRSettings.enabled == enabled) { yield break; }
        XRSettings.LoadDeviceByName(enabled ? DeviceCardboard : DeviceNone);
        yield return null;
        XRSettings.enabled = enabled;
        this.reticlePointer.gameObject.SetActive(enabled);
    }
}

ゲームシーン

  1. メニューシーンと同様にゲームシーンを作成します。
    1. Projectタブで右クリック→Create→Sceneと選択して、「Game」シーンを作成します。
  2. メニューシーンへ遷移するための「終了」ボタンを作成します。
    1. 作り方は「開始」と同じです。
    2. メニューシーンと異なるのはCanvasの設定です。
      1. HierarchyタブでCanvasオブジェクトを選択し、InspectorタブのCanvasコンポーネントでRender ModelWorld Spaceに変更してください。
        • そうすると、ボタンが極端に大きくなるので、CanvasのTransformコンポーネントのScaleを0.05前後にしてください。(X, Y, Z同じ値です。)
        • (補足)変更前のCanvasの設定では、画面の上に1枚のレイヤーが存在し、そこにボタンなどのUIを配置するイメージです。現実世界(とゲームの世界の境界線)のインターフェースですが、変更後は、ゲームの世界にボタンを配置するイメージです。そうすることによって、VRとしてボタンを表現することができるようになります。なので、Render ModelWorld Space以外だと、VRにはなりません。
    3. 新規にスクリプト作成し、下記のように作成します。名前をGame.csとします。
      // Game.cs
      using System.Collections;
      using UnityEngine;
      using UnityEngine.SceneManagement;
      using UnityEngine.UI;
      
      public class Game : MonoBehaviour {
          [SerializeField] private Text countdownText;
          private Coroutine countdown = null;
      
          /// <summary>視線(カーソル)がボタンに重なったときのイベント</summary>
          /// <remarks>カウントダウンの処理を開始します</remarks>
          public void OnEnter() {
              this.countdown = this.StartCoroutine(this.Countdown());
          }
      
          /// <summary>視線(カーソル)がボタンから離れた時のイベント</summary>
          /// <remarks>カウントダウンの処理を停止します</remarks>
          public void OnExit() {
              this.StopCoroutine(this.countdown);
              this.countdownText.text = string.Empty;
          }
      
          /// <summary>ボタンが重なって離れるまでに走る処理</summary>
          /// <remarks>カウントダウンが終わったら、シーン遷移します</remarks>
          private IEnumerator Countdown() {
              const int sec = 3;
              float elapsed = 0;
              while (elapsed < sec) {
                  this.countdownText.text = $"{(int)(sec - elapsed)}";
                  elapsed += Time.fixedDeltaTime;
                  yield return null;
              }
              SceneManager.LoadScene("Menu");
          }
      }
      
    4. 視線イベントによるカウントダウンを表示するため、CanvasにTextオブジェクトを追加します。
      • Hierarchyタブ内のCanvasオブジェクト上で右クリック→UI→Textを選択してください。
        • 表示位置は適宜修正してください。ボタンと重ならないくらいが丁度良いです。
    5. メニューシーンの時と同様にスクリプトをアタッチするためのGameObjectを作成して、Game.csを追加してください。
      1. Inspectorタブ上のGame (Script)コンポーネントにCountdown Text項目が表示されるので、作成したTextオブジェクトをHierarchy上から、Countdown Text項目の右に表示されているNone (Text) 部分にDrag&Dropしてください。
      2. メニューシーンと同様にButtonのイベントにメソッドを紐付けます。
        1. ButtonEvent Triggerコンポーネントを追加してください。
        2. Add New Event Typeボタンをクリックして、Pointer EnterとPoiner Exitを追加してください。
        3. メニューシーンの作成で、ButtonのOn Clickにメソッドを紐付けたように、以下2つを紐付けてください。
          • Pointer Enter: OnEnter
          • Pointer Exit: OnExit
    6. Utilityスクリプトを追加します。
      1. 作成したXRUtility.csをGame.csを追加したGameObjectに追加してください。
      2. XRUtility (Script)コンポーネントにReticle Pointerが表示されるので、Main Cameraの直下に配置した、GvrReticlePointerをDrag&Dropしてください。
    7. VR用のイベントを拾うための設定を行います。
      1. HierarchyタブでEvent Systemを削除して、GoogleVRのGvrEventSystem(Prefab)をProjectタブからDrag&Dropして追加してください。
        • Assets/GoogleVR/Prefabs にあります。
      2. HierarchyタブのMain CameraにGvrReticlePointer(Prefab)を追加してください。
        • Assets/GoogleVR/Prefabs/Cardboard にあります。
    8. エディタ上で動作確認するためのオブジェクトを配置します。
      • GvrEditorEmulator(Prefab)を追加してください。
        • Assets/GoogleVR/Prefabs/EventSystem にあります。
      • (補足)以前のGoogle VR SDKでは、エディタ上でのゲーム動作も2眼でしたが、現在のバージョンでは、ノーマルとしてしか動作できません。
    9. 最終的にHierarchyタブはこんな感じになります。
      [Heirarchy]
          - Main Camera
              - GvrReticlePointer 注2
          - Directional Light
          - Canvas
              - Button
                  - Text
              - Text
          - GameObject
          - GvrEventSystem 注2
          - GvrEditorEmulator 注2
      
      • 注2: Prefabとして追加したので、青いアイコンです

ビルド設定およびXRの有効化

Unityエディタのメニュー→File→Build Settings…と選択し、「Player Settings…」ボタンをクリックしてください。

主な設定事項を以下に記載します。特に記載がない場合はiOS/Android共通です。


Resolution and Presentation

Orientation

  • Default Orientation: Landscape (Left or Right)

Other Settings

[Android]

Identification

  • Package Name: 例) com.miyazakis.samplevr
  • Version: 例) 0.0.1
  • Bundle Version Code: 例) 1
  • Minimum API Level: Android 4.4 ‘KitKat’ (API level 19)

Target Architectures

  • ARMv7: false
  • ARM64: true
  • x86: false

Configuration

  • Api Compatibility Level: .NET 4.x

[iOS]

Identification

  • Bundle Identifier: 例) com.miyazakis.samplevr
  • Version: 例) 0.0.1
  • Build: 例) 1

Configuration

  • Api Compatibility Level: .NET 4.x
  • Architecture: ARM64

XR Settings

  • Virtual Reality Supported: true
  • Virtual Reality SDKs (以下の順番は大事。上にある方がアプリ起動時の状態となるため)
    • None
    • Cardboard

ビルド

各Pratform(iOS or Android)向けにビルドを行います。

iOSの場合Xcodeプロジェクトへのエクスポートになるので、エクスポートした後にXcodeでプロジェクトを開いてビルドしてください。

  1. Unityエディタのメニュー→File→Build Settings…と選択してください。
    • ビルドしたいPlatformが選択されていない場合は、Switch Pratformを行ってください。
  2. Buildボタンを押してください。

あとがき

長くなった上に、文字が多くて分かりづらいですが、下記リポジトリに作成したプロジェクトをコミットしていますので、参考にしてください。

https://github.com/sear-azazel/SampleVR.git

※ Androidの動作確認は行いましたが、iOSはまだ行っていないため、確認が取れたら追記します。

iOS、Android共に確認が取れました。

以上