【Unity(C#)】VRのデモ向けリセット、スキップ機能の実装方法

##サマーアドベントカレンダー
[Unityゆるふわサマーアドベントカレンダー 2019 #ゆるふわアドカレ](https://qiita.com/nkjzm/items/7c933bb5c772a0b75512)に参加させていただきました！！

Unity関連の記事ということで本記事はスレスレですが、ゆるふわなのでセーフセーフ！

##VRのデモ
VRエンジニアになってからというもの、
VRのデモを体験しに行ったり、実際に自分が実施したりする機会が増えました。

自分が実施する立場になった際には、
デモの目的によりますが、体験の回転率を意識しないといけない状況なんかも 時たまあります。

そんな時には、**体験者から体験者のスムーズな受け渡しが必要**になります。

また、営業で使用する際には
**・VR初心者の方の操作がうまくいかない**
**・時間の都合上、2人目以降は特定のシーンから開始する**
という状況が発生することが多く、 いきなり見せ場のシーンから始めたい! と思うことがよくあります。

##リセット、スキップ機能
そこでリセット、スキップ機能を使います。

リセットであれば**コンテンツの体験が終了した際、シーンを再度読み込ませた状態で次の体験者に渡す**という流れです。
スキップの場合は**操作に行き詰った際に、見せ場のシーンを読み込ませた状態で体験者に渡す**　といった感じです。

しかし、PCVRの場合、デモの内容によってはスタート位置及び終了位置がPCから離れた場所になる恐れがあります。
キー入力でリセット、スキップを実装すると、その度にPCまで戻らなければいけません。
また、スタンドアロン型のデバイスの場合はそもそもキー入力の実装はできません。

以上から、コントローラーでなんとかリセット、スキップを実装したいところです。


##誤操作防止
コントローラーでリセット、スキップ機能を実装したい理由は明らかになりましたが、
**単純にコントローラーにリセット機能を付与した場合、一つ問題があります。**

それは、**誤操作**です。

体験者が誤ってリセットボタンを押した際、デモが台無しになってしまいます。
人によっては「もう十分楽しめたので大丈夫ですよ(苦笑い)」と気を使わせてしまう場合もあります。

そこで、ゲームの**隠しコマンド**のように一工夫して実装します。
とはいえ難しくしすぎて入力に時間がかかるようでは本末転倒なので、
**誤操作はしない⇔簡単　というトレードオフの妥協点を責める**必要があります。

今回はサンプルを2パターン用意しました。

##長押し
「**～秒間押し続ける**」ことでリセット、スキップが行われます。
秒数を変更することで`妥協点`を責めましょう。

入力は`OVRInput`と書かれている箇所を変えるだけでOKです。

---
2019/09/09 追記
**トリガー長押しの実装はやめた方がいい**です！
実際にデモやって思いましたが、やったことない人みんな押しちゃう！
(Viveのグリップボタンくらいわかりにくいならありかも)

---

```C#:長押しでリセット、スキップ
using UnityEngine;
using UnityEngine.SceneManagement;

public class ReloadScene : MonoBehaviour
{
    [SerializeField]
    float inputSeconds;

    bool preventContinuityPushButton;

    float timer;

    void Update()
    {

        if (OVRInput.GetDown(OVRInput.RawButton.RHandTrigger))
        {
            preventContinuityPushButton = true;
        }

        if (OVRInput.Get(OVRInput.RawButton.RHandTrigger) == false)
        {
            timer = 0;
            return;
        }
        else if (OVRInput.Get(OVRInput.RawButton.RHandTrigger) && preventContinuityPushButton)
        {
            timer += Time.deltaTime;
        }

        if (timer > inputSeconds)
        {
            Scene loadScene = SceneManager.GetActiveScene();
            SceneManager.LoadScene(loadScene.name);
        }
    }
}

```

##連続入力
「**～秒以内に～回入力する**」ことでリセット、スキップが行われます。
秒数と回数を変更することで`妥協点`を責めましょう。
`0.5秒以内に3回`とかにすると結構いい塩梅です。

こちらも入力は`OVRInput`と書かれている箇所を変えるだけでOKです。

```C#:素早く連続押下でリセット、スキップ
using UnityEngine;
using UnityEngine.SceneManagement;

public class ReloadScene : MonoBehaviour
{
    [SerializeField]
    float inputResponseTime;

    [SerializeField]
    float inputCount;

    float timer;
    float buttonDownCount;
    float buttonDownTime;
    
    void Update()
    {
        timer = Time.time;

        if (OVRInput.GetDown(OVRInput.RawButton.RHandTrigger))
        {
            if (buttonDownCount == 0)
            {
                buttonDownCount++;
                buttonDownTime = Time.time;
            }
            else
            {
                buttonDownCount++;
            }
        }

        if (timer - buttonDownTime > inputResponseTime)
        {
            buttonDownCount = 0;
        }

        if (buttonDownCount== inputCount)
        {
            Scene loadScene = SceneManager.GetActiveScene();
            SceneManager.LoadScene(loadScene.name);
        }
    }
}
```

##補足

**ほとんど(できれば全く)使わないボタンにリセット、スキップ機能を搭載する**のが大前提です。
でなければ妥協点を責めるのが難しくなります。

あと、キッズには当てはまらないかもしれません。彼らの戦闘力は計り知れないのでうまくいく保証はないです。

また、VR初心者に対するデモの場合は
**・テキスト、画像、動画等の操作説明を事前に見せる**
**・慣れるまで操作補助者をつけ、専門用語抜きでわかり易く説明**
などするだけで理解度も没入度も全く別物になるので、しっかりと準備することが大切だと日々感じてます。

今回のコードはデバッグ時にも大活躍します。
上司にもアドバイスいただきましたが、リセット、スキップ機能を入れるってのは開発において速度を上げるために
まあまあ重要なんじゃないでしょうか。