デコシノニッキ

デコシノニッキ

ホロレンジャーの戦いの記録

MRTKで自作サービスを追加する

MRTKそれ自体はサービスロケータパターンな作りになっており、MRTKではMixedRealityInputSystemやMixedRealityBoundarySystemのようなサービスを自作して、利用することができます。

www.tattichan.work

Extension services | Mixed Reality Toolkit Documentation

サービスを作成する

自作の拡張サービスを作るには、Toolsに含まれるextension service creation wizardを使って作ることになります。

Mixed Reality Toolkit > Utilities > Create Extension Service から順に従って作成します。

f:id:haikage1755:20200704235719g:plain

最初のページでは、Serviceの名前、対応プラットフォームの指定、生成されるスクリプトのネームスペースを入力します。Serviceには重複した名前が指定できません

f:id:haikage1755:20200704235910p:plain:w250

次のページでは、生成されるスクリプトの詳細及びプロファイルの生成先が表示されます。

もしServiceの設定をInspectorをカスタマイズしたければ、Inspectorにチェックを入れましょう。Profileにチェックを入れると、ScriptableObjectが生成されます。設定を差し替えたい場合は作っておくといいでしょう。

f:id:haikage1755:20200705000039p:plain:w250

サービスを追加する

サービスを追加するには、MRTKのProfileのExtensionsに自身で登録する必要があります。

MRTKのProfileからExtensionsを選びRegisterで登録します。

f:id:haikage1755:20200705000914g:plain:w250

サービスを呼び出す

コードから登録したサービスへアクセスするには以下のようにMixedRealityServiceRegistryを通じてアクセスします。
この手法で依存解決を行う場合、このようにMixedRealityServiceRegistryに依存することになります。

INewService service = null;
if (MixedRealityServiceRegistry.TryGetService<INewService>(out service))
{
    // Succeeded in getting the service,  perform any desired tasks.
}

実際に使ってみる

今回は本番ではネットワーク経由で画像をとってきて、開発中の段階ではStreamingAssetsから画像をとってくるというのをやってみます。

まず、PhotoStorageServiceという名前のサービスを作ります。InspectorとProfileは今回は使わないのでチェックを外して大丈夫です。

IPhotoStorageServiceは、呼び出すとどこからかTexture2Dを返してくれる仕様とします。

   public interface IPhotoStorageService : IMixedRealityExtensionService
    {
        Task<Texture2D> GetPhotoAsync();
    }

次にこのサービスを実装した本番用のサービスを作ります。twitterの自分の画像のURLを突っ込んでます。(エラー処理とかはサンプル実装なので省いてます。)

   [MixedRealityExtensionService(SupportedPlatforms.WindowsStandalone|SupportedPlatforms.MacStandalone|SupportedPlatforms.LinuxStandalone|SupportedPlatforms.WindowsUniversal)]
    public class PhotoStorageService : BaseExtensionService, IPhotoStorageService, IMixedRealityExtensionService
    {
        public PhotoStorageService(string name, uint priority, BaseMixedRealityProfile profile) : base(name, priority, profile) 
        {
        }

        public async Task<Texture2D> GetPhotoAsync()
        {
            using (var request = UnityWebRequestTexture.GetTexture("https://pbs.twimg.com/profile_images/1041161611782705157/hj7diH8H_400x400.jpg"))
            {
                await request.SendWebRequest();
                return DownloadHandlerTexture.GetContent(request);
            }
        }
    }

続いて開発中はStreamingAssetsから画像を引っ張ってきたいので、StreamingAssetsに適当な画像を置いて呼び出すモックサービスを作ります。

    [MixedRealityExtensionService(SupportedPlatforms.WindowsStandalone|SupportedPlatforms.MacStandalone|SupportedPlatforms.LinuxStandalone|SupportedPlatforms.WindowsUniversal)]
    public class PhotoStorageServiceMock : BaseExtensionService, IPhotoStorageService, IMixedRealityExtensionService
    {
        public PhotoStorageServiceMock(string name, uint priority, BaseMixedRealityProfile profile) : base(name, priority, profile) 
        {
        }
        
        public async Task<Texture2D> GetPhotoAsync()
        {
            var url = System.IO.Path.Combine (Application.streamingAssetsPath, "sample.png");
            url = "file://" + url;
            
            using (var request = UnityWebRequestTexture.GetTexture(url))
            {
                await request.SendWebRequest();
                return DownloadHandlerTexture.GetContent(request);
            }
        }
    }

これらサービスを実装する上での注意点なのですが、どうやらname, priority, profileをコンストラクタの引数として渡す必要があるようです。

       public PhotoStorageService(string name, uint priority, BaseMixedRealityProfile profile) : base(name, priority, profile) 
        {
        }

最後に、サービスを呼び出すクラスです。Quadにアタッチします。MixedRealityServiceRegistryからIPhotoStorageServiceを取得し、画像の読み込みを行い自身のTextureを差し替えます。

public class TextureViewer : MonoBehaviour
{
    // Start is called before the first frame update
    async void Start()
    {
        if (MixedRealityServiceRegistry.TryGetService<IPhotoStorageService>(out var service))
        {
            var photo = await service.GetPhotoAsync();
            this.GetComponent<Renderer>().material.mainTexture = photo;
        };
    }
}

あとは、サービスを切り替えて動作させてみましょう。

f:id:haikage1755:20200705014220g:plain

まとめ

というわけでMRTKの仕組みでサービスロケータな使い方ができました。ぱちぱち。 まあ取得するサービスは常にSingleなのであまり柔軟な使い方はできない訳ですし、CleanArchitectureライクでレイヤーが深いものをMRTKのサービスに任せるのは大変なので、普通にZenject(Extenject)使った方が良いんじゃない?というツッコミがありそうですが、MRTK単体でもこういったことができるよーという紹介でした。はい。 他にもInitializeやUpdateをoverrideできるので初期化処理や毎フレーム処理とかも組めます。

[デコシノニッキ]は、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、Amazonアソシエイト・プログラムの参加者です。」