じぇしかめも

VRChatとインフラのお話がメインです。主に備忘録として、自分の理解を書いています。かならずしも事実とは限らないので、ご了承くださいな。

Google DriveからVRChatワールド上のテクスチャ画像をらくらく更新する

はじめに

だいぶ経ってしまいましたが、あけました、おめでとうございます。 2018年は7月にHTC VIVE Proを買ってから、取り憑かれたようにVRにハマっていました。 2019年もこの熱は冷める気がしないと思いますので、VRChatを中心に関わってくださっている方々、今年もよろしくお願いします

さてさて、本題です。 VRChatのワールドにちょこちょこ遊びに行くと、たまに広告を見かけることがあります(当然、今のところお金が絡むようなものはないですが)。 これらはワールド作成時に貼られたテクスチャが多いかと思いますが、ワールドそのものはアップロードして更新する静的なコンテンツなので、それに伴って静的なものになりがちです。 定期的にワールドのテクスチャを張り替えてアップロードしなおしている方もいらっしゃるのですが、そのたびにUnityを開いて、テクスチャを入れ替えて、ワールドをアップロードする必要があるので、正直かなり手間がかかります。

一方で、VRChat SDKにはVRC_Panoramaというコンポーネントが提供されているのですが、これを使うと指定のWebリンク先からテスクチャ画像をリアルタイムに取得できます。そこで今回は、Google Driveにアップロードした画像をテクスチャとして取得し、動的に更新する方法をハンズオン形式で紹介します。

テクスチャ画像をGoogle Driveにアップロードする

早速ですが、まず初めにGoogle Driveにテクスチャ画像をアップロードしてみます。 Google Driveのセットアップは、あらかじめ済ませておいてください。 今回は"VRChat"フォルダを作成し、その下にアップロードします。 f:id:jscmla1118:20190113152822p:plain

次に、アップロードする画像を用意します。 今回はPNG画像に"vrc_texture"という名前をつけて、先ほどのVRChatフォルダにでアップロードします。

そしたら、ひとつ確認事項です。 この後Unityからこの画像を参照するためのURLを張り付けるのですが、あらかじめGoogle Driveで見ておきましょう。 Google Driveのアップロードした画像に対して右クリック、「共有可能なリンクを取得」を選択します。 するとリンクが表示されるので、それをコピーしてメモしておいてください。 後ほど使用します。 f:id:jscmla1118:20190113153715p:plain

ワールド(Unity)の設定

それでは、Google Driveにアップロードした画像を取得してみます。 まずVRChat SDKをインポートし、VRCWorldやPlaneなどを適当に設置して、ワールドの設定を済ませます。 f:id:jscmla1118:20190113172432p:plain

次に、コンポーネントの設定です。 テクスチャを張り付けるためのQuadを設置しましょう。 縦横の比率は、アップロードした画像に合わせておきましょう。 これに、VRC_Panoramaコンポーネントを追加してください。 f:id:jscmla1118:20190113175140p:plain

Size欄には、1を入力します。 すると、URLを入力する項目が現れます。 そこに、先ほど確認したGoogle Driveの共有リンクを張り付けてください...と言いたいところですが、残念ながらこのままでは取得に失敗してしまいます。 f:id:jscmla1118:20190113171326p:plain

そこで、このURLを少し書き換えましょう。 先ほど取得したURLですが、下記のような構造となっています。

https://drive.google.com/open?id=[ID]

このIDがGoogle Drive上のファイルを一意に識別するためのものです。 このIDだけを抜き出し、下記のURLに書き換えてください。

http://drive.google.com/uc?export=view&id=[ID]

VRC_Panoramaの設定はこれでよいですが、もう一つ設定項目があります。 VRC_Panoramaを追加したタイミングで、VRC_DataStorageも自動で追加されます。 こちらも設定する必要があるので、下記のように入力してください。 f:id:jscmla1118:20190113172059p:plain

ちなみにVRC_DataStorageはその名の通り、VRChatにて使用する様々な数値を管理するコンポーネント...かと思います。 詳細な仕様はあまり公開されていないのですが、VRC_Panoramaで取得した画像はこのdisplayパラメータとして管理されるようです。 Sizeを増やすと、その数だけ画像を保持できるようになり、順々に参照できるようにもなります。 やり方は少し複雑、かつ今回の話からは逸れるので、紹介は割愛します。

さて、設定はこれで完了なので再生してみましょう。 設定が完了していれば、テクスチャが表示されるはずです。 f:id:jscmla1118:20190113172742p:plain

試しにBuildして、VRChatからも確認します。 ログインした時点で画像が表示されていれば、おっけーです。 f:id:jscmla1118:20190113173611p:plain

テクスチャ画像を更新する

ひとまず、Google Driveからテクスチャ画像を参照できるようになりました。 ただ、これだけではUnity上にテクスチャ画像をローカルで配置する手間と何ら変わりません。 そこで次は、ワールドの更新は行わずに、テクスチャ画像を更新してみましょう。

前提として、ワールド側には既に画像のファイルIDを含むURLを参照させています。 IDが変わってしまうとテクスチャ画像を取得できなくなり、設定しなおしとなってしまいますので、これを維持する必要があります。

ではどうすればよいのかというと、対象の画像ファイルと同じ名前のファイルを、Google Driveにアップロードすればよいのです。 Google Driveは、名前が同じファイルがアップロードされた場合、新たなバージョンとして更新されます。 この場合ファイルIDは変わらないので、VRC_Panoramaで参照しているURLは継続して使用できます。

実際に試してみると、バージョンが更新されたことを示すポップアップも出てきます。とっても親切。 f:id:jscmla1118:20190113174252p:plain f:id:jscmla1118:20190103131215p:plain

と、上記の通りGoogle Driveにファイルをアップロードするだけで、画像の差し替えが可能になります。 実際にUnity側でもう一度、再生してみましょう。 f:id:jscmla1118:20190113174420p:plain

画像が切り替わりました。 ここでの確認はローカルのUnityで実施していますが、この時点でアップロード済みのワールドも同様に画像が更新されています。 なので、必要な更新作業はこれで完了しています。かなり楽になりますね。 一つだけ注意なのですが、アップロードしてから名前を変えても同名のファイルが共存する状態になるだけなので、あらかじめ名前を合わせておきましょう。

おわりに

ということで、シンプルに画像をアップロードのみで更新できる流れを書きました。 同様に今後も、新たに更新する場合は同様の手段で実現できます。 複数の画像を管理したい場合は、それだけ別名のファイルを用意してください。 今回はシンプルな例を紹介していますが、Google Driveへのアップロードや、GASを使用した画像自動生成など、複雑な処理も実装することも可能です。 今回の話含め、先日のVRChat Advent Calendar 2018にて紹介されている方もいますので、ぜひ調べてみてください。 qiita.com

macOS上でVueプロジェクトを作成するまで

はじめに

最近、転職に向けて色々と遊びながらネタを作っています。 そこでふと「ブログはあるけど、ポートフォリオみたいなWebページ作りたい!」と思ったのです。 思い立ったが吉日、ということでWebページ作成にさっそく着手してみることにしました。 ついでに、せっかくならトレンドになっている技術に触れておこうということで、Vue.jsを使うことに。 それにあたって、まずは開発環境を用意しようということで、今回は手順を整理してみました。 と言っても、単にQiitaの記事にしたがって作業しただけですが、備忘録として残します。 前提として、Homebrewはインストール済とします。

Vue.jsとは

まず、Vue.jsが何なのか。 Vue.jsはJavaScriptフレームワークの一種です。 他にはReactやAngularJSなどが同列のフレームワークとして挙げられるようですが、正直どのような違いがあるのかは全く理解していません。Vue.jsが何やら流行っているようなので、単純思考で使ってみることにしました。

Vue CLIとは

Vue CLIはその名の通り、Vue.jsを使用したプロジェクト構築をサポートするコマンドラインインターフェースだそうです。 後ほどコマンドを使用しますが、対話形式でプロジェクトの初期設定を実行できます。 他にもいろいろな役割があるとは思いますが、現時点ではVue.jsをさっぱり理解できていないので、慣れてきた頃に詳しく調べておきます。

Node.jsのインストール

それでは、環境の構築を始めましょう。 Vueプロジェクトの構築環境を整える際にnpmを使用するため、まずはNode.jsの環境を整備していきます。 これはありふれた話なので、先人の記事に従います。 qiita.com

nodebrewのインストール

他の言語と同じく、バージョンを気軽に変えられるようにしておこうということで、まずはnodebrewをインストールします。

$ brew install nodebrew
$ nodebrew -v
nodebrew 1.0.1

Usage:
    nodebrew help                         Show this message
    nodebrew install <version>            Download and install <version> (from binary)
    nodebrew compile <version>            Download and install <version> (from source)
    nodebrew install-binary <version>     Alias of `install` (For backword compatibility)
    nodebrew uninstall <version>          Uninstall <version>
    nodebrew use <version>                Use <version>
    nodebrew list                         List installed versions
    nodebrew ls                           Alias for `list`
    nodebrew ls-remote                    List remote versions
    nodebrew ls-all                       List remote and installed versions
    nodebrew alias <key> <value>          Set alias
    nodebrew unalias <key>                Remove alias
    nodebrew clean <version> | all        Remove source file
    nodebrew selfupdate                   Update nodebrew
    nodebrew migrate-package <version>    Install global NPM packages contained in <version> to current version
    nodebrew exec <version> -- <command>  Execute <command> using specified <version>

Example:
    # install
    nodebrew install v8.9.4

    # use a specific version number
    nodebrew use v8.9.4

問題なく、バージョンが表示されました。 OSバージョンアップに伴ってコマンドが打てなくなったりするケースがあるようなので、ついでにパスを通しておきます。 .bash_profileに下記のものを追記します。

# nodebrew
export PATH=$HOME/.nodebrew/current/bin:$PATH

Node.jsのインストール

次に、Node.js本体をインストールしていきましょう。 まずは、配布されているNode.jsのバージョンを確認します。

$ nodebrew ls-remote

今回は、2018/12/26時点で最新のv11.5.0をインストールします。 バージョン指定する方法でも良いのですが、最新版を取ってくる場合は下記コマンドでインストールできるようです。

$ nodebrew install-binary latest
Fetching: https://nodejs.org/dist/v11.5.0/node-v11.5.0-darwin-x64.tar.gz
Warning: Failed to create the file 
Warning: /Users/jscmla1118/.nodebrew/src/v11.5.0/node-v11.5.0-darwin-x64.tar.gz
Warning: : No such file or directory
                                                                           0.0%
curl: (23) Failed writing body (0 != 1056)
download failed: https://nodejs.org/dist/v11.5.0/node-v11.5.0-darwin-x64.tar.gz

と、ここでエラーが出てしまいました。 ダウンロードパッケージを格納するディレクトリを作ってあげましょう。

$ mkdir -p ~/.nodebrew/src

再度インストールコマンドを実行し、使用するバージョンを設定してやります。

$ nodebrew ls
v11.5.0

current: none
$ nodebrew use v11.5.0
use v11.5.0
$ nodebrew ls
v11.5.0

current: v11.5.0

念の為、Node.jsとnpmコマンドが使えることを確認しましょう。

$ node -v
v11.5.0
$ npm -v
6.4.1

大丈夫そうですね。これでNode.jsの設定は完了です。

Vue CLIのインストール

Node.jsの環境が整ったので、Vue環境の設定に入ります。 ここからは、手元にある参考書「基礎から学ぶVue.js」に基づきながら、設定を進めていきます。 早速ですが、Vue CLIをインストールしちゃいます。

$ npm install -g vue-cli
npm WARN deprecated coffee-script@1.12.7: CoffeeScript on NPM has moved to "coffeescript" (no hyphen)
/Users/jscmla1118/.nodebrew/node/v11.5.0/bin/vue -> /Users/jscmla1118/.nodebrew/node/v11.5.0/lib/node_modules/vue-cli/bin/vue
/Users/jscmla1118/.nodebrew/node/v11.5.0/bin/vue-init -> /Users/jscmla1118/.nodebrew/node/v11.5.0/lib/node_modules/vue-cli/bin/vue-init
/Users/jscmla1118/.nodebrew/node/v11.5.0/bin/vue-list -> /Users/jscmla1118/.nodebrew/node/v11.5.0/lib/node_modules/vue-cli/bin/vue-list
+ vue-cli@2.9.6
added 239 packages from 206 contributors in 17.066s
$ vue --version
2.9.6
$ which vue
/Users/jscmla1118/.nodebrew/current/bin/vue

難なく、入りました。 ログを見ると、nodebrew管理下のディレクトリに入っているようです。 Node.jsのバージョンを切り替えたタイミングでおそらく使えなくなるような気がしますが、今のところは困らないので気にしません。

Vueプロジェクトを作ってみる

Vue環境が整ったので、お試しプロジェクトを作ってみましょう。

$ vue init webpack my-app
? Project name my-app
? Project description A Vue.js project
? Author Jessica Mellia <jscmla1118@gmail.com>
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Set up unit tests Yes
? Pick a test runner jest
? Setup e2e tests with Nightwatch? Yes
? Should we run `npm install` for you after the project has been created? (recommended) npm

~  省略 ~

gyp ERR! configure error 
gyp ERR! stack Error: Command failed: /usr/local/var/pyenv/shims/python2 -c import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack pyenv: python2: command not found

~ 省略 ~

To get started:

  cd my-app
  npm run dev
  
Documentation can be found at https://vuejs-templates.github.io/webpack


$ echo $?
0

プロジェクト作成にあたって、対話ベースで色々と質問されます。 今回はひとまず動くところまでを確認したいので、詳細は割愛します。 Vueプロジェクトの構成解説と併せて、別記事を書く予定です。

一通り質問に答えたらインストールが始まるのですが、なんか途中でPythonコマンドを実行していて、そこでエラー吐いてました。 一方でvueコマンドのリターンコードは0だったので、ひとまず無視して先に進めちゃいます。

サーバのテスト起動とアクセスチェック

さて、プロジェクトとしては構築が完了したので、さっそくサーバを起動してみます。 プロジェクト構築時のログにテスト起動のコマンドが記載されているので、そのまま打ちます。

$ cd my-app
$ npm run dev

> my-app@1.0.0 dev /Users/jscmla1118/Project/my-app
> webpack-dev-server --inline --progress --config build/webpack.dev.conf.js

 95% emitting                                                                         

 DONE  Compiled successfully in 4423ms                                                                                                                                                              22:54:54

 I  Your application is running here: http://localhost:8080

ビルドが実行され、サーバが起動されます。 指定の通り、localhost:8080をブラウザで開いてみます。 f:id:jscmla1118:20181226230110p:plain

想定通りのWelcomeページが開きました。 が、何故かこれを開こうとするとブラウザが途端に重くなる...。 まぁ、目的は一応達成できたので、今回は良しとします。

おわりに

Vue.jsを使用したWeb開発環境を一通り整えました。 ここから、Vue.jsの勉強を進めつつ、Webページを構築していく予定です。 進捗に応じて、備忘録としてまた記事は書こうと思います。

OSCでリアルタイムコントロール in VRChat - 概説編

はじめに

前回、OSCメッセージをトリガーとして使用する方法を説明しました。 jscmla1118.hatenablog.com

そこに記載した通り、応用次第では下記のような照明コントローラを作成することもできます。

今回は、VRChat側の設定を主として、照明コントロールの基本となる仕組みについて説明します。 まずは基本的な動きを掴むため、VRChat上の自作ワールドに設置したCubeの赤色を自由に変える方法を例として説明します。 ロジックはこれを応用しているものなので、この記事を読んでもらえれば大体の流れは掴めると思います。

送信側

まず始めに、OSCメッセージの送信側について、説明します。 送信側で指定するのは、反映させたい色情報です。 今回の例では赤色のみを扱うので、RGBのR値を対象に説明します。 また、R値は0~255の10進数で指定することを前提とします。

さて、例えば「Cubeオブジェクトの赤色をR180にしたい」としましょう。 送信側で最初に入力する値は、この180という10進数の数値です。 最終的にVRChatへ送信するのは、OSCメッセージです。 このOSCメッセージですが、どんな値を送ったとしてもVRChatにて判定できるのはOn/Offのみです。 そのため、1つのOSCメッセージだけでR値を表現することはできません。

では、どのように数値を扱うのかというと、2進数表記を使用します。 2進数は各桁を0か1で表現する方法で、言い換えればスイッチのOn/Offに対応させられます。 そもそもR値の0~255という数値は、赤色の明度を1Byte(8Bit)の数値、すなわち8桁の2進数で表現したものです。 各桁に対応したOSCメッセージを8つ送信し、それを元の10進数にさえ変換できれば、On/Offしか利用できないVRChat上でもR値を表現できるというわけです。
f:id:jscmla1118:20181118171011j:plain

受信側

送信側の説明は簡単に済ませつつ、受信側の処理については細かく追っていきます。

オブジェクトパラメータの変更方法

まずは、VRChatにてどのようにしてオブジェクトの色を変えるのか、考えてみます。 通常、Unityで作成したアプリケーションなら、C#スクリプトからオブジェクトのパラメータを参照して変更するのが、一般的な手段かと思います。 しかし、VRChatには自作のC#スクリプトを持ち込むことができないので、この方法は使用できません。 となったとき、他の手段として挙げられるものの1つがアニメーションです。 アニメーションは、オブジェクトのパラメータを一時的に変更できます。 再生が終わったら元に戻るのが基本的なアニメーション動作ではありますが、終了状態を維持することもできます。 VRChat上では、このアニメーションの性質を利用してCubeオブジェクトの色を変えます。

VRChatトリガーとアニメーション

次に、OSCメッセージの受信をトリガーとして、アニメーションにアクションを与える方法を考えます。 VRChat SDKにて定義されているアニメーション関連のトリガーは、以下のものがあります。

  • AnimationBool
  • AnimationFloat
  • AnimationInt
  • AnimationTrigger
  • AnimationIntAdd
  • AnimationIntDivide

これら全て、アニメーションパラメータを操作するトリガーです。 このパラメータはアニメーション実行の閾値として使用されるもので、「設定した値が一定の範囲に入ったら、アニメーションを再生する」というような使い方ができます。 ここに設定した値をそのままアニメーションに反映できればよいのですが、残念ながらその方法はないようです。
f:id:jscmla1118:20181118184335p:plain

ここに含まれている、"AnimationBool"はOn/Offを取るパラメータです。 このOn/Offを、OSCメッセージから受け取ったOn/Offに対応させて使用します。

アニメーションによるパラメータ操作

次に考えることは、アニメーションで色を変化させる方法です。 これはUnityに慣れている方なら簡単な話で、Cubeオブジェクトのメッシュに定義されているColorパラメータを変化させればよいです。 例えば、Cubeオブジェクトの赤色をR128に変更する場合、まず0~255の範囲を0~1に正規化します("128"を正規化した場合は、"0.5"になります)。 それに基づき、"Color.r"パラメータを0から0.5に変化させるアニメーションクリップを設定します。
f:id:jscmla1118:20181118190504p:plain

このアニメーションクリップですが、終了状態を維持する必要があるのでLoop Timeはチェックを外してください。
f:id:jscmla1118:20181118214134p:plain

設定が完了したら、上記のアニメーションクリップを含むアニメーションコントローラを作成します。 そして、AnimationBoolのOn/Offに応じてアニメーションクリップが再生/停止されるように遷移を定義すれば、色を変えることができます。
f:id:jscmla1118:20181118191613g:plain

OSCメッセージの復号

Cubeオブジェクトの色を操作する方法はざっくり理解できたかと思います。 とはいうものの、VRChatにて受信するOSCメッセージは、今回のケースでも8つあります。 それらパラメータを使用するとして、どのようなアニメーションでCubeの色に反映させればよいのでしょうか。

1つ考えられる方法は、255個のアニメーションクリップを用意し、パラメータに基づいて255パターンに遷移させる方法です。 ですが、この方法では手間がかかりすぎるので、選択肢からは除外せざるを得ません。 ではどうするのかというと、2進数から10進数に変換する方法を応用すればよいのです。 2進数の各桁は、下記のような10進数に対応させられます。 これは2進数を10進数に変換する一般的な方法で、算出された数値をすべて足し合わせると元に戻せます。
この場合、 128+32+16+4=180 というように、送信側の数値に戻すことができます。
f:id:jscmla1118:20181118195713p:plain

改めて、アニメーションの話に戻ります。 Unityのアニメーションコントローラーでは、アニメーションレイヤーを設定できます。 これを使用すると、アニメーションを重ね合わせる、言い換えれば足し合わせることができます。 何が言いたいかというと、R値を128だけ変更するアニメーションに加えて、64, 32, 16...だけ変化させるアニメーションを重ね合わせてやれば、組み合わせ次第でR値を0から255まで自由に変化させられるようになります。

アニメーションレイヤーは、Animatorタブにて追加できます。 アニメーションを重ね合わせるときには、このレイヤーにてWeightを"1"、Blendingを"Additive"に設定します。 8つのOSCメッセージに対応した複数のレイヤーとパラメータを用意することになりますが、アニメーションの遷移は全て同じように設定すればよいです。
f:id:jscmla1118:20181118201858p:plain

また、基軸となるオフセットアニメーションも用意します。 こちらはBlendingを"Override"に設定します。
f:id:jscmla1118:20181118215301p:plain

これで、アニメーションパラメータのOn/Offに応じて色を自由に変えられるようになります。 これらのアニメーションパラメータは先に説明した通りOSCメッセージで全てスイッチング可能なので、結果的にOSCメッセージを通じてCubeオブジェクトの色を自由に変更できるようになります。
f:id:jscmla1118:20181118203211g:plain

おわりに

ここまで、OSCメッセージを使用して、Cubeオブジェクトの赤色を変更する一通りの流れを説明しました。 この応用だけで、照明コントロールもほぼ実現はできます。 なぜなら、照明コントロールの場合は、色に加えて照明オブジェクトの位置や角度を同じように操作すればよいだけなのです。 他にも、アニメーションクリップから操作できるパラメータであれば同じ方法で操作できるはずなので、それなりに応用が利くかと思います。 とはいうものの、いくつか工夫が必要な項目があるので、詳細については別記事を書こうと思います。 また、現状の方法ではいまいち使い勝手が悪いので、自動で設定可能な拡張エディタを作成しようと思っています。 Unityについてはよわよわなので時間がかかりそうですが、完成したら配布します。

OSCメッセージでVRChatのアクションを実行する

はじめに

最近、VRChatに週1でペースちょこちょこログインして遊んでいる一方、現実とVRChatを結び付ける面白いものが作れないかと模索しています。 それなりに遊べるものをいくつか作ることができたのですが、いずれもOSCメッセージが情報伝搬手段のコア技術となっています。 ただ、個人的にはとっても有用な仕組みだと思っているものの、あまり情報が出回っていないせいか使用例はあまり見かけない印象です。 そこで、OSCメッセージによるトリガーとアクションを誰でも動かせるように、ハンズオン形式の説明をつらつら書いてみました。

今回はチュートリアルとして、ワールド上のCubeオブジェクトをアクティベート/ディアクティベートしてみます。 なおOSCメッセージの送信側はPythonスクリプト、 受信側はUnityで実装します。 誰でもと言いつつ申し訳ないのですが、最低限、下記のスキルがあることを前提とします。

  1. Unity(VRChat SDK)で自作ワールドをビルドして、ログインできる
  2. VRChatを起動するPCにPythonがインストールされており、PythonとpipコマンドをPowerShellから実行できる

2については、PowerShellを開いて下記のコマンドが実行できれば大丈夫です。

PS> python --version
PS> pip --version

OSCとは

そもそも"OSC"なんて知らない方が普通なので、ざっと調べてみます。

OpenSound Control(OSC)とは、電子楽器(特にシンセサイザー)やコンピュータなどの機器において音楽演奏データをネットワーク経由でリアルタイムに共有するための通信プロトコルである。
https://ja.wikipedia.org/wiki/OpenSound_Control

どうやら、OSCはMIDIと同じく音楽の演奏データ通信に使用することを目的として作られたプロトコルのようです。 ただメッセージの構造がシンプルで汎用性があるので、音楽用途以外でも問題なく使えます。 じゃあ具体的にどんな構造かというと、OSCメッセージは「アドレス」と「引数」から成り立っています(呼称にはバラつきがあるようです)。 アドレスは階層構造を持たせることができるので、受信側でパースしやすいように設計できます。 また引数については、一つのアドレスに対して複数定義してやることも可能なようです。 例えば、RGBで赤を表現するようなメッセージであれば"/color 255 0 0"や"/color/red 255"というように記述できます。
f:id:jscmla1118:20181110150441p:plain

VRChatにおけるOSCメッセージの取り扱い

VRChatには、様々なトリガーとアクションを組み合わせることで自作ワールドを構築できる仕組みが備わっています。 そのトリガーの1つとして、VRChat SDKには"VRC_OscButtonIn"が含まれており、特定のOSCメッセージを送信すれば発火させられます。 ただし実行できるのはスイッチのOn/Offのみで、"0"を受け取ればOff、"それ以外の数値"を受け取ればOnとして動作します。 とはいえ、VRChatの「外部」からトリガーを発火させられる貴重な仕組みなので、これが使用できると表現の幅が一気に広がります。 次節以降では、OSCメッセージを使用してオブジェクトのアクティベート/ディアクティベートを実装する方法を説明します。

OSCメッセージ送信側の実装

pythonoscのダウンロード

まず、これから作成するPythonスクリプトにてOSCメッセージを簡単に使用できるようにするため、pythonoscパッケージをダウンロードします。 PowerShellを起動し、pipコマンドでパッケージを取ってきます。 ハイフンが必要なので、忘れずに。

PS> pip install python-osc

スクリプトの実装

pythonoscをダウンロード出来たら、スクリプトを書いていきます。 メモ帳を開いて、お好きな場所に保存してください。 今回はファイル名を"osc_message_client.py"として保存し、使用します。

# -*- coding: utf-8 -*-
import sys
from pythonosc import udp_client
from pythonosc.osc_message_builder import OscMessageBuilder


def main():
    # 受信先のVRChat
    IP = '127.0.0.1'
    PORT = 9000

    # OSCメッセージ情報の読み込み
    address = sys.argv[1]
    arg = sys.argv[2]

    print('Address: '+address)
    print('Arg: '+arg)

    # OSCメッセージの構築
    client = udp_client.UDPClient(IP, PORT)
    msg = OscMessageBuilder(address=address)
    msg.add_arg(arg)
    m = msg.build()

    # OSCメッセージの送信
    client.send(m)


if __name__ == '__main__':
    main()

実行している処理の概要は、コメントに記載の通りです。 テスト用のスクリプトなので、エラーハンドリングなし、詳細な解説も割愛します。

スクリプトが実装できたら、PowerShellから試しに動かしてみます。 アドレスは"/cube/activate"、引数は"1"と"0"をそれぞれ指定してみます。

PS> cd [ファイルの保存先フォルダ]
PS> python .\osc_message_client.py /cube/activate 1
Address: /cube/activate
Arg: 1
PS> python .\osc_message_client.py /cube/activate 0
Address: /cube/activate
Arg: 0
PS>

アドレスと引数がスクリプトに渡り、特に問題なく動いているようです。 この時点で、OSCメッセージは送信されています。 しかし受信側をまだ構築していないので、見せかけ上は何も起こりません。 このメッセージは、また後ほど使用します。

OSCメッセージ受信側の実装

テスト用ワールドの構築

次は受信側の実装です。 こちらはVRChatの自作ワールドになるので、Unityを使用します。 まずは、プロジェクトを作成します。 お好みで、適当に名前を付けてください。 f:id:jscmla1118:20181110161219p:plain

オブジェクトの配置

プロジェクトを作成したら、シーンの保存とVRChat SDKをインポートします。 そしたら、ヒエラルキーにぱぱっと下記のものを追加しましょう。

  • 床用のPlane
  • トリガーハンドリング用の空オブジェクト
  • ターゲットになるCubeオブジェクト
  • リスポーン用のPrefab(VRCWorld)

階層構造は特に気にする必要はないですが、今回はCubeオブジェクトを空オブジェクトの子要素にします。 またマテリアルをCubeに適用していますが、特に意味はないので、お好みでどうぞ。 配置が終わったら、Cubeオブジェクトはディアクティブにしておきます。 f:id:jscmla1118:20181110164056p:plain

アクションの設定

OSCメッセージ受信時のアクションを設定していきます。 ちなみにトリガーとアクションは、ターゲットとなるCubeオブジェクトではなく、親要素の空オブジェクトに対して設定します。 というのも、Cubeオブジェクトにトリガーを設定してしまうとディアクティベートした時点でトリガーも無効になってしまい、 OSCメッセージを受信できなくなってしまうためです。

それでは、Hierarchyタブにて空オブジェクトを選択しましょう。 そしたら、InspectorタブのAdd Componentボタンをクリックして、VRC_Triggerを追加します。

f:id:jscmla1118:20181110165501p:plain

次に、アクティベートとディアクティベート用に、アクションを2つ追加してください。 その際に選択するトリガーは、"custom"でよいです。 下記のようにもろもろ設定して、図と同じようになれば大丈夫です。

アクティベーション設定
①Advance Modeにチェックを入れる
②"Always"を選択
③"OscButtonOn"を入力
④子要素のCubeオブジェクトを選択
⑤"True"を選択

ディアクティベート設定
⑥"OscButtonOff"を入力
⑦"False"を選択

f:id:jscmla1118:20181110170152p:plain

トリガーの設定

受信部の要である、トリガーの設定です。 アクションと同じく"Add Component"ボタンをクリックして、 "VRC_Osc Button In"を追加します。

f:id:jscmla1118:20181110171639p:plain

ここで設定する項目は、3つあります。

Address
これは、先ほど説明したOSCメッセージのアドレスです。先ほどのPythonスクリプトで渡した"/cube/activate"を入力します。

On Button On/Off
ここには、それぞれスイッチのOn/Off時のアクションを設定します。 左側の欄ではVRC_Triggerを追加したオブジェクト、右側の欄では実行するアクションを選択します。 今回のケースでは、オブジェクトはともに空オブジェクト、アクションは先に設定した"OscButtonOn"と"OscButtonOff"を選択します。

f:id:jscmla1118:20181110171623p:plain

これで、設定は完了です。

OSCメッセージの送受信テスト

さて、以上でUnity側の設定も完了したので、ワールドの動作確認を行いましょう。 通常のワールドテストを同じようにテストビルドを実行し、ログインしてください。 ちなみにウィンドウの操作を行うので、デスクトップモードが好ましいです。

ログイン時点では、Cubeオブジェクトはディアクティベートされているので見えません。 ここで、先ほど作ったスクリプトを再度叩いてみましょう。

PS> python .\osc_message_client.py /cube/activate 1
Address: /cube/activate
Arg: 1
PS> python .\osc_message_client.py /cube/activate 0
Address: /cube/activate
Arg: 0
PS>

"1"を送信するとCubeが出現し、"0"を送信するとCubeが消えるようになります。 これが確認できれば、無事にOSCメッセージがVRChatまで届いています。やったね。 f:id:jscmla1118:20181110174313g:plain

おわりに

ここまで、実際にサンプルを作成しながら基本的な動作と実装を紹介しました。 今回の例ではあまり魅力が伝わらなかったかもしれませんが、OSCメッセージを使いこなすことで、ArduinoRaspberry Piに繋いだセンサーと組み合わせたり、外部からの通信に基づいてメッセージを送信するなど、現実世界のイベントもトリガーとして使用できるようになります。一例ですが、下記のようにスマートフォンからトリガーを引くことも可能です。

一方で、VRChat側でもアドレスさえ設定してやれば多数のOSCメッセージを瞬時に受信できるので、複雑なアクションもすぐさま実行できます。扱い次第では、下記のように連続的で細かな操作もできるようになるので、ぜひいろいろ試してみてください。

参考

qiita.com pypi.org docs.vrchat.com