developerWorks記事「Watson×Unity！初心者でもできる、VR 空間で Unity ちゃんとおしゃべりアプリ！」を試してみる

IBM の [developerWorks](https://www.ibm.com/developerworks/jp/) サイトの記事 [Watson×Unity！初心者でもできる、VR 空間で Unity ちゃんとおしゃべりアプリ！](https://www.ibm.com/developerworks/jp/cloud/library/unity/) を現時点(2018年4月15日)の最新環境(バージョン)で試してみましたので、メモを公開します。

最近、Unity の勉強を始めたので、Watson の復習も兼ねてのトライです。

# Unity ちゃんのデータをゲット

私の使用している Unity のバージョンは 2017.4.0f1 で、Windows 10 64bit版で動作しています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/a50268be-fef8-9173-5c10-aa4c6ec096ef.png)

Unity-chan を Asset store からインポートして、シーンに表示するまでは簡単でした。最初は「ダウンロード」ボタンが表示されていましたが、一度クリックしてダウンロードすれば「インポート」ボタンに変化して後は説明どおり。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/4379b2e2-da91-be86-791b-caa5b66be716.png)

シーンに Unity-chan を表示したところで先に進みます。

# UnitySDK のダウンロード

[Unity-SDK](https://github.com/watson-developer-cloud/unity-sdk#ibm-watson-services) のバージョンは [v2.2.1](https://github.com/watson-developer-cloud/unity-sdk/releases/tag/2.2.1) とだいぶ変わっているようです。

ダウンロードして解凍し、unity-sdk-2.2.1 というフォルダ名を watson に変更し、Project ビュー内の Assets にコピー。2つほど警告が出ていますが…

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/399beea9-d2fa-4d26-b179-ec262a32f110.png)

クリティカルな場所でないのと、never used 警告なので、とりあえずはスルーしておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/2635f1fe-5222-3b68-8c89-f19c8ad8a0e3.png)

これで準備は完了ってことで、先に進んでみましょう。

# Speech to text を Unity に適用

カタログの中から、「Speech To Text」を見つけ作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/f3cb0ca0-3e37-a75f-118e-2d8f68fdebea.png)

参照: [Speech To Text APIの日本語説明ページ](https://www.ibm.com/watson/jp-ja/developercloud/speech-to-text.html)

そして「資格情報」をコピペして Unity に戻りますが…

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/f8905d40-3c1f-98a7-44aa-b55264bf944a.png)

あれ？メニューが全然足りない？

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/cbf51ad3-8889-4e6f-264b-928d01777303.png)

これじゃ先に進めませんね。

# UnitySDK のバージョンを変えてみる

「Watson APIs Unity SDK Configuration Editor 出ない」でググってみると、以下の記事が見つかります。

 [UnityとWatsonをつないで、VR空間でAIと会話できるようにしてみた](https://qiita.com/yukinaco/items/fe64ac9d227ec6d15df7)

Unity に関してと
> 2017年12月時点では、最新版は不具合が起こり得るため、過去バージョンの2017.2.x版を推奨らしいです

Unity SDK に関して

> 現在(2017年12月)最新版であるv2.0.0で実施したところ、メニューバーに現れるはずのボタン(Configuration Editorなどの便利ボタン)が出ないという現象が起きました。
> 調べたところ、v1.0.0以降では出なくなってしまったようです。

これらから考えるに、バージョンアップによる不整合(不具合)が発生していて、どちらか、もしくは両方のバージョンを戻さないと問題の解決は難しそうです。

できれば Unity の再導入は避けたいので、まずは [Unity SDKのリリース履歴](https://github.com/watson-developer-cloud/unity-sdk/releases/) を眺めてみます。

で、だいぶ戻した　v1.0.0 で試しましたが、結果は v2.2.1 と同じ。メニュー項目 Configuration Editor が出ませんでした。

そこで記事と同じ v0.13.0 で試したところ、コンソールに警告ではなくエラーが表示されてしまい、元の Watson メニューすら表示されませんでした。新しい Unity に対応できていない様子。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/3a307c43-c8f9-d0c1-464b-742c8d218fce.png)

あかん、これ面倒なやつや…

# UnitySDK v2.2.1 を眺めてみる

ここで、ふと疑問に思ったんですよ。

> v1.0.0 以降、Configuration Editorなどメニューからごっそり消えているけど、コンソールにエラー出ていないよね

ってことに。なので UnitySDK の中身をみていくと、Unity にメニューを追加する Scripts/Editor の中身がごっそり消えていることに気がつきました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/65769cff-1591-b279-0b66-6fc3430e6665.png)

左が v2.2.1 で、右が v0.13.0 です。ここだけでなく、全体を見回しても ConfigurationEditor.cs が見当たらないんですよ。もしかして、削除されたんと違うか？

んで、GitHub の UnitySDK ページ の README.md を読んでいくと、[Authentication](https://github.com/watson-developer-cloud/unity-sdk#authentication) セクションには Credentials インスタンスを生成するコードだけが記載されていて、Configuration Editor の説明はない。

ということで、便利ツールである Configuration Editor を使わず、利用するコード側で Credentials 管理をするのが v1.0.0 以降のお作法であり、正解なのかな？が私の結論です。

# というわけで対応策としては

まず、元のページにある Configuration Editor の部分はさくっとスキップします。

そして最初のサンプルコードの最初に以下の using 文を追加し

```cs:SampleSpeechToText.cs
using IBM.Watson.DeveloperCloud.Connection;
using IBM.Watson.DeveloperCloud.Utilities;
```

最初のほうにある m_SpeechToText の初期化部分を以下のように変更します。

```cs:SampleSpeechToText.cs
//private SpeechToText m_SpeechToText = new SpeechToText();
private SpeechToText m_SpeechToText;
```

インスタンスを new している部分を消したので、代わりに、IEnumerator Start() メソッドの先頭に以下のコードを追加します。

```cs:SampleSpeechToText.cs
string cre_id = "4934xxxx-xxxx-xxxx-xxxx-xxxxxxxxxx99";                  // 資格情報より
string cre_pw = "MxxxxxxxxxxE";                                          // 資格情報より
string cre_url = "https://stream.watsonplatform.net/speech-to-text/api"; // 資格情報より
Credentials credentials = new Credentials(cre_id, cre_pw, cre_url);
m_SpeechToText = new SpeechToText(credentials);
m_SpeechToText.Keywords = new string[] { "ibm" };
m_SpeechToText.KeywordsThreshold = 0.1f;
```

cre_id, cre_pw, cre_url は Watson でサービス登録した際に表示された「資格情報」をみて設定してください。

Keywords と KeywordsThreshold は空のままだとWatson API 呼び出し時にエラーになってしまうようで、適当な値を設定してあります。

そして肝心の Recognize 関数も引数が変わっているようなので、以下のように書き換えて…

```cs:SampleSpeechToText.cs
//m_SpeechToText.Recognize(HandleOnRecognize, audioSource.clip);
m_SpeechToText.Recognize(HandleOnRecognize, OnFail, audioSource.clip);
```

不足している OnFail 関数を新たに定義してあげます。

```cs:SampleSpeechToText.cs
private void OnFail(RESTConnector.Error error, Dictionary<string, object> customData)
{
    Debug.Log("SampleSpeechToText.OnFail() Error received: " + error.ToString());
}
```

デバッグのため SpeechToText.cs の中身読んだり、対応は意外と大変でした… が、原因がわかれば対応は見えてきます。

さあ、これでやっと最初のサンプルコードが動きます。Unity に戻って実行してみましょう！

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/513c1dcf-825c-081a-a1a6-8f931747812b.png)

ふう、動いてよかったっす…

# 最初のサンプルコード

書き換えた最初のサンプルコードの最終版を掲載しておきます。私が修正で加えたコードの著作権は放棄しますので、コードの扱いは元の [developerWorks 記事](https://www.ibm.com/developerworks/jp/cloud/library/unity/) と同じでかまいません。

```cs:SampleSpeechToText.cs
//SampleSpeechToText.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using IBM.Watson.DeveloperCloud.Services.SpeechToText.v1;
using IBM.Watson.DeveloperCloud.Connection;
using IBM.Watson.DeveloperCloud.Utilities;

public class SampleSpeechToText : MonoBehaviour
{

    [SerializeField]
    private AudioClip m_AudioClip = new AudioClip();
    //private SpeechToText m_SpeechToText = new SpeechToText();
    private SpeechToText m_SpeechToText;

    // Use this for initialization
    IEnumerator Start()
    {
        string cre_id = "4934xxxx-xxxx-xxxx-xxxx-xxxxxxxxxx99";                  // 資格情報より
        string cre_pw = "MxxxxxxxxxxE";                                          // 資格情報より
        string cre_url = "https://stream.watsonplatform.net/speech-to-text/api"; // 資格情報より
        Credentials credentials = new Credentials(cre_id, cre_pw, cre_url);
        m_SpeechToText = new SpeechToText(credentials);
        m_SpeechToText.Keywords = new string[] { "ibm" };
        m_SpeechToText.KeywordsThreshold = 0.1f;

        // 音声をマイクから 3 秒間取得する
        Debug.Log("Start record"); //集音開始
        var audioSource = GetComponent<AudioSource>();
        audioSource.clip = Microphone.Start(null, true, 10, 44100);
        audioSource.loop = false;
        audioSource.spatialBlend = 0.0f;
        yield return new WaitForSeconds(3f);
        Microphone.End(null); //集音終了
        Debug.Log("Finish record");

        // ためしに録音内容を再生してみる
        audioSource.Play();

        // SpeechToText を日本語指定して、録音音声をテキストに変換
        m_SpeechToText.RecognizeModel = "ja-JP_BroadbandModel";
        //m_SpeechToText.Recognize(HandleOnRecognize, audioSource.clip);
        m_SpeechToText.Recognize(HandleOnRecognize, OnFail, audioSource.clip);
    }

    void HandleOnRecognize(SpeechRecognitionEvent result, Dictionary<string, object> customData)
    {
        if (result != null && result.results.Length > 0)
        {
            foreach (var res in result.results)
            {
                foreach (var alt in res.alternatives)
                {
                    string text = alt.transcript;
                    Debug.Log(string.Format("{0} ({1}, {2:0.00})\n", text, res.final ? "Final" : "Interim", alt.confidence));
                }
            }
        }
    }
    private void OnFail(RESTConnector.Error error, Dictionary<string, object> customData)
    {
        Debug.Log("SampleSpeechToText.OnFail() Error received: " + error.ToString());
    }

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

    }
}
```

# というわけで

最初のサンプルだけで長くなってしまったので、とりあえず今回の記事は終わりにします。動かなかった原因と、対応するためのコードがわかりましたので、以後もこの要領で対応していけるのではないか？とおもいます。

この先も自分で試すと思いますので、何か面白い点がありましたら後半もメモ作成するかもしれません。また、もしご要望でもあれば記事書くかもしれません。時間が欲しい…

【追記】残りの2つのAPI用のサンプルも修正したので [続きのページ](https://qiita.com/yamachan360/items/bcfa10b2d9bf62df8300) でコードを公開しました。

ではでは。