3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

FirstVR キャリブレーションをasync/awatiで書き直す

Last updated at Posted at 2018-12-24

はじめに

FirstVRのSDKはジェスチャーのキャリブレーションをコルーチンで実行する仕組みになっています。
これを書き換え、async/awaitで使えるようにします。

変更前のコード

次のコードは、FirstVRのサンプルコードの一部抜粋です。


public void SetTargetPress()
{
    StartCoroutine(Calibrate(true));
}

public void SetNonTargetPress()
{
    StartCoroutine(Calibrate(false));
}


/// <summary>
/// Calibrate the gesture with target or non-target values.
/// Calibration requires time, and it's best to let the user know what's going on, so this process is best done in a coroutine.
/// </summary>
IEnumerator Calibrate(bool target)
{
    if (target)
    {
        // Setting target values
        fvr.gestureManager.SetTargetData(gesture);
        tCalibRounds++;
    }
    else
    {
        // Setting non-target values
        fvr.gestureManager.SetNonTargetData(gesture);
        /// The first time we set a target or non-target value, the round length and samples per second are ignored and the SVM takes only one value with dummy data then
        /// the dummy data is replaced with real data. 
        /// After the first round the FVRGesture.calibrated flag is set to true and you are ready to start calibrating with real data
        if (gesture.calibrated)
        {
            ntCalibRounds++;
        }
        else
        {
            nonTargetBtn.GetComponentInChildren<Text>().text = "Set\nNonTarget";
            foreach (Button b in varBtns)
            {
                b.interactable = false;
            }
        }
    }

    // We dont wan't multiple coroutines taking the same data so it's good to block the user from starting a new one before this round is done
    targetBtn.interactable = false;
    nonTargetBtn.interactable = false;
    resetBtn.interactable = false;
    float t = 0;
    while (gesture.registering)
    {
        /// While the target or non-target data is being set, the FVRGesture.registering flag will be set to true.
        /// A count down or a image fill loading bar is a good way to let the user know your app is doing something.
        /// Once the porcess is done, the FVRGesture.registering flag will be set to false, and we will exit this while loop.
        t += Time.deltaTime;
        if (target)
            targetImg.fillAmount = t / (float) roundLength;
        else
            nonTargetImg.fillAmount = t / (float) roundLength;
        yield return null;
    }

    UpdateTexts();
    targetImg.fillAmount = 0;
    nonTargetImg.fillAmount = 0;
    // After the process is done you can enable whatever buttons you need to proceed with the calibration or move on with your app.
    targetBtn.interactable = true;
    nonTargetBtn.interactable = true;
    resetBtn.interactable = true;
}

コルーチンでベタッと書かれているのを書き換えてみます。

拡張メソッド

キャリブレーションする部分を拡張メソッドに切り出します。

using UniRx.Async;
using UnityEngine;

namespace FVRSamples
{
    public static class FVRGestureManagerExt
    {
        public static async UniTask CalibrateTargetGestureAsync(this FVRGestureManager manager, FVRGesture gesture,
            IProgress<float> progress = null, CancellationToken token = default)
        {
            manager.SetTargetData(gesture);

            var startTime = Time.time;
            var span = manager.calibrationRoundLength;

            while (gesture.registering)
            {
                await UniTask.Yield();
                progress?.Report((Time.time - startTime) / span);
            }
        }

        public static async UniTask CalibrateNonTargetGestureAsync(this FVRGestureManager manager, FVRGesture gesture,
            IProgress<float> progress = null, CancellationToken token = default)
        {
            manager.SetNonTargetData(gesture);

            var startTime = Time.time;
            var span = manager.calibrationRoundLength;
            while (gesture.registering)
            {
                await UniTask.Yield();
                progress?.Report((Time.time - startTime) / span);
            }
        }
    }
}

UniTask.WaitWhileを用いることで特定のフラグがfalseになるまでawaitすることがきます。
IProgress<float>を外から渡し、現在のキャリブレーションの進行状況を通知できるようにしています。

全体を書き換えたあとのコード


public void SetTargetPress()
{
    _ = CalibrateAsync(true);
}

public void SetNonTargetPress()
{
    _ = CalibrateAsync(false);
}

private async UniTaskVoid CalibrateAsync(bool target)
{
    if (target)
    {
        tCalibRounds++;
    }
    else
    {
        if (gesture.calibrated)
        {
            ntCalibRounds++;
        }
        else
        {
            nonTargetBtn.GetComponentInChildren<Text>().text = "Set\nNonTarget";
            foreach (Button b in varBtns)
            {
                b.interactable = false;
            }
        }
    }

    targetBtn.interactable = false;
    nonTargetBtn.interactable = false;
    resetBtn.interactable = false;

    if (target)
    {
        await fvr.gestureManager.CalibrateTargetGestureAsync(gesture,
            Progress.Create<float>(f => targetImg.fillAmount = f));
    }
    else
    {
        await fvr.gestureManager.CalibrateNonTargetGestureAsync(gesture,
            Progress.Create<float>(f => nonTargetImg.fillAmount = f));
    }

    UpdateTexts();
    targetImg.fillAmount = 0;
    nonTargetImg.fillAmount = 0;
    targetBtn.interactable = true;
    nonTargetBtn.interactable = true;
    resetBtn.interactable = true;
}

コルーチンで書くよりかは制御しやすいかなと思います。たぶん。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?