以下のようなWindowsアプリケーションから、UnityアプリケーションをNamedPipeを介して制御する方法を紹介する。なおソースコード全体はGitHubのリポジトリに置いてある。
Windows側
Unity側にメッセージを送るときには、NamedPipeのクライアントであるIPCクラスを利用する。以下では、ViewにバインドされたプロパティでUnity側にメッセージを送っている。Send
はUnity側からの応答を返す。
Unity側
まず第一に、NamedPipeをUnityで利用するには、Player SettingsでApi Compatibility Levelを.NET 4.xにする必要があることに注意してほしい。エディターのプレイモードなら.NET Standard 2.0でも動くが、standalone playerでエラーになってしまう。

Unity側ではこちらのIPCクラスを使用する。以下にNamedPipeの待ち受けを行うReceive
メソッドの定義を示す。
UniTaskを用いた非同期メソッドになっている。コルーチンよりもUniTaskのほうがずっと簡単に非同期操作を実装できる。ここでNamedPipeの非同期系(~Async)のメソッドを用いていないのは、Unityには非同期系のメソッドが実装されていないからだ。コンパイルは通るが実行時にエラーになるので注意してほしい。
以下は、Windowsからのメッセージを処理するControllerクラスである。すべてのシーンに空のGameObjectを配置して、Controllerオブジェクトをコンポーネントとして張り付ける。
Awake
でControllerをDontDestroyOnLoad
を使用して、シーンが変わったときにControllerが破棄されないようにしている。そうしないとLoadScene
でシーンを切り替えた後にWindows側に応答を返せなくなる。Start
ではメッセージのハンドラーを与えてIPC.Run
を実行する。
OnDisable
では以下に実装を示したIPC.Close
を呼んでいる。IPC.Close
は、シーンの切り替え中はパイプが接続中なので何もせずに終了する。
それ以外は自身のNamedPipeに接続して、IPC.Receive
の待ち受け状態を解除する。エディターのプレイモードで実行したときに、そうしないと待ち受けスレッドが残り、終了時とコードリロード時にエディターがフリーズする。
上で紹介した2つのIPCクラスは私が独自に実装したものだ。UnityでNamedPipeを使う記事はどれも実験レベルで、現実のアプリケーションで問題なく動くように作られていなかったので、自分で実装することにした次第である。