hyoromoのブログ

最近はVRSNS向けに作ったものについて書いています

VRChatでのInteractとuGUIの使い方について


VRChatはDesktop操作があるため、VR必須だと公言しておかなければDesktop(腕1本)+VR(腕2本)それぞれで動作するワールドを制作する必要があります。
今回はそのことを踏まえ、どのように作ると良いかを思い悩んだ事を書きます。

制作ワールド


Interact/uGUIをどう使い分けるかこだわって作ったワールドで、今回はこのワールドでの話がベースとなります。
実装に対して実際どうなったかはワールドへ一度足を運ばれるのが早いです。だから行って!

開発環境

  • Unity2019.4.31f1
  • VRCSDK3-WORLD-2021.11.24.16.19_Public
  • UdonSharp_v0.20.3

InteractとuGUIの違いについて

VRor Desktop 種類 操作GIF
Desktop Interact
VR Interact
Desktop uGUI
VR uGUI

それぞれ発火までの手順を挙げてみましょう。

種類 イベント発火までの操作手順
Interact 1. 対象物の近くへ手を近づける
2. トリガーを引くことで発火
uGUI 1. World座標に配置されたCanvasの方を向く
2. プレイヤーがその方向へ手をかざすとRay(レーザー)の先にポインタ表示
3. 発火可能なUI上でトリガーを引くと発火

それを踏まえた特徴も挙げます。

種類 特徴
Interact ・360度どこから近付くことで発火
・近付くというのがプレイヤーに分かりにくく、隣接すると操作しにくい印象を与える*1
・距離が離れると操作不能なため、距離に対して工夫は必要なケースが少ない
uGUI Canvasのある方向から距離に関係なく発火
マウスポインタのような操作感覚で狙ったUIのイベントを発火しやすい*2
・そのまま配置すると遠い距離からも操作可能なため、間にColliderを配置する等の工夫が必要

といったものが特徴です。
ここまでDesktopでの話を一切してきませんでした。ではDesktop/VRでの差は何なのか?

種類 DesktopとVRの差
Interact ・Desktopはカメラからrayが飛び、カメラとの距離が近いと触れる
uGUI ・Desktop/VRに差は無い*3

そう。InteractでのみDesktop/VRで差が生じてしまうのです。
なので普段Desktopで開発してInteractを並べて配置してしまうと・・・いざVR操作する時に触れたい対象に触れられない!なんて事が発生します。

どういったケースでInteractとuGUIをどちらを使うべきか?

ここからは私見です。現時点の私の考えであって未来で変わる可能性がありますし、世間一般的な考えでない事を留意ください。

Interactを使うパターン

Interact最大の良さは "VRらしさ" です。
手を伸ばして、触れて、発火させる。uGUIだとほぼ手を動かずコントローラーの傾きだけで済んで味気なく感じてしまいます。

それとCanvasと違って特定の方向に向けるとRayが飛ぶ訳では無いため、発火対象物が常に固定座標に無い場合はInteractの方が使い勝手が良いです。
例えばPickup可能なObject上にuGUIを配置した場合、手から離した状態がどういう向きになるかも分からないためRayが出続けるような鬱陶しい状況になる可能性があります。

まとめると...

  • Interactの方がVRっぽい操作ができる
  • 動く物に配置しやすい
uGUIを使うパターン

uGUI最大の良さは "細かい操作のしやすさ" です。
前述したようにInteractは近距離に複数並べた場合はVRで操作しにくいです。その点uGUIはVRは手から、DesktopはカメラからのRayで操作出来るため詰まった配置をしても問題なく操作可能です。

それに加えてDesktop/VRで操作にほぼ差が無い事によって工数削減に繋がります。Interact使用箇所をDesktop/VR両方で確認&対応はコストが高く、両方確認後に配置調整/コードで分岐対応とするのは珍しくないです。

まとめると...

  • 細かい操作がしやすい
  • VR/Desktop対応コストが低い

それでもInteractを多用してVRらしさを追求したい!

PickupもInteractなのでそうは言ってもInteractを並べて使いたいケースがあります。
そうした時に「[Unofficial] 765PRO VR Live Theater 」ワールドではどう対処したかを書いていきます。


作成ワールドには動画情報を表示するタブレットを配置しています。
タブレットは中央にPickup、上/左/右/左下/右下それぞれにInteractを設定しています。
この場合、中央に近い位置で外のInteractに触れると 中央のPickupに反応して外のInteractを触れません。

その解決策は2つあります。

1つ目はColliderとInteract/Pickupのproximityを小さくする事です。
Colliderを小さくすると単純に手と距離が近付き難くなったり、他Interactと隙間が大きく取りやすくなります。
proximityは手とInteractが反応する距離で、小さくすればするほど近付かないと反応しなくなります。

2つ目はタブレットのPickupに付けたColliderを覆うColliderを配置する事です。

そしてPickupは初期状態ではpickupableをfalseにしてPickup不可状態とし、覆っているColliderに手が入ってきたらpickupableをtrueにしてPickup可能状態にします*4

[SerializeField]
VRC_Pickup _pickup;

[SerializeField]
Collider _pickupAreaCollider; // Pickup周囲を覆っているCollider

void Start() {
    // 非VRであればエリアは不要
    if (!Networking.LocalPlayer.IsUserInVR()) {
        _pickup.pickupable = true;
        Destroy(gameObject);
    }
}

public void Update() {
    // 手の座標を取得してエリアCollider内にあるか/ないかでPickup可不可を設定
    var trackingRightHandData = Networking.LocalPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.RightHand);
    var trackingLeftHandData = Networking.LocalPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.LeftHand);
    if (_pickupAreaCollider.ClosestPoint(trackingRightHandData.position) == trackingRightHandData.position
        || _pickupAreaCollider.ClosestPoint(trackingLeftHandData.position) == trackingLeftHandData.position) {
        // Collider範囲内にどちらかの手が存在
        if (!_pickup.pickupable) {
            _pickup.pickupable = true;
        }
    } else {
        if (_pickup.pickupable) {
            _pickup.pickupable = false;
        }
    }
}

こうする事でタブレットぎりぎりまで手を近付けないとPickup要求される事はありません。
Interactの場合は試したことありませんが UdonBehaviour#DisableInteractive あたりで制御できそうです。

本当は1つ目だけで解決したいところですが、Collider/proximityが小さすぎると今度は単体でInteractし難くなります。

まとめ

自分の中での整理が一旦出来て満足でした。
他の方の理想的なInteract/uGUI使い分け方法も聞いてみたいのでコメントか、VRChatで直接会った時か、同じテーマで書いて頂けると興味深く読まさせて頂きます。

*1:旧カメラのボタンを思い出すと分かりやすかと

*2:VRCメニュー操作と同じ

*3:厳密にはボタン等の押下時のハイライトとか差が出るが些細な話

*4:コード量減らすためにリファクタリングしたため、そのままでは動かないかもしれません...