yotiky Tech Blog

とあるエンジニアの備忘録

HoloLens で Managed Plugin を実装する際の落とし所

HoloLens でプラグインを作成する理由はいくつかありますが、ひとつは「共通機能をモジュール化してプロジェクト間で共有する」、もうひとつは「UWPで使えるFramworkのAPIを頻繁に使う」あたりが大きいかなと思います。
プラグインの作り方はググれば出てきますが、楽をするためにプラグインを選んだもののその縛りのキツさに大体皆さん悶絶しながらコードを書いているのが垣間見えます。。
そこで個人的な「めんどくさい」をできるだけ排除して、現時点でベターな実装の落とし所をまとめてみようかと思います。

目次

先人の知恵

HoloLens でプラグインを作るのに、以下のサイトを参考にさせて頂きました。

blog.okazuki.jp

更にその中で紹介されているこちらのサイトもとても参考になります。

satoshi-maemoto.hatenablog.com

blog.d-yama7.com

何をやってるか簡単に説明すると、
HoloLens は UWP のプロジェクトなので UWP 用のライブラリ(.NET Core Framework)を使う必要があり、 一方 Unity Editor 上では .NET Framework 3.5 互換のライブラリを使う必要があります。
インターフェイスが全く同じ2つのライブラリをそれぞれの環境でスイッチさせて読み込むことで辻褄を合わせプラグインを利用できるようになっています。

先に挙げたサイトの手順を参考に作って頂ければ間違いないです。 (執筆時点) 間違いないです、、、ないのですが、サイトの中でも苦悩されているように、
「同じインターフェイスの UWP 用 / Editor 用の dll を出力する必要がある」という特殊な事情のため、その設計をどうするかはとても悩ましい問題です。

問題点

個人的に悩ましい問題は以下の点です。

  • Editor 用のコードがお荷物になる
    • 仕様に対して実装が2重管理となり、手の混んだことをすればするほど Editor 用のコードのメンテがめんどくさい
  • 共有プロジェクトや partial クラスを使うと見るべき箇所が増える
    • 特にコード書き始めの初期の段階では実装、リファクタリングの対象が複数箇所に散らばっていてファイルを行き来したりめんどくさい
  • 条件付きコンパイル地獄

結論、めんどくさいことが多い!

どうしたか

そんな問題を踏まえ、現時点での実装の落とし所はこうなりました。
もちろん正攻法な設計が必要な場面もあるので、この手抜き設計が無条件に適用できるわけではありません。

こちらが簡単なサンプルコード。

namespace HelloWorldPlugin
{
    public abstract class MessageBuilderBase
    {
        public virtual string Create(string name) => name;
    }

#if !NETFX_CORE
    // for Editor
    public class MessageBuilder : MessageBuilderBase { }
#else
    // for UWP
    public class MessageBuilder : MessageBuilderBase
    {
        public override string Create(string name)
            => $"Hello {name}. Enjoy your HoloLens!";
    }
#endif
}

コード量が少なければ一つのファイルにまとめてしまいます。
コード量が多くなってくれば、ベースクラスと Editor 用のクラスを一つのファイルに、 UWP 用のクラスを一つのファイルにし、計2つのファイルで管理することが多いです。

f:id:yotiky:20180425034348p:plain

ポイント1 抽象クラス

まず大前提として Editor 用のコードは捨てます。これは問題に挙げた通りメンテしたくし実機で確認しないとわからないこと多いから。
ベースクラスは abstract で定義し、インターフェイスとなるメソッドを virtual で定義することで、共通のインターフェイスとします。
また virtual で定義するので、Editor 用クラスの中身は必要がない限りは override 不要で、最小限のコードでダミークラスを作れます。

ポイント2 UWP 用のクラス

#if をクラス毎囲ってしまうことで、メソッド内に条件付きコンパイルが混在することがなくなりコードが読みやすくなります。 インテリセンスも効くので心置きなくコーディングに専念できます。

ポイント3 csファイル(物理)を分けすぎない

コードの書き始めなどインターフェイスや実装自体が猛スピードで変化します。
ファイルを分けると、2つや3つ(またはより多く)のタブを行ったり来たりしながら修正しなければなりません。
コード量が多くなってくれば個別に分けることもありますが、少ないうちは1つのファイルにすべて書いてしまいます。

補足

この方法に限ったことではありませんが、csファイルはリンクとして追加されているため、どちらのプロジェクトとして開いているかによってビルド条件が変わってきます。 他方のコードはグレーアウトするため、適宜開き直して修正することでインテリセンスの恩恵を受けることもできます。

f:id:yotiky:20180425034352p:plain

なお、プロジェクトの設定やビルド、Unity での設定などは参考にさせて頂いたサイトと同じ内容ですので是非そちらを参考にしていただければと思います。

まとめ

個人的に今ベターだと思う Managed Plugin の実装の落とし所をまとめてみました。