仮想と物理とエトセトラ

xRや物理とかごった煮の備忘録的技術ブログ

MRTK(2.7.2)のExampleを試す その1 Demos-Audio

今回は、MRTK ver.2.7.2のExampleを見て、これまであまり使用してこなかったMRTKの機能を確認します。
第一回はDemos-Audioを使用します。

前提

今回の前提は以下です。
Unity 2019.4.17f1
MRTK 2.7.2

準備

まずは、Package ManagerのMixed Realiy Toolkit Examplesでインポートしたいサンプルを選択します。
Demos - AudioImport into Projectを選択しインポートします。
※ 私の場合一度インポートしているので表示はInport againになっています。
f:id:napo909:20211123135238p:plain f:id:napo909:20211123135024p:plain

ProjectウィンドウからAsset/Samples/Mixed Reality Toolkit Examples/2.7.2/Demos - Audio/ Scenesを開くとサンプルシーンを確認できます。
一つずつ確認していきます。

f:id:napo909:20211123135353p:plain

Audio Lo-Fi Effect Examples

このサンプルは、AudioLoFiEffect.csでLo-Fi効果をAudio出力に反映させるサンプルです。

ja.wikipedia.org

赤い球体から音声が出力されており、球体をタップするたびにLo-Fi効果の種類が変化します。
AudioSourceから出力される音声に対して周波数制限を行って、聞こえる音を変更することができます。
このサンプルで使用される主なMRTKコンポーネントMixedRealitySeneContent -> SoundEmitterにアタッチされているAudioLoFiEffect.csです。

f:id:napo909:20211123150230p:plain

f:id:napo909:20211123150329p:plain

AudioLoFiEffect.cs

github.com

AudioLoFiEffect.csではAudioSourceから出力される音声を、ラジオのように音声の周波数範囲を制限することができます。 Inspector上から、周波数範囲制限としてAudioLoFiSourceQualityで定義される下記の5種類を設定できます。

  • NarrowBandTelephony : 300 Hz - 3.4 kHz
  • WideBandTelephony : 50 Hz - 7 kHz
  • AmRadio : 40 Hz - 5 kHz
  • FmRadio : 30 Hz - 15 kHz
  • FullRange : 10 Hz - 22 kHz

依存コンポーネントとしてAudioSource, AudioLowPassFilter, AudioHighPassFilterがアタッチされますが、MRTKで提供されているAudioInfluencerController.csも同時にアタッチすることが推奨されています。

AudioInfluencerController.cs

github.com

AudioSourceの出力に対してMicrosoft.MixedReality.Toolkit.Audio.IAudioInfluencerのアタッチされたコンポーネントを用いて影響を与え、コントロールするためのコンポーネントです。
Inspector上から制御できる要素は下記のとおりです。

  • UpdateInterval : AudioSourceに対して操作をする間隔(デフォルト値 : 0.25 s)
  • MaxDistance : ユーザとIAudioInfluencerを検索するための最大距離 (デフォルト値 : 20 m)
  • maxObjects : 対象とするIAudioInfluencerの最大値 (デフォルト値 : 10個)
  • audioSource : 操作対象となるAudioSourceをアタッチする

AudioOcclusionExamples

このサンプルでは青色の球体から音声が出力されており、青いパネル(左)の前で聞くといつものように、後ろで聞くと若干くぐもった壁にさえぎられているように聞こえます。
このサンプルで使用される主なMRTKコンポーネントは青いパネルであるMixedRealitySeneContent -> SoundOccluderにアタッチされているAudioOccluder.csです。

f:id:napo909:20211123150104p:plain
f:id:napo909:20211123150414p:plain

AudioOccluder.cs

github.com

このスクリプトでは音声に対して、カットする周波数と通す音量の割合を設定して、遮られているかのような効果を音声に与えることができます。
IAudioInfluencerが継承されており、先ほどのAudioInfluencerControllerを用いて音声に効果を与えることができます。
Inspector上から制御できる要素は下記のとおりです。

  • CutoffFrequency : 10 Hz - 22 kHzの間でカットする周波数を設定する。設定値より高音をカットする。(デフォルト値 : 5kHz)
  • VolumePassThrough : 透過する音量を0 ~ 100%の間で設定する。(デフォルト値 : 100%)

TextToSpeechExamples

このサンプルは、先日翻訳機で使用したTextToSpeechを用いたサンプルです。
赤い球体をそれぞれクリックすることで、違う種類の音声ソースでの合成音声を試すことができます。
設定した文字列を合成音声で音声出力させることができます。

f:id:napo909:20211123152017p:plain

TextToSpeech.cs

github.com

このスクリプトでは、TextToSpeech::StartSpeaking(string)メソッドを使用することで、任意の文章(英文)を合成音声にしゃべらせることができます。
Inspector上から制御できる要素は下記のとおりです。

  • AudioSource : 音声出力に使用するAudioSourceを設定します。
  • Voice : TextToSpeechVoiceで定義された4つの合成音声(Default(system voice), David, Mark, Zira)から音源を指定できます。

WindowsMicrophoneStreamDemo

このサンプルでは、マイクの入力の大きさによって、Spatial Awarenessのメッシュの明るさが変化します。

f:id:napo909:20211123153903p:plain

このサンプルでは、特定のMRTKコンポーネントを使用しておらず、MicrophoneAmplitudeDemo.cs中でWindowsMicrophoneStreamからのマイク入力結果を使用して判定しています。

github.com

WindowsMicrophoneStream::Initialize()で初期化し、WindowsMicrophoneStream::StartStream()で録音を開始します。
WindowsMicrophoneStream::ReadAudioFrame()でそのフレームにおける音声情報を取得できます。

MRTKのコンポーネントにフォーカスしたDemos-Audioとしては以上です。
次回は、Demos - Boundaryを取り扱います。

簡易翻訳機を作ってみる その6 ボイスコマンドを使って録音を開始する

今回はその5の続きです。

xr-physics-work-etc.hatenablog.com

せっかく音声入力で翻訳対象となる文字列を入力しているので、録音の開始も音声入力で行えるように設定します。

準備

音声入力でコマンドを実行するために、Speech Commands ProfileInteractableを使用します。

docs.microsoft.com

Speech Commands Profileを設定する

まずはMixedRealityToolkitオブジェクトのMixedRealityToolkitコンポーネントからSpeech Commands Profileを編集します。
InputSpeechからSpeech Commands Profileにアクセスできます。
まずは、現在の設定を修正するためにCloneボタンを押下します。

f:id:napo909:20211114161343p:plain

Cloneボタンを押すと、今のProfileをコピーして新たなプロファイルを作成できます。
Profile Nameから作成するプロファイル名を設定して、Cloneボタンを押します。

f:id:napo909:20211114161517p:plain

新しいプロファイルを作成すると、Speech Commands Profileの中身を修正できるようになります。
Add a New Speech Commandボタンを押すと、新しい設定項目を増やすことができます。

設定項目の内容と機能は以下の通りです。

  • Keyword
    音声入力で使用する語句(実際に発声するワード)

  • LocalizationKey
    入力された文字列をWindows.ApplicationModel.Resources.ResourceLoader中で検索して該当するものを置き換える。
    説明翻訳:ローカライズされたバージョンでキーワードを上書きするために使用するオプションのキー。
    ※ 用途がよくわからない。

  • KeyCode
    Keywordを入力したときと同様の効果を持つキーの設定。
    例) F1を設定しておくと、F1キーを押したときに設定したKeywordが音声入力されたのと同じ動作をする。

  • Action
    発火するActionの設定。MixedRealityInputActionで設定されており、InteractableInput Actionと同じです。

f:id:napo909:20211114162318p:plain

今回は音声入力のため、次のように設定しました。
Lキーを押すことで、UnityEditor上でも動作確認できるはずです。
f:id:napo909:20211114165239p:plain

Interactableを設定する

次に、音声入力で発火対象になるボタンのInteractableに設定を行います。
Speech Commandに先ほど設定したRecordを設定します。 今回は、どの状態でも録音を開始できるように、Requires Focusのチェックを外します。
視線などが当たっている場合のみ音声コマンドを受け付けたい場合は、Requires Focusにチェックを付けます。

f:id:napo909:20211114170625p:plain

動作確認

Project SettingsPlayerPublishing SettingsCapabilitiesMicrophoneが有効になっていることを確認しビルドします。

f:id:napo909:20211114170850p:plain

「レコード」の発声で録音を開始することができました。
現状の問題点としては、入力終了時に結局ボタンを押す必要があることです。
試しに「レコード」を発声しても、翻訳対象の文言として認識されるだけで、select扱いにはなりませんでした。
原因は、Interactableの音声コマンド待ちより、文字入力用のDictationが優先されて音声入力を受けているためと思われます。
対策としては、無音時間○秒で完了とみなす、とかでしょうか。
DictationHandlerでは、無音が一定時間経過するとエラー発生の挙動となっていたので、無音時間で判断するにはDictationHandlerをラップするスクリプトが必要そうです。

MRTK Figma Bridgeを試してみる

今回は以前取り上げたMRTK Figma Toolkitを、Unityに取り込むためのUnity用Figma Bridgeを試してみます。

xr-physics-work-etc.hatenablog.com

MRTK Figma Bridge for Unityは、MRTK FIgma Toolkitで作成したデザインを直接MRTKに取り込むことができるツールです。

docs.microsoft.com

環境

Unity 2019.4.17f1 → 2020.3.16f1
MRTK 2.7.2

準備

Figma Toolkitのインストール

まず、MRTK Figmaの結果をインポートしたいUnityProjectを、Mixed Reality Feature Toolで開きます。

docs.microsoft.com

Discover FeatureMixed Reality ToolkitからMRTK Figma Bridgeを探し、チェックボックスにチェックを入れます。
念のため、インポート済みのMRTK2.7.2にもチェックを付けます。

f:id:napo909:20211107164719p:plain

右下のGet Featuresボタンを押すと、インポートされる内容が表示されます。
必要なものがすべて選択されていれば、Importボタンを押します。
f:id:napo909:20211107164808p:plain

右のFiles to be copied into the projectにMRTK Figma bridgeとMRTK2.7.2が設定されていることを確認し、Approveを選択します。
f:id:napo909:20211107164903p:plain

UnityProjectが更新されるので、Exitボタンを押た後、UnityProjectを開きます。

f:id:napo909:20211107164407p:plain

いくつかエラーが出ているので、エラー内容を確認したところ、Unity2019に存在しないIStyle.textOverflowを使用しているようです。
Figma Bridgeの公式チュートリアルにはUnity2019以降って書いてあったんだけどなぁ
f:id:napo909:20211107165144p:plain

docs.unity3d.com

そのため、Unity2020.3.16f1でMRTK、Figma Bridgeの導入を再度実施しました。
Unity2020.3.16f1で試したところ、今度はエラーが出ずにFigma Bridgeのメニューが選択できるようになりました。
f:id:napo909:20211107171719p:plain

Figmaの内容のインポート

先ほど確認したFugna BridgeのメニューからFigma Bridge Windowを選択するとFigma Tokenを入力するウィンドウが表示されます。
f:id:napo909:20211107171825p:plain

Figma Tokenを取得するために、ブラウザからFigmaのプロジェクト画面を開き、左上メニューからHelp and AccountAccount Settingを選択します。
※アカウント作成が必要です。
f:id:napo909:20211107172114p:plain

表示されたAccount SettingPersonal access tokensのテキストボックスに任意の文字列を入れ、エンターキーを押すとaccess tokenが生成されます。
生成されたaccess tokenをUnityのFigma Bridge WindowFigma Tokenテキストボックスに入力し、Open Fileボタンを押します。
f:id:napo909:20211107172324p:plain

次に、FigmaプロジェクトのURLからIDを取得し入力します。
URLのフォーマットは下記で、[ID]の部分を探し、コピー&ペーストします。
https://www.figma.com/file/[ID]/[Title]
参考: docs.microsoft.com

f:id:napo909:20211107172638p:plain

GetFileボタンを押してしばらくすると、対象のFigmaプロジェクトの名前が表示されます。
目的のプロジェクト同じであれば、Load Fileボタンを押下します。
f:id:napo909:20211107173036p:plain

すると、Figmaプロジェクト中のページが表示され、インポートするページを選択できます。
今回は、作成したExportExampleを選択します。
f:id:napo909:20211107173248p:plain

動作確認

Build Pagesボタンを押すと現在表示中のScene中にFigmaからインポートした内容が追加されます。
ボタンやテキストなども適用されていますが、Menuの背面のプレートは表示されず、MeshRenderer自体もアタッチされていませんでした。
また、ボタンのアイコンも適用されていません。
Figmaプロジェクトのインポート元:
f:id:napo909:20211107174256p:plain
UnityProjectでのインポート結果:
f:id:napo909:20211107173802p:plain

別途元から用意されているComponentsページもインポートしましたが、同様に正しくインポートできるもの/できないものがありました。
インポート元:
f:id:napo909:20211107174840p:plain インポート結果:
f:id:napo909:20211107175048p:plain

原因は不明ですが、MRTK Figma Bridgeはまだベータ版なので正しく動いていないのでしょう。
正しくインポートされるようになれば、デザイン試作から実装までのプロセスがより簡単になりそうです。

簡易翻訳機を作ってみる その5 翻訳結果を音声出力する

今回は前回の翻訳機の続きです。

xr-physics-work-etc.hatenablog.com

今回は、音声読み上げと、別言語を翻訳先とする翻訳を行います。
音声の読み上げでは、TextToSpeechを使用します。
TextToSpeechは、任意の文字列を音声出力するためのコンポーネントです。

docs.microsoft.com

翻訳先の変更は、DeepL APIで翻訳リクエストを行うためのtarget_langを切り替えることで実現します。

準備

音声出力する

まずは、前回のUnityProjectにTextToSpeechコンポーネントをアタッチします。
今回はTranslateResultゲームオブジェクトにアタッチしました。
f:id:napo909:20211031154826p:plain

TextToSpeechコンポーネントをアタッチすると、AudioSourceコンポーネントも一緒にアタッチされます。
TextToSpeechには2つのオプションがInspector上から設定できます。

  • Audio Source
    使用するAudioSourceコンポーネントを設定する。
  • Voice
    音声ソース(しゃべるための音源)を変更する

TextToSpeechコンポーネントのStartSpeaking(string)メソッドを使用することで、任意の文字列を音声出力することができます。

次に、音声出力有効/無効を切り替えるために、トグルボタンを配置します。
新しいトグルボタンをMRTK ToolBoxから選択し、シーン上に配置します。
f:id:napo909:20211031161946p:plain

今回は、アプリ起動時に音声出力を有効にしたいので、InteractableコンポーネントのIsToggledを有効にします。
f:id:napo909:20211031161906p:plain

次に、翻訳結果をTextToSpeechコンポーネントに渡すためのスクリプトSwitchAudioPlaying.csを作成し、Toggleボタンにアタッチします。

  • SwitchAudioPlaying.cs
クリックで展開
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Microsoft.MixedReality.Toolkit.Audio;
using Microsoft.MixedReality.Toolkit.UI;

namespace SimplifiedTranslator
{
    public class SwitchAudioPlaying : MonoBehaviour
    {
        /// <summary>
        /// 翻訳機本体
        /// </summary>
        [SerializeField]
        Translator translator;
        
        /// <summary>
        /// 音声出力用コンポーネント
        /// </summary>
        [SerializeField]
        TextToSpeech textToSpeech;

        /// <summary>
        /// トグル状態取得用Interactableコンポーネント
        /// </summary>
        [SerializeField]
        Interactable interactable;

        // Start is called before the first frame update
        void Start()
        {
            if (interactable == null)
            {
                this.enabled = false;
            }
            translator.Finish_translate_event.AddListener(PlayTranslatedAudio);
        }

        /// <summary>
        /// 翻訳結果出力処理
        /// </summary>
        /// <param name="translateResult"></param>
        void PlayTranslatedAudio(string translateResult)
        {
            if (interactable.IsToggled)
            {
                textToSpeech.StartSpeaking(translateResult);
            }
        }

    }
}

f:id:napo909:20211031162600p:plain

Inspectorから対応するコンポーネントをアタッチすることで、翻訳結果取得時に、トグルボタンが有効な時には音声が出力されるようになりました。

翻訳先言語を変更する

これまでは英語にしか翻訳できなかったため、別言語にも翻訳できるようにします。
DeepL APIでは20種類以上の言語に翻訳することができます。

www.deepl.com

RadioButtonsオブジェクトをシーン上に配置し、InteractableToggleCollectionGridObjectCollectionをアタッチします。
そのオブジェクトの子として任意の数(今回は4つ)のラジオボタンを配置し、InteractableToggleCollectionコンポーネントに登録します。
GridObjectCollectionでは、ボタンを目的の並びになるようにパラメータを設定し、UnityEditor上からUpdate Collectionボタンを用いて整列します。
参考:

f:id:napo909:20211031162934p:plain f:id:napo909:20211031164347p:plain f:id:napo909:20211031163052p:plain f:id:napo909:20211031163113p:plain

次に、それぞれのボタンに言語情報を持たせるためにLangSettingコンポーネントを作成します。
このコンポーネントをそれぞれのラジオボタンにアタッチし、インスペクター上のプルダウンメニューから翻訳先の言語を設定します。

  • LangSetting.cs
クリックで展開
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace SimplifiedTranslator
{
    public enum TargetLang
    {
        JA,
        EN,
        ZH,
        DE,
    }

    public class LangSetting : MonoBehaviour
    {
        [SerializeField]
        TargetLang target_lang;

        public string GetTargetLang()
        {
            return target_lang.ToString();
        }
    }
}

f:id:napo909:20211031163421p:plain

次に、現在ラジオボタンで有効になっている言語を翻訳時に取得するために、GetLangSettingコンポーネントを作成し、その3で作成したTranslateTestを一部修正します。

  • GetLangSetting.cs
クリックで展開
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Microsoft.MixedReality.Toolkit.UI;

namespace SimplifiedTranslator
{
    public class GetLangSetting : MonoBehaviour
    {
        [SerializeField]
        InteractableToggleCollection interactableToggleCollection;

        public string GetLangInfo() 
        {
            Interactable interactable = interactableToggleCollection.ToggleList[interactableToggleCollection.CurrentIndex];

            return interactable.GetComponent<LangSetting>().GetTargetLang();
        }
    }
}
  • TranslateTest.cs
クリックで展開
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SimplifiedTranslator;
using TMPro;

namespace SimplifiedTranslator
{
    public class TranslateTest : MonoBehaviour
    {
        [SerializeField]
        TMP_InputField inputField;

        [SerializeField]
        TextMeshPro result;

        [SerializeField]
        Translator translator;

        /// <summary>
        /// 今回の追加点
        /// </summary>
        [SerializeField]
        GetLangSetting getLangSetting;


        // Start is called before the first frame update
        void Start()
        {
            translator.Finish_translate_event.AddListener(ShowTranslateResult);
        }

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

        }

        public void StartTranslate()
        {
            Debug.Log("Start Translate");
            // 今回の変更点。getLangSettingを使用することで、ラジオボタンで選択された言語をターゲットに翻訳する
            // translator.GetTranslatorResult(inputField.text, "EN");
            translator.GetTranslatorResult(inputField.text, getLangSetting.GetLangInfo());
        }

        public void ShowTranslateResult(string translated_text)
        {
            result.SetText(translated_text);
        }
    }
}

作成した、GetLangSettingコンポーネントRadioButtonsオブジェクトにアタッチし、TranslateTestコンポーネントに参照させます。

f:id:napo909:20211031164158p:plain f:id:napo909:20211031164300p:plain

これで、ラジオボタンを用いて、目的の言語に翻訳させる準備ができました。

動作確認

音声出力はアルファベットの出力の際しか取得できず、ドイツ語も発音が正しいかわかりませんが、英語では正しそうな音声出力結果を取得することができました。
中国語への翻訳の場合は、対応する文字をTextMeshProのフォントに登録していなかったため正しく表示されていません。今後対応する必要があります。

これで、少なくとも日本語→英語への翻訳結果を音声出力することができました。

簡易翻訳機を作ってみる その4 翻訳対象を声で入力する

今回は下記の続きで、前回キーボードから入力していた翻訳元の文字を、音声で入力するようにします。

xr-physics-work-etc.hatenablog.com

前回の記事で、DictationHandlerをつかっては?とのアドバイスをいただいたので、さっそく使ってみます。

bluebirdofoz.hatenablog.com

準備

MRTKのプロファイル設定を行います。
この設定は、MRTK2.7時点でも同じように実施できるため、参考記事の「プロファイルの設定」を参照し実施しました。

設定を完了すると、MixedRealityToolkitコンポーネントは以下のような状態になります。

f:id:napo909:20211024101550p:plain

Dictationのオン/オフはトグルボタンで切り替えます。
そのため、MRTK Toolboxからトグルボタンを選択し、シーン上に追加、任意の場所に移動します。
f:id:napo909:20211024103908p:plain

f:id:napo909:20211024111411p:plain

わかりやすいように、トグルボタンが有効な時はBackPlateが赤色になるようにします。
HolographicBackPlateRecordingStateマテリアルをトグルが有効になった場合に表示されるBackPlateToggleStateオブジェクトの子オブジェクトであるQuadに適用します。

f:id:napo909:20211024103612p:plain

f:id:napo909:20211024103650p:plain

次に実際にDictationを使用するために、DictationHandlerをアタッチします。
今回は、前回翻訳結果表示に使用したTranslateResultオブジェクトにアタッチしました。

音声入力されている場合、途中経過と結果をInputFiledに出力し、音声認識終了後に自動で翻訳されるようにするため、OnDictationHypothesisOnDictationCompleteにInputFIledのTMP_Inputfiled.SetTextWithoutNotifyを設定し、OnDictationCompleteにはTranslateTest.StartTranslateを追加で設定します。
現在の設定だと、誤入力された場合にも翻訳されてしまうので、音声入力の精度によっては翻訳は手動で行ったほうが良いかもしれません。
また、録音時間は規定値の10秒だと短いので、100秒に設定しています。
f:id:napo909:20211024112222p:plain

次に、先ほど追加したトグルボタンのInteractableにDictation開始、終了のためのイベントを設定します。
必要に応じてAdd Eventボタンを使い表示を増やし、Evetn Receiever TypeInteractableOnToggleReceiverを設定します。
そのあと、OnSelectにDictationHandler.StartRecording、OnDelectにdictationHandler.StopRecordingを設定します。
f:id:napo909:20211024115055p:plain

これで、音声認識で入力された文字列を元に翻訳する準備ができました。

動作確認

音声入力での翻訳元文字列入力ができました。
たまに誤認識もありますが、手動で文章も修正できるのでキーボード入力よりは早いです。

今回は短いですがここまで。
次回は翻訳後文章の自動読み上げを試します。

簡易翻訳機を作ってみる その3 HoloLens2からDeepLのWebAPIをつかう

今回は下記の続きで、WebAPIをHoloLens2アプリからたたくことで、翻訳できるようにします。

xr-physics-work-etc.hatenablog.com

1. 準備

前回試したWebAPIをC#からHTTPリクエストすることで使用し、翻訳結果を受け取ります。
今回はHTTPリクエストはUnityWebRequest、レスポンスで得られるJsonを解釈するためにはJsonUtilityを使用しました。

docs.unity3d.com

docs.unity3d.com

  • Translator.cs
クリックで展開
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Events;

namespace SimplifiedTranslator
{
    /// <summary>
    /// 応答用構造体
    /// </summary>
    [System.Serializable]
    public struct translate_results
    {
        public translate_result[] translations;
    }

    /// <summary>
    /// 翻訳結果取得用構造体
    /// </summary>
    [System.Serializable]
    public struct translate_result
    {
        public string detected_source_language;
        public string text;
    }
    
    /// <summary>
    /// 変換後用UnityEvent
    /// </summary>
    [System.Serializable]
    public class TranslatedEvent :UnityEvent<string>
    {

    }

    public class Translator : MonoBehaviour
    {
        /// <summary>
        /// 変換前テキスト
        /// </summary>
        private string pre_conversion_text;

        /// <summary>
        /// 変換対象
        /// </summary>
        private string target_lang;
        
        /// <summary>
        /// 変換後テキスト
        /// </summary>
        private string converted_text;

        /// <summary>
        /// deepLのAutherntication Key
        /// </summary>
        [SerializeField]
        private string deepl_auth_key;

        /// <summary>
        /// deepl API用のURL
        /// </summary>
        [SerializeField]
        private string url = "https://api-free.deepl.com/v2/translate";

        /// <summary>
        /// 変換完了後に発火するEvent
        /// </summary>
        public TranslatedEvent Finish_translate_event = new TranslatedEvent();
        
        public void GetTranslatorResult(string pre_conversion_text, string target_lang)
        {
            this.pre_conversion_text = pre_conversion_text;
            this.target_lang = target_lang;
            StartCoroutine("GetTranslatorResultCoroutine");
        }
        
        /// <summary>
        /// 変換結果取得処理
        /// </summary>
        /// <param name="pre_conversion_text"></param>
        /// <param name="target_lang"></param>
        /// <returns></returns>
        private IEnumerator GetTranslatorResultCoroutine()
        {
            // リクエストを作成
            WWWForm form = new WWWForm();
            form.AddField("auth_key", this.deepl_auth_key);
            form.AddField("text", this.pre_conversion_text);
            form.AddField("target_lang", this.target_lang);

            UnityWebRequest webRequest = UnityWebRequest.Post(this.url, form);

            yield return webRequest.SendWebRequest();

            if (webRequest.isNetworkError || webRequest.isHttpError)
            {
                // エラーの場合はエラー出力
                Debug.Log(webRequest.error);
            }
            else
            {
                // 応答から翻訳結果を取得
                string result = webRequest.downloadHandler.text;
                Debug.Log($"response:{result}");
                translate_results results = JsonUtility.FromJson<translate_results>(result);
                this.converted_text = results.translations[0].text;
                Finish_translate_event.Invoke(this.converted_text);
                Debug.Log($"result:{this.converted_text}");
            }
        }
    }
}

Translator::GetTranslatorResult(string pre_conversion_text, string target_lang)を実行することで、リクエストを実施でき、Translator::Finish_translate_eventにstringを引数とするイベントを登録することで、結果を取得することができます。

Scene上に空のTranslatorGmeObjectを作成し、Translator.csをアタッチします。
Deepl auth keyには、前回取得したDeepLの認証キーを入力します。
f:id:napo909:20211017142921p:plain

次に、動作確認のためのスクリプトを作成しました。
変換対象をENに限定し、InputFileldの内容を入力として、TextMeshProに結果を返す方針としました。

  • TranslateTest.cs
クリックで展開
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SimplifiedTranslator;
using TMPro;

public class TranslateTest : MonoBehaviour
{
    [SerializeField]
    TMP_InputField inputField;

    [SerializeField]
    TextMeshPro result;

    [SerializeField]
    Translator translator;


    // Start is called before the first frame update
    void Start()
    {
        translator.Finish_translate_event.AddListener(ShowTranslateResult);
    }

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

    }

    public void StartTranslate()
    {
        Debug.Log("Start Translate");
        translator.GetTranslatorResult(inputField.text, "EN");
    }

    public void ShowTranslateResult(string translated_text)
    {
        result.SetText(translated_text);
    }
}

次に、空のTranslateResultGameObjectを作成し、TranslateTest.csをアタッチし、下画像のようにScene上にTextMeshProのInputfiled, ラベル用のテキスト、MRTKのボタンを構成します。
f:id:napo909:20211017143146p:plain
f:id:napo909:20211017143216p:plain

作成した各要素をTranslateTestコンポーネントに設定します。
f:id:napo909:20211017143249p:plain

ボタンのOnClick()には、変換開始するためのTranslateTest::StartTranslateを設定します。
f:id:napo909:20211017143430p:plain

これでボタンを押すとInputFiledに入力された文字列を、英語に変換しTextラベルに出力することができます。
なお、日本語を変換する際はInputfieldに日本語表示をできるように設定する必要があります。
設定に必要なFontAssetは、下記を参考に作成できます。

bluebirdofoz.hatenablog.com

2. 動作確認

準備が終わったら、アプリをHoloLensにデプロイします。
アプリを起動すると、先ほど用意したInputFieldに文字を入れてボタンを押すことで英語への翻訳結果を取得できます。

システムキーボードの音声入力で、簡易的に音声入力での翻訳もできるでしょう。
これで、簡単な翻訳機の入り口ができました。

UnityProjectをWebGLでビルドする

今回はUnityで作ったプロジェクト、特にMRTKを用いたプロジェクトをブラウザ上で実行できるようにWebGLでビルドする方法をメモします。

今回のメモでは、PCからアクセスすることを前提として、HoloLensやOculus Questなどでブラウザ上からMR, VR体験できるWebXRは含みませんが、いずれこの方法も調べてメモしたいと思います。
※ これを使えばできそうな気がする

github.com

実行環境

今回は下記環境で動作確認しています。

  • Unity 2019.4.17f1
  • MRTK 2.7.2 Foundation, Extensions, Examples
    (MixedRealityFeatureToolで導入)

準備

今回は、MRTKでよく用いられるHandInteractionExamplesのサンプルを用います。
MixedRealityFeatureToolで導入されたMRTK Examplesの場合、Package Managerから目的のサンプルを追加する必要があります。
Unity上部メニューの「Window」→「Package Manager」から、Package Managerウィンドウを開きます。

f:id:napo909:20211003171220p:plain

Package Managerの上部を「In Project」に変更したうえで、Mixed Reality Toolkit Examplesから「Demos - HandTracking」を選択します。

f:id:napo909:20211003172111p:plain

選択すると、Assets中にサンプルが導入されるので、HandInteractionExamplesを探し、ダブルクリックして選択します。

f:id:napo909:20211003172149p:plain

次に、ブラウザ上でサンプルを操作できるようにMRTKの設定を修正します。
Hierarchy上のMixedRealityToolkitからMRTKの設定ProfileをCloneでコピーします。
そのあと、Inputの設定もコピーして内容を変更可能にして、UnityEditor上での動作確認で使用するInput Simulation ServiceSupported Platform(s)Webを追加します。
追加することで、Webブラウザ用にビルドした際にもWASDで移動やマウスでのハンドの操作を行えるようになります。

f:id:napo909:20211003172804p:plain

これでSceneの設定ができました。

WebGLをビルド対象にする

Build Settingsからビルドを行います。
今回はWebブラウザ用のビルドのため、PlatformをWebGLにしてビルドを行います。
まだModuleを導入していない場合は、Build SettingsのInstall with Unity HubからModuleの導入を行います。
導入を行うと、 右下のSwitch Platformが選択できるようになるので、Platformとして設定します。
Switch Platformをした後、MRTK Project Configuratorが出る場合は、Legacy XRなど目的の設定を行います。
そのあと、Buildボタンがアクティブにならない場合は、一度UnityProjectを開きなおすとビルドできるようになります。

f:id:napo909:20211003173746p:plain

ビルドして動作確認する

ブラウザ上で動作確認をするために、今回はBuild And Runを選択します。
ビルドしたものの配置先を選択すると、ビルドが始まります。

f:id:napo909:20211003174327p:plain

ビルドが終わると、ブラウザが開きUnityで作成したコンテンツがブラウザ上で確認できます。

f:id:napo909:20211003180021p:plain

右下の矢印ボタンで全画面表示できます。
設定でInput Simulation ServiceをWeb上で使用できるようにしたため、WASDで移動したり、左Shiftキーで手を出したりして操作することもできます。
f:id:napo909:20211003175408p:plain

なお、ビルドで生成されたindex.htmlを用いてローカルから開こうとしても下記メッセージが表示されてブラウザから閲覧することはできません。
f:id:napo909:20211003180245p:plain

ビルド時にBuild And Runを用いるか、Webサーバにアップロードして閲覧するしかないようです。

Unity公式ドキュメントによると、Task処理が使えなかったり、HTTP, HTTPS通信に外部との通信が限定されることなど制約もありますが、サーバ上にビルド内容をアップロードすることで、手軽にUnityの成果物を共有できることは種々役に立ちそうです。
docs.unity3d.com