開発
開発
プラットフォームを選択

チュートリアル1 - ビルトインNLPを使ってアプリ音声エクスペリエンスを有効化する

概要

このチュートリアルでは、ユーザーが希望する長さのタイマーを設定できる単純な瞑想アプリを構築します。Witのビルトインのインテントとエンティティを使った基本のやり方で、アプリ音声エクスペリエンスを使ってタイマーを設定します。

前提条件

このチュートリアルを始める前に、Voice SDKのチュートリアルで概説されている手順を完了し、必要な依存関係を含むプロジェクトを作成してください。チュートリアルはこのようなプロジェクトに基づき構成されています。

ビルトインNLPを用いてアプリ音声エクスペリエンスを有効化する

ステップ1: シーンを設定する

  1. 新しいUnityプロジェクトのブランクシーンで、新しいGameObjectを作成しCanvasという名前を付けます。
  2. GameObjectを右クリックしてから[UI] > [テキスト - TextMeshPro]の順に選択します。ボックスにLog Text (TMP)という名前を付け、画面の下部に移動します。このテキストボックスは、アプリのログメッセージを表示します。
  3. Canvas GameObjectを右クリックしTimer Text (TMP)という名前のテキスト要素を追加します。「0:00:00」を入力して、画面の中央に移動します。
  4. TimerDisplay.csという名前のスクリプトを添付して、Timer Text (TMP)要素に添付します。そこに、次のコードをコピーします。
     using TMPro;
     using UnityEngine;
    
    
     public class TimerDisplay : MonoBehaviour
     {
         public TimerController timer;
    
         private TextMeshProUGUI _uiTextPro;
    
         // Start is called before the first frame update
         void Start()
         {
             _uiTextPro = GetComponent<TextMeshProUGUI>();
         }
    
         // Update is called once per frame
         void Update()
         {
             // Note: This is not optimized and you should avoid updating time each frame.
             _uiTextPro.text = timer.GetFormattedTimeFromSeconds();
         }
     }
    
    
  5. Canvas GameObjectを右クリックしてボタン要素を追加し、Buttonという名前を付けます。このボタンの子を作成し[Button Label (ボタンラベル)]という名前を付けます。ラベルにActivateと入力し、Timer Text (TMP) GameObjectの下に配置します。
  6. Canvas GameObjectを右クリックしてテキスト要素を追加し、Status (TMP)という名前を付けます。これをButtonの子にし、空のままにします。これは字幕の一部または全部を表示し、アプリ音声エクスペリエンスのステータス(リスニング中、処理中、など)を表示します。
    Unityの[Hierarchy (階層)]ウィンドウの表示は次のようになります。
    "Screengrab of the Unity Hierarchy window"

ステップ2: タイマーを作成する

  1. 階層ビューに空のGameObjectを作成し、TimerGOという名前を付けます。次のコードを含んだTimerController.csというスクリプトを作成してTimerGOにアタッチし、タイマーを作成します。
     using System;
     using TMPro;
     using UnityEngine;
    
     /// <summary>
     /// Represents a countdown timer.
     /// </summary>
     public class TimerController : MonoBehaviour
     {
         private float _time = 0; // [sec] current time of the countdown timer.
         private bool _timerExist = false;
         private bool _timerRunning = false;
    
         public const int CONVERSION_MIN_TO_SEC = 60;
         public const int CONVERSION_HOUR_TO_SEC = 3600;
    
         [Tooltip("The UI text element to show app messages.")]
         public TextMeshProUGUI logTextPro;
    
         [Tooltip("The timer ring sound.")] public AudioClip buzzSound;
    
         // Update is called once per frame
         void Update()
         {
             if (_timerExist && _timerRunning)
             {
                 _time -= Time.deltaTime;
                 if (_time <= 0)
                 {
                     // Raise a ring.
                     OnElapsedTime();
                 }
             }
         }
    
         private void Log(string msg)
         {
             Debug.Log(msg);
             logTextPro.text = "Log: " + msg;
         }
    
         /// <summary>
         /// Buzzes and resets the timer.
         /// </summary>
         private void OnElapsedTime()
         {
             _time = 0;
             _timerRunning = false;
             _timerExist = false;
             Log("Buzz!");
             AudioSource.PlayClipAtPoint(buzzSound, Vector3.zero);
         }
    
         /// <summary>
         /// Deletes the timer. It corresponds to the wit intent "wit$delete_timer"
         /// </summary>
         public void DeleteTimer()
         {
             if (!_timerExist)
             {
                 Log("Error: There is no timer to delete.");
                 return;
             }
    
             _timerExist = false;
             _time = 0;
             _timerRunning = false;
             Log("Timer deleted.");
         }
    
         /// <summary>
         /// Creates a timer. It corresponds to the wit intent "wit$create_timer"
         /// </summary>
         /// <param name="entityValues">countdown in minutes.</param>
         public void CreateTimer(string[] entityValues)
         {
             if (_timerExist)
             {
                 Log("A timer already exist.");
                 return;
             }
    
             string duration = entityValues[0];
             string unit = entityValues[1]; // [sec, minute or hour].
    
             try
             {
                 _time = getSeconds(duration, unit);
                 _timerExist = true;
                 _timerRunning = true;
                 Log("Countdown Timer is set for " + duration + " " + unit + ".");
             }
             catch (Exception e)
             {
                 Log("Error in CreateTimer(): Could not parse with reply.");
             }
         }
    
         /// <summary>
         /// Displays current timer value. It corresponds to "wit$get_timer".
         /// </summary>
         public void GetTimerIntent()
         {
             // Show the remaining time of the countdown timer.
             var msg = GetFormattedTimeFromSeconds();
             Log(msg);
         }
    
         /// <summary>
         /// Pauses the timer. It corresponds to the wit intent "wit$pause_timer"
         /// </summary>
         public void PauseTimer()
         {
             _timerRunning = false;
             Log("Timer paused.");
         }
    
         /// <summary>
         /// It corresponds to the wit intent "wit$resume_timer"
         /// </summary>
         public void ResumeTimer()
         {
             _timerRunning = true;
             Log("Timer resumed.");
         }
    
         /// <summary>
         /// Subtracts time from the timer. It corresponds to the wit intent "wit$subtract_time_timer".
         /// </summary>x
         /// <param name="entityValues"></param>
         public void SubtractTimeTimer(string[] entityValues)
         {
             if (!_timerExist)
             {
                 Log("Error: No Timer is created.");
                 return;
             }
    
             string duration = entityValues[0];
             string unit = entityValues[1];
    
             try
             {
                 _time -= getSeconds(duration, unit);
                 var msg = duration + " " + unit + " were subtracted from the timer.";
                 Log(msg);
             }
             catch (Exception e)
             {
                 Log("Error in SubtractTimeTimer(): Could not parse with reply.");
             }
    
             if (_time < 0)
             {
                 _time = 0;
             }
         }
    
         /// <summary>
         /// Adds time to the timer. It corresponds to the wit intent "wit$add_time_timer".
         /// </summary>
         /// <param name="entityValues"></param>
         public void AddTimeToTimer(string[] entityValues)
         {
             string duration = entityValues[0];
             string unit = entityValues[1];
    
             if (!_timerExist)
             {
                 Log("Timer does not exist. Creating a timer...");
                 CreateTimer(entityValues);
                 return;
             }
    
             try
             {
                 _time += getSeconds(duration, unit);
                 var msg = duration + " " + unit + "were added to the timer.";
                 Log(msg);
             }
             catch (Exception e)
             {
                 Log("Error in AddTimeToTimer(): Could not parse with reply.");
             }
         }
    
         /// <summary>
         /// Returns the remaining time (in sec) of the countdown timer.
         /// </summary>
         /// <returns></returns>
         public float GetRemainingTime()
         {
             return _time;
         }
    
         /// <summary>
         /// Returns the duration in seconds.
         /// </summary>
         /// <param name="duration"></param>
         /// <param name="unit"></param>
         /// <returns></returns>
         /// <exception cref="ArgumentException"></exception>
         private float getSeconds(string duration, string unit)
         {
             int factor = 1;
             if (unit == "second") factor = 1;
             if (unit == "minute") factor = CONVERSION_MIN_TO_SEC;
             if (unit == "hour") factor = CONVERSION_HOUR_TO_SEC;
             if (float.TryParse(duration, out float timeAmount))
             {
                 return timeAmount * factor;
             }
             else
             {
                 throw new ArgumentException("could not parse.");
             }
         }
    
         /// <summary>
         /// Returns time in the format of min:sec.
         /// </summary>
         /// <returns></returns>
         public string GetFormattedTimeFromSeconds()
         {
             return Mathf.FloorToInt(_time / 60.0f).ToString("0") + ":" + Mathf.FloorToInt(_time % 60.0f).ToString("00");
         }
     }
    
    このスクリプトはカウントダウンタイマーを作成します。これはまた、次のビルトインのインテントに対応する公開インターフェイスを提供します。
    • wit/create_timer
    • wit/delete_timer
    • wit/add_time_timer
    • wit/get_timer
    • wit/pause_timer
    • wit/resume_timer
    • wit/subtract_time_timer
  2. [Hierarchy (階層)]ウィンドウで[Canvas] > [Timer Text (TMP)]の順に選択します。[Inspector (インスペクター)]ウィンドウでTimerDisplayコンポーネントを見つけます。このコンポーネントにはTimerと呼ばれるプロパティスロットがあります。TimerGO GameObjectをドラッグしてこのスロットに入れます。
  3. [Hierarchy (階層)]ウィンドウで、[Canvas] > [Log Text (TMP)]の順に選択し、[TimerGO Inspector(TimerGOインスペクター)][Timer Controller (スクリプト)]にある[Log Text Pro]スロットにドラッグして入れます。
  4. [Buzz Sound]プロパティに、好みの音声ファイルを設定します(空のままにすることもできます)。

ステップ3: シーンのアプリ音声エクスペリエンスを有効にする

  1. Unityで、[Assets (アセット)] > [Create (作成)] > [Voice SDK (音声SDK)] > [Add App Voice Experience to Scene (アプリ音声エクスペリエンスをシーンに追加)]の順に選択します。
  2. App Voice Experience GameObjectの[Inspector (インスペクター)]ウィンドウで、アプリ音声エクスペリエンススクリプトを見つけます。[Event(イベント)]セクションに、イベントのリストが表示されます。
    a. [On Response (WitResponseNoe)]イベントの[+]をクリックして、イベントスロットを作成します。Button Label GameObjectをドラッグしてそのGameObjectスロットに入れ、その機能ドロップダウンを[TextMeshProUGUI] > [text(テキスト)]に変更します。そのボックスにActivateを入力します。
    b. 上記のステップを、別のイベントOnErrorについても繰り返します。このイベントに提供されたボックスにも、同様にActivateを入力します。
    c. On Start Listening()イベントについて、[+]をクリックしてイベントスロットを作成し、Status (TMP) GameObjectをドラッグしてそのスロットに入れます。その機能ドロップダウンで、[TextMeshProUGUI] > [text(テキスト)]を選択します。提供された入力ボックスに、Listening…と入力します。再度[+]をクリックして、この特定のイベントに対して別のイベントスロットを作成します。[Button Label(ボタンラベル)]をドラッグしてGameObjectスロットに入れ、[TextMeshProUGUI] > [Text(テキスト)]を選択します。Deactivateと入力します。
    d. イベントOn Stopped Listening()について、[+]をクリックしてイベントスロットを作成し、Status (TMP) GameObjectをドラッグしてそのスロットに入れます。その機能ドロップダウンで、[TextMeshProUGUI] > [text(テキスト)]を選択します。提供された入力ボックスに、Processing…と書き込みます。
    e. イベントOn Partial Transcription()について、[+]をクリックしてイベントスロットを作成し、Status (TMP) GameObjectをドラッグしてそのスロットに入れます。その機能ドロップダウンで、[TextMeshProUGUI] > [text(テキスト)]を選択します。こうすることで、Status (TMP)のテキストが、ユーザーが話している際にそのユーザーが言った言葉に設定されます。
    f. On Full Transcription()イベントについて、[+]をクリックしてイベントスロットを作成し、Status (TMP) GameObjectをドラッグしてそのスロットに入れます。その機能ドロップダウンで、[TextMeshProUGUI] > [text(テキスト)]を選択します。こうすることで、Status (TMP)のテキストが、ユーザーが話し終えた後でそのユーザーが言った言葉に設定されます。
  3. 先に作成してAssetフォルダーに保存しておいたWitConfigファイルに移動します。Unityで、[Wit Configuration(Wit設定)]プロパティを見つけ、そのファイルをドラッグしてこのプロパティのスロットに入れます。
    "Screengrab of the Wit Configuration slot"
  4. WitActivation.csという名前のスクリプトを作成し、App Voice Experience GameObjectに添付します。そのスクリプトに次のコードをコピーします。
     using Oculus.Voice;
     using UnityEngine;
    
     public class WitActivation : MonoBehaviour
     {
         private AppVoiceExperience _voiceExperience;
         private void OnValidate()
         {
             if (!_voiceExperience) _voiceExperience = GetComponent<AppVoiceExperience>();
         }
    
         private void Start()
         {
             _voiceExperience = GetComponent<AppVoiceExperience>();
         }
    
         private void Update()
         {
             if (Input.GetKeyDown(KeyCode.Space))
             {
                 Debug.Log("*** Pressed Space bar ***");
                 ActivateWit();
             }
         }
    
         /// <summary>
         /// Activates Wit i.e. start listening to the user.
         /// </summary>
         public void ActivateWit()
         {
             _voiceExperience.Activate();
         }
     }
    
    このスクリプトによって、ユーザーがスペースバーを押すとアプリ音声エクスペリエンスが有効になります。
  5. Button GameObjectを開いて[Inspector(インスペクター)]ウィンドウが表示されるようにします。Buttonコンポーネントで、On Click ()イベントの[+]をクリックします。App Voice Experience GameObjectをドラッグしてイベントのGameObjectスロットに入れてから、機能ドロップダウンで[App Voice Experience(アプリ音声エクスペリエンス)] > [Activate(有効化)]を選択します。こうすると、ユーザーが[Activate(有効化)]ボタンをクリックして機能を有効化できるようになります。
  6. Response Handler GameObjectをApp Voice Experience GameObjectの子として作成します。これは次のステップで使用します。

ステップ4: アプリ音声エクスペリエンスをタイマーにリンクする

  1. [Meta] > [音声SDK] > [認識ビューアー]の順に選択します。
    "Screengrab of the Understanding Viewer menu"
  2. [階層]ウィンドウでResponse Handler GameObjectを選択し、[発話]ボックスにSet a timer for 10 minutesと入力します。
  3. [Send(送信)]をクリックします。数秒後、インテントと関連するエンティティを含むネストしたデータ構造の形式で結果が返ります。
    "Screengrab of the returned results from the Create a timer for 10 minutes"
    このインテントでは、期間と単位という2つのエンティティを抽出する必要があります。そうするためには、[entities(エンティティ)] > [wit$duration:duration] > [0]を開き、[value = 10]を選択します。この発言から抽出できるエンティティは、10(期間)と分(単位)です。
  4. ポップアップウィドウで、[Add Response Matcher to Wit Response Handler(Response MatcherをWit Response Handlerに追加)]を選択します。
    "Screengrab of the Add response matcher menu item"
    Response MatcherスクリプトがGameObjectに添付されており、表示されるインテントがwit$create_timerであることを確認してください。
  5. [unit = minutes (単位=分)]をクリックします。ポップアップウィンドウで、[Add unit matcher to Response Handler(単位MatcherをResponse Handlerに追加)] > [Handler 1]を選択します。[Wit Response Matcher (スクリプト)]で、[Value Matchers]プロパティのサイズが2になっていることとエンティティ[entities.wit$duration:duration[0].unit]が表示できることを確認してください。
    "Screengrab of the Wit Response Matcher Script window"
    返された結果がインテントと一致している場合、スクリプトは期間と単位をOn Multi Value Event (string[])イベントのサブスクリプション登録者に送信します。
  6. On Multi Value Event (String[])[+]をクリックして、TimerGo GameObjectをGameObjectスロットにドラッグします。機能ドロップダウンで、[TimerController] > [CreateTimer]を選択します。これにより、メソッドがイベントに割り当てられます。
  7. [Oculus] > [音声SDK] > [認識ビューアー]を開いて、アプリをテストします。[発話]ボックスに、Set a timer for 3 minutesと入力します。[Send(送信)]をクリックします。結果は次のようになります。
    "Screengrab of the Timer Debug Log object"

ステップ5: 他のコマンドを実装する

  1. 適用される他の各コマンドについてステップ4の指示を繰り返し、適切なエンティティを抽出しエンティティをそれぞれのメソッドにマップします。
    • Subtract 5 minutes from the timer(タイマーから5分引く) -- 期間と単位エンティティを抽出します。これをTimerController.SubtractTimeTimer()にマップします。
    • Add 5 minutes to the timer(タイマーに5分足す) -- 期間と単位エンティティを抽出します。これをTimerController.AddTimeToTimer()にマップします。
    • Get the time of the timer(タイマーの時間を取得する) -- エンティティ無し。これをTimerController.GetTimerIntent()にマップします。
    • Pause the timer(タイマーを一時停止する) -- エンティティ無し。これをTimerController.PauseTimer()にマップします。
    • Resume the timer(タイマーを再開する) -- エンティティ無し。これをTimerController.ResumeTimer()にマップします。
    • Delete the timer(タイマーを削除する) -- エンティティ無し。これをTimerController.DeleteTimer()にマップします。
  2. Wit Response Matcherの新しいコンポーネントが、次のようにそれぞれのメソッドにフックされることを確認してください。
    "Screengrab of the Wit Response Matcher Script window"
  3. 発言のそれぞれのタイプをテストし、[Unity Console(Unityコンソール)]ウィンドウにそれぞれの出力を取得していることを確認してください。

ステップ6: アプリをテストする

  1. Unityで実行してアプリをテストできます。
    • スペースバーを使用するか[activate(有効化)]ボタンをクリックしてアプリ音声エクスペリエンスを有効化すると、[Status (TMP)(ステータス(TMP))][Listening…(リスニング中…)]に更新されます。
    • マイクに向けて「Create a timer for 5 minutes(5分間のタイマーを作成する)」などと言葉を発して、カウントダウンタイマーがカウントダウンを開始し発言が[Status (TMP)(ステータス(TMP))]に正しく文字化されて表示されているか確認します。
ナビゲーションロゴ
日本語
© 2026 Meta