VRChatと外部世界を繋ぐインターフェース

## はじめに

こんばんは。初めてAdvent Calendarに投稿します、じぇしか(@jscmla1118)です。この記事は、[VRChat Advent Calender 2018](https://qiita.com/advent-calendar/2018/vrchat "VRChat Advent Calender 2018")の16日目の記事です。

本日は**「VRChatと外部世界を繋ぐインターフェース」**についてご紹介します。

さて、この記事を見ている方のほとんどは、日常的にVRChatを利用している人が多いかと思います。私はどうかというと、週1日にログインできるか否かといった感じです。毎日ログインしている人を見ていると羨ましく感じることもあるのですが、その半面、「そもそも何でVRChat内でコミュニケーションが完結しちゃうの？」というようなことをずっと思っていたりします。そんな疑問が発端となって、VRChatから外部へ、またはその逆向きで影響を及ぼす方法はないのかと模索した結果、それらを実現できる様々なインターフェースが存在することを知りました。

そして、それらをちょこちょこ使ってみたところ、ある程度制限はあるものの想定していた以上に出来ることが多いことに気がつきました。そこで本日は、このインターフェースの概要と用途について語ってみようと思った次第です。

## インターフェースの分類
それでは、インターフェースの紹介にあたって、まずは分類を定義してやります。VRChat外部の世界をOthersとした場合、インターフェースの分類は下記のように定義できます。

- V2V: VRChat to VRChat
- V2O: VRchat to Others
- O2V: Others to VRChat

大半のインターフェースはVRChat内に閉じたもの、すなわちV2Vのものが多くみられます。そもそもVRChat自身がコミュニケーションのプラットフォームなので、これは当然でしょう。一方で、VRChatの外側でトリガーを引いてVRChat内でアクションを実行したり、その逆を実現する仕組みもいくつか存在します。

私が把握しているインターフェースを、下記にまとめました[^1]。これらについて、次項より詳細を紹介していきます。

| インターフェース名 | 分類 | ターゲット           |
|------------------|-----|----------------------|
| VRC_Panorama     | V2V | ワールド              |
| VRC_MidiNoteIn   | O2V | インスタンス(ローカル) |
| VRC_OscButtonIn  | O2V | インスタンス(ローカル) |
| Cortana          | V2O | 外部(任意のプログラム) |
 
## VRC_Panorama
これは比較的メジャーなインターフェースですね。VRChat SDK上ではアクションとして設定できます。あらかじめURLを設定しておくと、アクションが実行されるタイミングでURLにアクセスして画像を取得、テクスチャとしてマッピングできます。都度、外部のサーバにアクセスするため動的にテクスチャを適用でき、かつてのWebPanelの代替手段として使用されるケースが多い印象です。
![スクリーンショット 2018-12-15 08.44.55.png](https://qiita-image-store.s3.amazonaws.com/0/176258/3741e134-4a33-d4ff-4677-ef83606d68f8.png)

また、複数のURLも設定可能です。uGUIなどから指定の関数を叩くと画像を切り替えることも可能ですが、今回のテーマからは逸れるので、ご紹介は割愛します。

### 処理の分化
このVRC_Panorama、ざっくり言えば画像を取得するためのインターフェースなのですが、実質的には2つの処理に分けられます。
- **指定のURLにアクセスする**
- **画像を受け取って、テクスチャとして貼り付ける**

全体で見ればV2Vに分類できるのですが、分割してしまえば前者をV2O、後者をO2Vとしてみなすことができます。

さてさて、VRC_Panoramaには**指定のURLにアクセスする**という処理が含まれているのですが、これはとても有用な機能となります。何ができるようになるかというと、URLアクセス先となるサーバ側に処理を実装してやれば**任意のプログラム**を動かすことができます。細かなパラメータを渡すことは難しいですが、例えばRESTful APIサーバを構築してやれば、VRChat内のユーザ動作に応じてAPIを叩くことが可能です。

### Googleドライブの連携
このVRC_Panorama、大半の方が使っているであろうGoogleドライブとの相性がとても良いです。Googleドライブにはワークシートやスプレッドシート、画像などをアップロードできますが、これらのファイルには、**一意なURL**がアサインされます。それをVRC_Panoramaにセットすることで、簡単に好きな画像を設定できるようになります。
![スクリーンショット 2018-12-16 00.36.07.png](https://qiita-image-store.s3.amazonaws.com/0/176258/e2e28e55-2751-d1b2-15db-0c39c10af9ad.png)

またGoogleドライブ上では、拡張子を含むファイル名が一致していれば同じファイルとして扱われるので、画像を上書きしても共有URLは維持されます。この性質を利用すれば、管理者の好きなタイミングでワールド上に存在するオブジェクトのテクスチャを差し替えることも可能です。
![DrKgAodUcAA3CJe.jpg](https://qiita-image-store.s3.amazonaws.com/0/176258/34bcea88-7fec-3ca7-b426-74f9de8cc95e.jpeg)

### Google Apps Scriptの連携
もう一歩、踏み込んでみます。私も最近知ったのですが、JavaScriptベースの言語である**GAS: Google Apps Script**が、Googleドライブ上に用意されています。このスクリプト、Googleドライブ上で処理を記述すれば、**URLアクセスをトリガーに実行**させられます。これによって、例えば、VRChatからURLアクセスが来たらGoogleドライブ上の画像をランダムに選択して、それをVRChatに返すなんて処理が書けたりします。またGoogleスライドの自動編集とサムネイル化を組み合わせれば、動的な画像生成も可能になるようです。Googleフォームに入力した情報をGoogleスプレッドシートに吐かせておいて、URLアクセス時にグラフ画像を返すようにすれば、リアルタイムアンケートなんてものも作れたりすると思います。

さらには、GAS上で別のAPIを叩くなんてことも出来たりします。あらかじめ用意した文書にはなりますが、Twitterと連携させてしまえばVRChatからツイートすることも可能だったりします。

なぜ他のスクリプトではなく、GASを推しているのかというと、サーバに詳しくない人でも扱いやすいためです。GASと同じようなことを自前でやろうとすると、先にローカルPCやクラウド上にサーバを立てて、HTTPリクエストを捌く処理をゴリゴリ書いてやらないといけません。しかしGASの場合はそんなことを気にする必要がなく、リクエストが来た時点で実行したい処理だけを書けばOKです。なので、先人の書いたコードをコピペするだけでも何とかなりやすいので、とりあえずVRChat向けにプログラミングを試してみたい！という方にはオススメです。

## VRC_MidiNoteIn
VRC_MidiNoteInは、その名の通りMIDIデータを受け取るインターフェースです。MIDIデータは「Channel」と「Note(音階)」パラメータを含んでいます。VRChat側では、受け取ったパラメータに応じてアクションを実行させられます。
![スクリーンショット 2018-12-16 10.17.41.png](https://qiita-image-store.s3.amazonaws.com/0/176258/e2ce97ed-ecce-a077-db49-4f5408e82410.png)

例を挙げるなら、ピアノ鍵盤型MIDIコントローラから押したキーを受け取って、そのキーに応じた音を鳴らす、なんて用途が一般的でわかりやすいですね。音を鳴らす以外のアクションも可能で、アニメーションを再生するなんてことも可能です。DTM界隈なら普及しているプロトコルなので、MIDI対応したハードウェアさえ用意してやれば、比較的簡単に取り扱うことが出来ると思います。

## VRC_OscButtonIn
これは、OSCを扱うためのインターフェースです。もともとOSCは、MIDIと同じく音楽データ用途で開発されたのプロトコルです。ただ、OSCメッセージはシンプルながら汎用性のある構造となっているので、扱い方さえ身につけてしまえば何でもできるようになります。

VRChatアプリケーションを起動すると、9000番ポートにて常にOSCメッセージを待ち受けるようになります。VRC_OscButtonInが設定されたインスタンスにインした上で、OSCメッセージを外部から送りつけてやれば反応します。

### OSCとは
そもそもOSCを知らない人が多いかと思いますので、概要を紹介します。OSCは、「アドレス」と「引数」の2つの要素で構成される、UDPベースのプロトコルです。いずれも、任意の値を設定可能です。
![image07.png](https://qiita-image-store.s3.amazonaws.com/0/176258/3f460dba-1d8a-6b71-7762-c6fc6ade8cd6.png)

VRChat側では、トリガーとしてOscButtonInが用意されており、Unity側で特定のアドレスを予め設定してやれば、そのアドレスを含むOSCメッセージを受信したタイミングでトリガーを引くことができます。ただし受け取れる引数に応じて取り扱いが変わります。VRC_ButtonInの場合、0ならOff、それ以外ならOnというようにBooleanに落とし込まれます。そのため、1つのOSCメッセージで出来ることはOn/Offのスイッチングのみとなります。このアドレスは自由に決めることができるので、バリエーションを用意さえしてやれば多数のアクションと組み合わせることができます。
![image05.png](https://qiita-image-store.s3.amazonaws.com/0/176258/29314da2-aa79-3bca-f154-e2e26aa13432.png)

OSCのよさは、**柔軟性**と**伝達速度**にあります。VRChat側は「OSCメッセージが届いたらトリガーを引く」ことさえ意識していればよいので、メッセージの生成と送信というところに落とし込めるのであれば、そこに至るまでの処理は外部でいくらでも自由に実装できます。またこのOSCメッセージ、数ms単位で送信可能です。言い換えると、手動では不可能な速さで多数のトリガーを引くことができます。

### 複数Bitデータとして扱う
OSCメッセージはそれなりに高速で伝送できるので、何ら違和感なく、複数のメッセージをひとまとまりで処理できます。例えば、8つの異なるOSCメッセージ連続で送信し、それをOscButtonInで受け取った後に8Bitのデータとして複合してやれば、0-255の数値としてみなすことができます。これを利用すれば、OSCメッセージでオブジェクトの色をコントロールできたりします。
![image01.jpg](https://qiita-image-store.s3.amazonaws.com/0/176258/5b598f38-4955-a20d-902d-11b21229218d.jpeg)

下記の例では、スマートフォン上で設定したRGBのR値(8Bit)を8つのOSCメッセージに変換し、受信したVRChat側にて、アニメーションを使用して復号しています。このようにデータをまとめて扱えば、任意の数値を自由に表現できるようになります。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">スマホからVRChatワールド上のオブジェクトの色を任意の値(Rのみ0-255)で変えられるようになりますた。デモはショボいけど、インターフェースさえ作ってやれば、スマホに限らず何からでもいけるはず。Arduinoとか。リアルタイムでどこまでいけるのかは、要検証ですです。
<a href="https://t.co/jSteEgNiH5">pic.twitter.com/jSteEgNiH5</a></p>&mdash; じぇしか@VRChat (@jscmla1118) <a href="https://twitter.com/jscmla1118/status/1049319249615835137?ref_src=twsrc%5Etfw">2018年10月8日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

自由が利くとは言え、それなりに注意点はあります。まずOSCはUDPベースのプロトコルであり、VRChat側のフレーム更新タイミングと同期していません。送信中にフレーム更新が入ってしまうと、中途半端な状態が反映されてしまいます。なので複数Bitデータとして扱う場合は、更新用のトリガーメッセージを1つ付け足し「そのメッセージを受信したらオブジェクトに対して一斉に反映させる」といった工夫が必要です。また、メッセージが届くことも担保されるわけではないので、確実に反映されるとは限らないことを把握しておいてください。

## Cortana
VRChatには実装されていませんが、どこでも、いつでも、そしてほとんどのユーザが使用できるインターフェースがもう1つ存在します。それが、Windowsに標準搭載されているAIアシスタント、Cortanaです。VRChatが動くOSはWindowsのみです。裏を返せば、VRChatユーザのほぼ全員のPCには、Cortanaが標準インストールされていることになります。このCortanaですが、設定すれば「コルタナさん」と呼びかけるだけで起動させることができます。VRChatはHMDを付けたユーザーがマイクを通じて会話しているので、その最中に「コルタナさん、〇〇して。」と呼びかけさえすれば、あっさり起動できます。実はVRChatと親和性が非常に高いインターフェースなのです。

CortanaはV2Oに分類されるインターフェースですが、認識内容に基づいてOSCメッセージを生成するスクリプトを実行させるようにすれば、話しかけるだけでアクションを実行できるV2V(V2O2V)のインターフェースとして使用できます。例えば下記のように、「コルタナさん、電気を消して。」と言ったらVRChatの照明を落とす、なんてことを実現できてしまいます。ホームワールドをスマートホーム化してみると、楽しいかもしれません。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Cortanaの音声認識使って、VRChatのWorldで照明が点けられるようになりました～！私の声入れてないので分かりにくいんですが、喋りかけるだけで操作できます。これ応用して、もっと面白いことしたい...！ <a href="https://t.co/m3GsCcVRul">pic.twitter.com/m3GsCcVRul</a></p>&mdash; じぇしか@VRChat (@jscmla1118) <a href="https://twitter.com/jscmla1118/status/1017064673852588033?ref_src=twsrc%5Etfw">2018年7月11日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## おわりに
駆け足かつ雑になってしまいましたが、ここまで、VRChatと外部世界を繋ぐインターフェースをご紹介してきました。自由に扱えるようになれば非常に有用な仕組みばかりなのですが、基本的に自作プログラムの実装やサーバの構築が必要になったりと、万人が気軽に扱うという観点では課題がたくさん残っています。もっといろんな人に使ってみて欲しいという思いはあるので、アプリケーションにまとめたり、技術を体系化するなど、今後は汎用化に向けて取り組んでいこうと思っています。ご質問がありましたら、お気軽にご連絡ください。

[^1]: VRChat APIの存在も認知しており、いろいろ試したことはあります。ただ、ユーザに対して明示的に公式提供されているものではないので、今回は対象から外しています。