VRChatワールド Friendly Crane 実装詳細解説記事
またアヤシイギミックのワールドを作ったので、それの解説をしていきます。ものはこれ ↓ です
自分の記事の例に漏れず 特殊な話しかしない ので、特殊じゃない方は要注意です。また、Unity / VRChat / VRCSDK のバージョンアップ等によって動かなくなる可能性が大いにあります。(2022/08/07)
別サイズアバター表示
このワールドでは、自分たちが捕まえようとする小さなアバター、もしくは自分たちを捕まえようとする大きなアバターが居るように見えますが、実はこれらに実体はなく、カメラで描画を追加している見た目だけの存在です。(基本は Doppelganger System と同じです。)
Targetに何も指定しないカメラをワールドに置くとその映像が視界に直接描画されるので、そのカメラの設定をイイ感じにすることで好きな映像を追加できる、というのが基本的な設計です。(ただし、そのイイ感じというのがかなり特殊ですね。) 以下、本当に特殊な話をするので全員読み飛ばしてください。
- まず、ワールドにその設定のカメラを置くと (安全のため) 勝手に無効化されるので、UdonやらAnimatorやらで有効化させる (enabled = true) 必要があります。
- 「アバターを追加する」ために、Clear FlagをDon't Clearに、Culling Maskを Player (他人) と MirrorReflection (自分の全身)に、Depthを-1とかにします。(なんでそのDepthで動くのかは知りません)
- 「どう見えてほしいか」を考えてカメラの位置と方向を指定します。Udonで目の位置を取得 (Networking.LocalPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head)) し、それから目標地点を計算しています。小さなアバターを映すには、大きくなった立場から見ればいいので大きなアバターの目にあたる位置に置く必要があります。(ちゃんと考えようとすると頭がこんがらがってくるので、なんか適当にいろいろ試したほうが早いです。)
- このワールドのように違うサイズ想定で描画する場合、前後関係を補正する必要があります。3Dレンダリングの前後比較はClipping PlanesのNearとFarの間のどこの比率にあるかで決まります。例えば、半分の大きさで見える想定の描画をする場合、実際にカメラが描画している距離の半分に来てほしいので、nearClipPlaneとfarClipPlaneをそれぞれ2倍にします。 (読者の約13割がついてこれていないと思いますが想定どおりです。)
- VRの場合、SteamVRによってカメラが移動されるので、カメラではなくその親を動かす & カメラのローカルの移動・回転を打ち消すように動かす必要があります (これはもしかしたらカメラの置き方を工夫すれば不要かもしれない。有識者のご意見をお待ちしています)。 SteamVRによるカメラ移動はカメラのlocalPositionとlocalRotationから取得できますね。
- VRの場合さらに、IPD (瞳孔間距離) をアバターのカメラと合わせる必要があります。この値はアバターの身長や設定のReal Heightによって変わってくる値で、Udonからでも取得できません。しかし、AudioLowPassFilterを付けたGameObjectはVRCによってscaleが変更されるようなので、それのlocalScaleの逆数をアバターによる拡縮の係数として使うことができます(AudioListenerの機能は使いたくないので無効化しておくと良いと思います)。取得したアバターによるカメラ拡大率をカメラに掛けて、後はなんかイイ感じにする (目標スケールとlocalScaleで割る?) とOKです。 (2022/07/31: スケールの取得方法修正)
また、この方法を使うといくつか半透明の描画が上手くいかない部分が出てきます。(半透明の描画順はね、難しいというか無理なんですよ。)
このワールドは大きな半透明を使っているので、目立つ部分 (具体的には、通常サイズから見て小サイズ方向と、通常サイズから見て大サイズ方向の黄色いやつ) は通常のカメラで描画せず(MeshRendererをShadows Onlyに)、また同じ機構で再描画 (専用のレイヤーを用意して描画) しています。よく観察すると、赤い半透明の奥でもアバターに赤が掛からなかったりとかを観測できます。
実は吊られて外側に行った人のさらに外側の複製が存在しています。普通は隠れて見えないので放置してるのですが、巨大なアバターにすると一番外に見えちゃいます。
クレーンの動作
- 実際に操作できて人をつかめるのは最初にいる場所のマシンのみで、他のアームやスティックは見た目だけを合わせています。
- アームの前後左右は位置をUdonの変数として同期しています。線形補間にしてあるので、特に何もしなくても他人目線でもなめらかに動いているように見えます。便利。
- マシンの状態(操作中、掴み中か、戻り中か等)はボタンを押したイベントだけを同期して、今どの状態かの判定は各クライアントがローカルでやっています。 (マシンは時間が経つと絶対に操作待ちになるので雑で大丈夫)
- プレイヤーを運ぶ機能は全てローカル、つまり持ち上げられる人がやっています。判定はアームのところにあるコライダーとプレイヤーの当たり判定で取っています。当たっている&つかみ状態なら移動不可・重力0にして、プレイヤーのVelocityにアームの速度を設定しています。
以下、きれいに見せるための最適化の話です (細かいし他のワールドに応用が効かない……)。 このワールド独自の特殊なものなので、読み飛ばしてもらって大丈夫です。
- スティックを掴んだ時に、アームのOwnerを取得しています。
- ボタンを押した時は全員にイベントを送っているのですが、ボタンを押した人目線でだけアームが早く移動してしまうので、ボタンを押した人だけ0.8秒待つようにしています(本当は時刻を送信して合わせたほうがいい)。ただし、自分自身を掴む時はその心配がないので、アームが自分の真上にあるときだけは待ちません。
- アームの前後左右の情報は常に同期しているのですが、吊った後戻るときにはその情報を使わずにローカルで戻しています (自分・アームのオーナー・吊られた人の間で動きを合わせるのが難しいため、アームのオーナーを考えないでいいように)。 アームが下がっている時とアームが開く時は前後左右には動かないので、その間に裏で辻褄が合うようになっています。
(正直なところ、きれいに同期するにはきれいに同期できる仕様にする必要もあるので、勘所なしでスムーズな同期を目指すのはおすすめしないです。)
疑似カメラ
このワールドの別サイズアバターは視界に直接描画しているもののため、VRCのカメラでそのまま撮っても映りません。そこでこのワールドにはVirtualLensのような、カメラの画像を上書きする疑似カメラを置いています。
そもそもカメラによる直接描画ギミックを使っていない限り不要な機構なので、そのつもりがない方は読み飛ばしてください。
- そもそもワールド置きカメラが必要なのは、VRカメラの位置を取る方法が見つかってないからです。残念。
- 疑似カメラに別サイズアバターを描画するため、上の方で説明した機構を丸々複製(+通常の描画も追加)して、疑似カメラの位置を基準で動かし、RenderTextureに描画しています。そのため、このワールドにはSceneDescriptorに設定するものを除いてCameraが7個あります。大変ですね。
- そのカメラで撮った画像をVRカメラに上書きします。そのためにはスクリーン座標をUVとするシェーダー (ざっくりいうと uv = ComputeScreenPos(UnityObjectToClipPos(v.vertex)) を作って巨大なCubeとかに貼ればいけます。
- そのシェーダーはカメラには映るが目には映ってほしくないので、アバターのカメラ描画ではスキップ ( if (_VRChatCameraMode < 0.5) discard; ) しています。
また、このカメラをいい感じの位置に配置できるように無駄に細かい調整も行っています。
- Pickupのオブジェクトは遠くから掴むと手元に引き寄せられてしまうのですが、カメラはそうなってほしくないのでその引き寄せを打ち消します。Pickupのルートとカメラの間にTransformを挟み、掴んだフレームはそのTransformの位置を固定し、離した瞬間にはルートをカメラの見た目の位置に戻しています。
- プレイヤー追従モードを用意していますが、それはいわゆるプレイヤーの位置ではなく、プレイヤーの位置からSteamVRによる移動を差し引いたものに追従しています。使うと自然な動作に見えますが作るのは結構難しかったので、Udon職人の人は頑張って作ってみてください。(聞かれたら教えますが)
- カメラの大きさはアバターのサイズ等によって変化します。サイズ勢でも安心! プレイヤー追従の場合、本当は自分からの相対位置が変わらないように位置もスケールによって変わるのがVRCのカメラの挙動ですが、めんどくさいのでそれは諦めました。
まとめ
- 1ミリも分からなかった人 : あなたは正常です! 異常なのはこの記事なので、これからも安心してバーチャル生活を楽しんでください。
- 1割ぐらい分かった人 : (まだの人は) ワールド製作に挑戦してみましょう!また新たな発見があるかも?
- 5割ぐらい分かった人 : あっと驚くようなワールドを作ってみんなに自慢しよう!
- 8割ぐらい分かった人 : なんで???
- 完全に理解した人 : 我々はUnityエンジニアを募集しています。
この記事が気に入ったらサポートをしてみませんか?