Android
Unity
VR

UnityでVR対応(Cardbord)したAndroidアプリを作成する

こんなもの作ります

概要

社内でアプリやVR作成をしたころがないメンバが集まり、Unityを使ってCardboardのVRアプリ作成を始めていきます。
イメージは以下のようなもの。

image.png

UnityではAndroid端末向けのapkファイルの作成やPC向けのexeファイルの作成は最後の最後に切り替えることができます。
そのため、まずはUnityで3Dゲームを作成しその後対象の端末に向けて実行ファイルを作成していきます。

メイン画面の説明

image.png

①問題を表示
②回答1を表示
③回答2を表示
④中心線、トロッコが左右どちらに位置するかで回答をする

(見えていないですが)トロッコは中心線に対して左右どちらに目線があるかで左右にハンドルを切ります。
そして、問題文の下を通るときに左右どちらに位置するかで回答をします。
Android端末であればそのまま目線を指します。PCの場合はマウスの座標が目線となります。

動かしたものがこちら

動画は貼れなかったのでアニメーションgifでのご提供。
非常にわかりづらいのですが、スマホの傾きによってトロッコが左右にハンドルを切っています。
こうしたスマホ実機を用いたVRアプリを今から作っていきます。

15226858710I5iDf41Zqs_nUb1522685861.gif

まずは3Dゲームを作る

トロッコ

トロッコは3Dオブジェクトを組み合わせています。このトロッコの中にMainCameraを入れ込みトロッコを動かすことでカメラも動くようにしました。

image.png

トロッコを動かす

トロッコを前へ動かすために、GoAheadというスクリプトを追加します。

image.png

using UnityEngine;

public class GoAhead : MonoBehaviour
{
    public CharacterController eCon;
    public int targetX;
    public int targetY;
    public int targetZ;
    private Vector3 destination;    //目的地
    private float speed;    //スピード値
    private Vector3 velocity;        //速度×方向
    private Vector3 direction;       //移動方向
    // Use this for initialization
    void Start()
    {
        destination = new Vector3(targetX, targetY, targetZ);
        speed = 10.0f;
        velocity = Vector3.zero;
    }
    // Update is called once per frame
    void Update()
    {
        if (eCon.isGrounded)
        {
            velocity = Vector3.zero;
            direction = (destination - transform.position).normalized;
            transform.LookAt(new Vector3(destination.x, transform.position.y, destination.z));
            velocity = direction * speed;
        }
        velocity.y += Physics.gravity.y * Time.deltaTime;
        eCon.Move(velocity * Time.deltaTime);
    }
}

Handleというスクリプトを作って、目線の位置によってトロッコのハンドルを切るようにします。
このようにスクリプトを組むとHandle(Script)にObjectという要素が現れるのでEyeのオブジェクトをドラッグして指定します。
Eyeオブジェクトは後述。

using UnityEngine;

public class Handle : MonoBehaviour {

    [SerializeField]
    private GameObject m_object = null;

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

        if (m_object.transform.position.x < transform.position.x)
        {
            if (transform.position.x >= -1)
            {
                transform.position -= new Vector3(0.1f, 0f, 0f);
            }
        }
        else
        {
            if (transform.position.x <= 1)
            {
                transform.position += new Vector3(0.1f, 0f, 0f);
            }
        }

    }
}

目線を作る

トロッコの中にEyeオブジェクトを作っていますが、実際はどこでもいいです。
PCならマウスの位置にこのオブジェクトを持っていき、スマホなら利用者の目線にこのオブジェクトを持っていきます。
今はとりあえずPC用にマウス位置にオブジェクトを持っていきます。

image.png

using UnityEngine;

public class EyeTarget : MonoBehaviour {

    [SerializeField]
    private GameObject m_object = null;

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

        Vector3 touchScreenPosition = Input.mousePosition;

        // 10.0fに深い意味は無い。画面に表示したいので適当な値を入れてカメラから離そうとしているだけ.
        touchScreenPosition.z = 10.0f;

        Camera gameCamera = Camera.main;
        Vector3 touchWorldPosition = gameCamera.ScreenToWorldPoint(touchScreenPosition);

        m_object.transform.position = touchWorldPosition;

    }
}

ゲームクリア画面の作成

1.シーンの作成
「File」→「NewScene」からシーンを作成。

2.画面の作成
テキストとボタンを貼り付け、位置やフォントサイズなどを整えて作成。

無題.png

3.ボタンの設定
ボタンを押したとき、タイトル画面に戻るように設定。

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;

[RequireComponent(typeof(Button))]
public class GameOverScript : UIBehaviour
{
    protected override void Start()
    {
        base.Start();

        // Buttonクリック時、OnClickメソッドを呼び出す
        GetComponent<Button>().onClick.AddListener(OnClick);
    }

    void OnClick()
    {
        // 「title」シーンに遷移する
        SceneManager.LoadScene("title");
    }
}

スクリプトを作成し、ドラッグ&ドロップでボタンに紐付ける。
無題1.png

※同様の手順でゲームオーバーの画面も作成。
※タイトルのシーンも作ってます。説明は割愛。

PC用の実行ファイルを出力する

実行ファイル出力用の設定をする

メニューのFileからBuild Settings...を選択します。
Build SettingsのAdd Open Scenesを押し、必要なシーンをすべてチェック済み状態にします。
Player Settings...を押します。

image.png

Inspectorが開かれるので、Company NameとProduct Nameを適当に決めます。
ここでは、「DefaultCompany」「torokko」としました。
下矢印のタブがPC用の設定です。今回は特にいじりません。

image.png

PC用の設定が完了したら、Build SettingsでBuildを押します。

image.png

_Dataフォルダと、exeファイル、UnityPlayer.dllの3つのものが出来上がります。
exeファイルをダブルクリックすればPC上で起動できます。

image.png

Android用の実行ファイルを作る

ここから本番です。まずはVRは横に置いておき、Android用の実行ファイルを作ることに注力します。

設定をいじらずにそのままビルドしてみる

image.png
image.png

Android SDKの場所を求められてしまいました。

Android SDKをインストールする

https://developer.android.com/studio/index.html
ここからAndroidStudioをインストールします。AndroidSDKは単体ではダウンロードできないようです。
AndroidStudioを初回起動したときにAndroidSDKのインストールが始まります。
AndroidStudioは利用しませんが、1回は起動しましょう。

Unityに戻って、EditのPreferences...からUnity Preferencesを開きます。
そのExternal ToolsからAndroidSDKとAndroidJDKを指定します。
image.png
image.png

Android SDKのtoolsを差し替える

さて、もう1回ビルドします。

image.png

https://qiita.com/niusounds/items/bcbeee622b2114b4016e
ここに記載のように、AndroidSDKのtoolsを差し替える必要があるようです。差し替えます。

http://dl-ssl.google.com/android/repository/tools_r25.2.5-windows.zip
ここからzipを落としてきて、解凍します。

AndroidSDKのtoolsに上書きします。左から右へ上書きします。

image.png

パッケージ名を合わせましょう

さて、もう1回ビルドします。

image.png

Company NameとProduct Nameは、AndroidタブのOther SettingsのPackage Nameと合わせる必要があります。
com.Company.ProductNameからDefaultCompany.torokkoへと修正しました。

image.png

JDKは1.8までのようです

さて、もう1回ビルドします。

image.png
image.png

先ほど、JDK10を指定していましたがJDK1.8まででないといけないようです。
JDK1.8に設定を変更します。

image.png

Android用実行ファイルが作成されました

ひとまずAndroid用実行ファイルが作成されました。まだ、VRという言葉はどこにも出てきていません。

image.png

VR化する

VR化の設定をする

VR化するのは簡単です。
AndroidタブのXR SettingsのVirtual Reality Supportedをチェックします。
今回はCardbordを選択します。(VR機器を何も持たない状態でVRアプリを作るのが目的の1つだったりします)

image.png

Androidの動作環境を設定する

さて、もう1回ビルドします。

image.png

Cardbordは最低のAPIレベル19が必要なのに対し、16を選択している状態だと怒られています。
AndroidタブのMinimum API Levelを16から19に修正します。

image.png

VR化されたAndroid実行ファイルが作成されました

このapkファイルをAndroid端末でインストールすればもうVR化されています。
しかし、もう少しだけ続きます。

image.png

VR上の目線でトロッコを操作する

どういうこと?

最初に3Dゲームを作ったときに、マウスの位置を目線とするようにスクリプトを組みました。
それでPC上ではうまくいっていましたが、Android端末ではマウスはないのでうまく動きません。
VR上では利用者の目線方向にトロッコを動かしたいのです。

EyeTargetスクリプトを修正する

EyeTargetスクリプトの中でマウスの座標位置を取得しEyeオブジェクトの座標としていました。
そして、Eyeオブジェクトの座標が中心より左か右かでハンドル操作をするスクリプトも組み終わっています。
つまり、EyeTargetスクリプトでVR上の目線をEyeオブジェクトの座標とすれば他の処理は変更しなくてよいはずです。

以前のロジックを一部コメントアウトし、以下のようにロジックを修正します。

using UnityEngine;

public class EyeTarget : MonoBehaviour {

    [SerializeField]
    private GameObject m_object = null;

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

        //Vector3 touchScreenPosition = Input.mousePosition;

        // 10.0fに深い意味は無い。画面に表示したいので適当な値を入れてカメラから離そうとしているだけ.
        //touchScreenPosition.z = 10.0f;

        Camera gameCamera = Camera.main;
        //Vector3 touchWorldPosition = gameCamera.ScreenToWorldPoint(touchScreenPosition);
        Vector3 touchWorldPosition = gameCamera.transform.position;
        touchWorldPosition.z = 10.0f;

        m_object.transform.position = touchWorldPosition;
    }
}

修正後に、再度ビルドしなおしたら完了です。

感想

VR化は簡単だよっていう情報を元に始めたのですが、予想外のところにつまづきが多かったです。
とはいえ、苦戦しつつ初めて実機でVRが動いたときの感動は大きかったです。
開発環境面での障害は取り除けたと思うので、今度は内容がともなったVRアプリの作成に励んでいきたいと思います。