「VR妄走」大好きなあの子とVRで一緒にランニングしてみた!

f:id:ozdasu:20190613203639p:plain

「VR妄走」CM

「VR妄走」のコンセプトが一発で分かるCMです!
(注:実際にはプレイエリア制限があり、動画のように縦横無尽には走れません)

目次

概要

「大好きなあの子と一緒なら、キツい運動も楽しくなるのでは…?」という発想から、VR空間で葵さん(僕が応援している女優)と一緒にランニングができるOculus Questアプリ「VR妄走」をつくった

f:id:ozdasu:20190428154341j:plain
叩き台となったイラスト

「VR妄走」の機能

  • 葵さんと一緒に走れる
  • 10分に1回、葵さんが音声で励ましてくれる

使用したツール

  • Oculus Quest
  • Unity 2018.4.0f1
  • Blender 2.79b

Oculus Quest

f:id:ozdasu:20190618190127j:plain

  • Oculus社のHMD
  • 動作にPCが不要
  • 6DoF
    • 身体の動きを捕捉できる
  • Androidベース
  • 2019年5月21日に発売された
  • 発売したばかりで知見が溜まっていないので、Questと共通点の多い(スタンドアロン型でAndroidベース)Oculus Goのノウハウを応用する

Oculus Questを選んだ理由

  • スタンドアロンで動作し、身体の動きをセンシングできるので、本企画(ランニング)にピッタリだと思った
    • 身体の動きと連動させてVR空間内を走り回れるようにしたかったが、結局コントローラーで操作することにした
    • 次のバージョンでは身体に連動させたい

Unity

  • ゲームエンジン
  • ゲーム以外のコンテンツもつくれる
    • Oculus Questアプリも開発できる
  • Oculus推奨バージョンの2018.4 LTSを利用

Blender

  • 3DCGを制作・編集できるソフト

制作

①葵さんの3DCGモデル作成

頭をつくる

写真からメッシュとアバターを生成できるUnityプラグイン「Avater Maker」で葵さんの頭をつくる

元画像

f:id:ozdasu:20190521215013j:plain

結果

髪は後からつけたもの f:id:ozdasu:20190521215118p:plain

生成したメッシュとアバターをFBX exportし、Blenderにimportする

胴体をつくる

f:id:ozdasu:20190618200401p:plain
MoxRig(胴体のみ)

頭と胴体のメッシュを結合

f:id:ozdasu:20190618200620p:plain

モデルが完成。MoxRigにはあらかじめリグが通されているので、別途、設定する必要はない
FBX形式でexportし、Unityにimport。Mecanimを設定する

②「葵さんと一緒に走れる」機能の実装

  • VR空間とのインタラクション
    • プレイヤーは、VR空間とどうやってインタラクションするか
    • VR空間は、どのようにして、「プレイヤーが走っている」ことを認知するか
  • 葵さんとプレイヤーを移動させる
  • 移動と同時に、葵さんに「走る」アニメーションを付ける
    • アニメーションを付けないと、身体が硬直したまま、ムーンウォークのように移動してしまう

当該機能のスクリプト

以下のスクリプトをOVRPlayerController(プレイヤーのGameObject)に追加

using UnityEngine;

public class Run : MonoBehaviour
{
    GameObject aoi;
    Animator aoiAnimator;

    private void Start() // ゲーム開始時に実行される
    {
        aoi = GameObject.Find("Aoi");
        aoiAnimator = aoi.GetComponent<Animator>();
    }

    private void Update() // フレームが更新されるごとに実行される
    {
       OVRInput.Update();

        if (OVRInput.Get(OVRInput.Button.One)) // TouchControllerButton.Oneが押されていれば
        {
            // Animator ControllerのisIdleパラメーターをFalse、isRunパラメーターをFalseに
            aoiAnimator.SetBool("isIdle", false);
            aoiAnimator.SetBool("isRun", true); 
            Invoke("MoveFoward", 2.0f); 
        } else { // TouchControllerButton.Oneが押されていなければ
          // Animator ControllerのisRunパラメーターをFalse、isIdleパラメーターをTrueに
            aoiAnimator.SetBool("isRun", false); 
            aoiAnimator.SetBool("isIdle", true); 
        }
    }

    void MoveFoward()
    {
      // 葵さんが、Z方向に、前回のフレームから経過した時間×3、移動する
       aoi.transform.Translate(0, 0, Time.deltaTime * 3);

       // プレイヤーが、Z方向に、前回のフレームから経過した時間×3、移動する
       transform.Translate(0, 0, Time.deltaTime * 3); 
    }
}

VR空間とのインタラクション

VR空間とのインタラクションには、Oculus TouchControllerを用いる

f:id:ozdasu:20190618185041j:plain f:id:ozdasu:20190618184728p:plain

TouchControllerのButton.Oneを押すと葵さんとプレイヤーが走り、離すと止まる

       OVRInput.Update();

        if (OVRInput.Get(OVRInput.Button.One)) // TouchControllerButton.Oneが押されていれば
        {
            // Animator ControllerのisIdleパラメーターをFalse、isRunパラメーターをFalseに
            aoiAnimator.SetBool("isIdle", false);
            aoiAnimator.SetBool("isRun", true); 
            Invoke("MoveFoward", 2.0f);  // 2秒後に実行
        } else { // TouchControllerButton.Oneが押されていなければ
          // Animator ControllerのisRunパラメーターをFalse、isIdleパラメーターをTrueに
            aoiAnimator.SetBool("isRun", false); 
            aoiAnimator.SetBool("isIdle", true); 
        }

葵さんとプレイヤーを移動させる

    void MoveFoward()
    {
      // 葵さんが、Z方向に、前回のフレームから経過した時間×3、移動する
       aoi.transform.Translate(0, 0, Time.deltaTime * 3);

       // プレイヤーが、Z方向に、前回のフレームから経過した時間×3、移動する
       transform.Translate(0, 0, Time.deltaTime * 3); 
    }

葵さんにアニメーションを付ける

アニメーションは、Unityプラグインの「Mecanim Locomotion Starter Kit」から「Humanoid Idle(停止時)」と「Humanoid Run(走行時)」を利用

Animator Controller

  • 開始時はIdleアニメーション
  • isRunがTrueならRunアニメーションを開始
  • isIdleがTrueならIdleアニメーションを開始

f:id:ozdasu:20190618184116p:plain

③「10分に1回、葵さんが音声で励ましてくれる」機能の実装

葵さんのGameObjectに、AudioSourceとして「励ましの音声」を追加
加えて、以下のスクリプトをコンポーネントに追加

using UnityEngine;

public class Cheer : MonoBehaviour
{
    public float span = 600.0f;  // 600秒=10分
    private float currentTime;
    private AudioSource audioSource;

    void Start()  // ゲーム開始時に実行される
    {
        audioSource = gameObject.GetComponent<AudioSource>();
    }

    void Update()  // フレームが更新されるごとに実行される
    {
        currentTime += Time.deltaTime;  // 前回のフレーム更新からの経過時間を足す
        if (currentTime > span)  // 前回の声援から10分経っていれば
        {
            audioSource.Play(); // 励ましの音声を再生する
            currentTime = 0.0f;
        }
    }
}

④完成

f:id:ozdasu:20190618204634g:plain

課題

  • VR酔い対策
    • プレイヤーの身体を表示する
  • 葵さんの身体を人間にする
  • TouchControllerのボタンではなく、プレイヤーの動きによってインタラクションを起こしたい

ソースコード

参考文献

Oculus

Oculus Quest

Oculus Go

Unity

VR

  • VR概要 - Unity
  • UnityによるVRアプリケーション開発(オライリー・ジャパン)

アニメーション

オーディオ

定期実行

Avater Maker

Blender