一度死んだVRCSDK2のワールド(と、それを使うイベント)をVRCSDK3でゴリ押し再構築した話
この記事は「VRChat Advent Calendar 2020 (https://adventar.org/calendars/5102)」19日目の記事です。 お久しぶりです(?)はるです。
私は以前VRChat上でVRC-LT(https://vrc-lt.github.io/)というLT会を開催していたのですが、この会は今年半ばごろにSDK2の同期関連の挙動が変わってワールドが動作しなくなってしまったのが原因で、しばらく無期限延期状態になっていました。
流石にワールドのロジックを記述するのにSDK2のTriggerベースではこの先メンテしきれないし、SDK3にはVRC_Panoramaのような画像を取得してきて表示するような機能を持ったComponentは存在しないし、どうしよう……と思っていたのですが、先日変なやる気を起こしてゴニョゴニョしていたら案外できるのでは?という気持ちになってきたので、その話を書きたいと思います。
この記事に関連するリポジトリは https://github.com/vrc-lt にあります。
全体的に突貫工事でガバガバなのはゆるして。
もともとのワールド
在りし日のVRC-LT on VRCSDK2の様子は以下のWebページに動画やスライドへのリンクとしてアーカイブされていますので、そこを見てもらえると手っ取り早いです。スライド画像のページめくりが同期しており、スライドを逐次読み込んだり、発表順に合わせてWeb管理画面から並び替えることができました。
https://vrc-lt.github.io/past-events.html
これらはVRChat Build 820くらいからまともに動かなくなってしまったので、SDK3で組み直せるようになるまでは一旦置いておこう……となってしまいました。
動画をコマ送りする
ときは流れてSDK3のワールドも一般的になり、C#ライクなUdonSharpが開発され、そしてSDK3にも動画プレイヤーが実装されました。
しかしVRC_Panoramaのような静止画を取得できるコンポーネントはいつまで待っても追加されないので、今あるものでどうにかならんかなーとぼんやり考えるようになりました。[*1]
スライドを外部から取得してきて表示すること自体は、とても単純なアイデアで実現できそうに感じました。一定の時間で画面が切り替わる動画を再生させて、動画プレイヤーとしては常に一時停止の状態のまま再生位置を変化させれば良いからです。
このやり方は実際に機能しました。
(ワールド自体も新たに作り直したので、ProBuilderを使ってみてうれしくなっている図)Cube脱法建築からProBuilder脱法建築に進化しました(それ以外にも全部変わりました) pic.twitter.com/gvajXMpLum
— はる@VRChat (@haru_vrchat) 2020年11月25日
VRCSDK3を触るのはこれが初めてだったので、同期などのUdonやUdonSharpに特有の概念がどんなものなのかを探るのが最も手間がかかるポイントでした。具体的には以下のようなポイントでつまづきました。
[UdonSynced]
がついた変数の扱い方- 変数に値を代入する事ができるClientをOwnerのみに限って、それ以外のClientは原則更新しない
OnDeserialization()
の動作- UdonSyncedな変数が更新されたら呼ばれる(どれが更新されたかはわからない)
- ローカルにUdonSyncedな値と対応する変数を持っておいて、それと比較して変更を検知する
- 変更が検知されたらローカルの値を更新するとともに必要ならローカルのComponentを更新する
- UdonSyncedな変数が更新されたら呼ばれる(どれが更新されたかはわからない)
UdonSharpでは変数を定義する際に、[UdonSynced]
をつけることによって、値が同期されるようになります。Syncedな変数の場合、値が代入されると他のクライアントでも値が新しいものに更新され、更新されたことを通知するためにOnDeserialization()
というメソッドが呼ばれます。
Syncedな変数にすべてのクライアントが値を代入するような形を取るとこの動作が何度も起きてしまって収拾がつかなくなりそうだったので、Owner以外のクライアントでは値を更新せず、他のクライアントは値の更新を受けてそれぞれのクライアント内での状態をそれに合わせて更新するというやり方が広く使われているようです。
スライド用プレイヤーを実装するときに参考にしていたUSharpVideoPlayerでもこの方法で再生開始位置等を同期していたのでこれが一般的なのかなと思っていますが、他にもカスタムイベントを送るなどの方法を用いて他のClientと通信することができるようです。
実際、この方法でトラブルが多い状態ではありつつも昨日のVRC-LT #8-alphaを一応開催できたので、今のところはこれが一番やりやすい方法なのかなーと思っています。
再生用にスライドを動画に変換する
あとは、スライドのPDF等を動画に変換してYoutube等にアップロードしてもらうだけです。
動画への変換自体はffmpegなどで行うことができますが、発表者に逐一ffmpegのインストールや使い方の確認をお願いするのも申し訳ないので、そのような準備をせずに変換できる方法が必要でした。
VRC-LTでは、発表者に専用Discordサーバーに参加してもらっており、そこで連絡をとっています。
そこで、PDFが添付されたMentionを受け取ると動画に変換するDiscord Botを用意して運用することにしました。
Discord Bot向けのライブラリがあって、以前から興味のあった言語であるRustを使用して書いてみました(内部的にはxpdfとffmpegを呼んでるだけなのでRustっぽさはあまりない気もします……)。それをDockerイメージに固めて適当にデプロイしています。
Discordでやり取りをしているという前提がある現在のVRC-LTでは、こうした方法で動画の変換を行うことができるので発表者側から見てもとそこまで煩雑ではなく、動画さえ用意できれば飛び入り参加も可能なので、一応やってやれないことはないかなーと思っています。
開催する
各発表者のアバターのサイズによってスライドの操作系が使いづらくなっちゃったりとか、人数が集まるとまともにページが送れなくなるみたいな実際やらないとわからないような修正ポイントが山程見つかったので、Alpha版としての開催は正解だったなーと思っています。ひとまずトラブルも有りつつすべての発表者が発表を終えることができたので、次までに今回引っかかったところを治せるだけ直してBeta版をやろうかと思っています……(意識が低い)
おわりに
ふたたびVRChat上でLT会を行えるような状態にはなったと思うので、これからまたイベントを開催できるといいなと思っています。もしよろしければご参加ください。