VRChat雑記

かやのみちゃのVRChat関連の雑記ブログです

VRChatでランダムなスクリーンショット表示+QRコード

どうも、かやのみちゃです。VRChat歴は2018/9に始めたので5ヶ月くらい。プレイ時間は430時間くらいです。今回作成した図書室というワールドがpublic化したので記事にしてみました。

VRChatを始める前はまったくUnityには触れていなかったので自分でも驚いています。普段は雑記ブログに書いていますが、技術的内容も多くnoteも使ってみたかったのでこちらに書いてみます。

図書室を作った、と言っても実際は調整したという言葉のほうが正しいように感じます。VRChat用+いくつか趣味でいろいろ追加しました。
大部分はUnityのAsset Storeで販売されているAssetのおかげです。
また利用させていただいたものは文章にまとめワールド内にQRコードつきで置かせていただきました。

図書室のワールドで一つ技術的に挑戦したものがランダムで表示されるスクリーンショット+QRコードです。
こちらはちうさんのPanoramaImageLoaderを利用させていただいてます。
自分がすごいのではなく、もとを作った人が一番すごいです…。
実は未だに動作原理はわかっていません。ですが、いい感じに使えたので共有します。

VRC_Panorama

VRChatのVRC_Panoramaは外部のURLを指定することでゲーム内に外部の画像を読み込ませることができます。これは各ユーザごと、それぞれがURLに対してリクエストを行う方式のようです。

ただし一度読み込むとキャッシュされ、再リロードなどの仕組みも通常はありません。内部でキャッシュの配列をたくさん用意すれば数は読めますが、徐々に重くなるだろうと思われます。

PanoramaImageLoaderのサンプルではおそらくPanoramaを30秒ごとに破壊して、再度生成することでリロードを実現されていると思います。ところがデフォルトの設定ではそれぞれがPanoramaを破壊できるらしく、多人数になると何度も画像がリロードされているように見えてしまいます。

そこでPanoramaの生成、破壊についてはLocalで動かすことにしました。
同期についてはまだわかっていませんが、こちらを参考に勉強しました。

同じものが見えていますか

さて、問題なのはどうやってランダムなのに全員が同じものを見れるようにするか?です。簡単なのはボタンを押して全員が同時に更新というのが非常に楽です。が、どうせ面倒で押されないことは目に見えています。
また更新用のURLなどを用意してもいいですが、あまり堅牢には思えません。

VRChatではインスタンスのIDやMasterかどうかを教えてはくれません。頑張ればできるかもしれませんが、ちょっと複雑になりすぎる気がします。来場者のIPアドレスを控えてMapにしてどこまで見たかサーバーで保持して…などはやりたくありません。

結局サーバー側で内部でタイマーを持ち、ランダムで画像を提供するという方式にしました。サーバー内部のタイマーはなんとなく25秒間隔で更新されます。そして、おそらくはLocalのTriggerで動作するため全員が必ず同じ画像は見れないだろうと覚悟しています。
改良するとしたら更新ボタンをVRChat側に設けたほうがいい気がしますが、そこまでの技術力がありません…。

GooglePhotoの高画質保存とGoogle Drive

GoogleDriveと連携していけそうだなと思ったのはこの記事のおかげです。最近ではVRChatのイベントカレンダーとしてよく利用されていると思います。GoogleDriveを直で利用するため、サーバーなしで負荷も気にせず使える非常にかしこい仕組みだと思います。

スライドショーで扱う画像はGooglePhotoの高画質保存とGoogleDriveを有効利用しています。GooglePhotoは高画質での保存を行うことで容量枚数無制限というとんでもないスペックを持っています。たくさんスクリーンショットを持っていてもこれなら安心です。

GooglePhotoの高画質による保存結果はGoogleDriveにも反映されます。
Google Drive側で一つフォルダを作り、そこにGooglePhoto下にあるファイルをすべて入れ込みます。その後、そのフォルダをURLリンクを知っている人のみ共有とすることで、外部に画像を見せる準備が整います。

このフォルダに対してリンクの共有をオンにすると、配下の画像にもすべて共有の許可がおります。

Google Apps Script

さて画像を入れたあと、実際に表示するためのURLを生成するため画像のIDをすべて取得します。これにはスプレッドシートGoogle Apps Scriptを使用します。

スプレッドシートを新規で開き、ツール→スクリプトエディタを選びます。

function myFunc(){
  //https://drive.google.com/open?id={yourfolderid}
  var url = 'https://drive.google.com/drive/folders/{yourfolderid}'
      paths = url.split('/'),  
      folderId = paths.pop(), 
      folder = DriveApp.getFolderById(folderId),
      files = folder.getFiles(),
      list = [];

  while(files.hasNext()) {
    file = files.next();
    list.push([file.getId()]);
  };

  var mySheet = SpreadsheetApp.getActiveSheet();
  var rows = list.length;
  var cols = list[0].length;

  mySheet.insertRows(1,rows);
  mySheet.getRange(1,1,rows,cols).setValues(list);
}

上記はネットでサンプルなどを参考に簡略化したものです。これでも結構エラーに出くわし苦労しました…。{yourfolderid}には先程の共有URLに含まれるID値を入れてください。

このコードをいれたあと、実行することでスプレッドシートにフォルダID一覧が書かれます。権限について確認を求められると思いますがOKします。
その後CSVとしてエクスポートしてサーバーに入れ込みます。自動で取得できるようにすれば、さらにいい感じになります。

Heroku

サーバーは無料で使えるHerokuを選択しました。おそらくVRChatプレイヤーは利用時間がまばら&Public化したあとで時間が経つと人が少なくなるため無料枠で絶対大丈夫だろうと。なにより処理自体が相当軽くなる見込みでした。

というのもVRChatからリクエストがあったらURLを参照して301 のリダイレクトを返すだけのサーバーだからです。
しかしながら若干実装があまりきれいではありません。がホビー向けならこの程度で十分、かつ壊れたらHerokuが勝手に立ち上げ直してくれます。

実装はCSVから画像IDリストを取得し、グローバルにもたせたリストを並列処理によって25秒毎にシャッフル。
URLにはクエリで番号を受け付けて同じ画像URLを返すようにしています。
例えば image?query=1 に来たものはすべて同じ画像になります。25秒経つと違う画像になるという仕組みです。

GoogleDriveの画像URLは
https://drive.google.com/thumbnail?sz=w480-h480&id=
などと行うことで軽量化が測れるようです。

これをVRChatで引き伸ばしてもいい感じに見えて表示が高速化されます。
そもそもGoogleCDNが優秀なのか、遅延が少なくてとてもよいです。

QRコード

QRコードについては盲点でした。最初は画像が表示できれば満足…と思っていましたが、テストで来てもらったフレンドに思い出を後で見たい、QRコードでダウンロードできると大変便利とコメントいただいて実装しました。ありがとうございます。素敵ですね。

QRコード自体は GoogleDriveのオリジナルのURLを入れ込んでいます。アクセスするURLは別パスで/imageと/image/qrcode とかで同じクエリ番号なら同じ画像になるように作りました。間違えたら大変なことです…。

VRChat上で読めるかは不安でした。ですが角度にもよりますが、そこそこ読めるようです。VR上でQRコードを読むものはいくつかあり、バーチャルマーケットなどでも有効活用されると思われます。なのでこういう仕組みはかなりいいんじゃないかと。

最後に

この仕組みを作った理由、public化した理由ですが、下手でもやってみようと思ったからです。VRChatは進化が早いです。そしてだれかがきっと追従してさらによくしてくれます。もっと強い人がアドバイスもくれるでしょう。
なので恐れず、自分でもできたよと発信してみたかったのです。

なにもかもオリジナルでなく、二番煎じ、三番煎じな気もしますが、それでも楽しかったです。フレンドに作ったものを見てもらい、そして楽しんでもらえるのは本当に嬉しいです。

このスライドショーの仕組みは大変面白く、交流会などではかなり効果を発揮するのではないかと思っています。例えばTwitterの特定のハッシュタグをもとに一定間隔で収集、サーバーにURLを蓄積、VRChat側はそれを表示…とすれば非常にリアルタイムで面白い試みができるのではと思います。

作って試している最中も結構ランダムで表示される画像に夢中になり、スクリーンショットを整理していると時間が過ぎていったりと…。VRChatではわりと需要があるのでは?と思います。
フレンドやDiscordのグループで共有のフォルダを作り、ワールドにこのような仕組みが一つあると結構華やかになるのではないでしょうか。

なにかご質問やわかりにくいことがありましたらTwitterマシュマロで聞いてみてください。ありがとうございました。