Expo
Fusion アプリに XR Interaction toolkit を組み込む方法についてヘルプが必要な場合は、Circle Discord の VR チームにご連絡ください。
概要
ExpoFusionのサンプルでは、Fusionを使って最大100人のプレイヤーが参加できるソーシャルアプリケーションを開発する方法をご紹介しています。
各プレイヤーはアバターで表現され、Photon Voice SDKにより、同じチャットブルブルにいる他のプレイヤーと会話することができます。
このサンプルのハイライトをいくつか紹介します。
- まず、アバター選択画面で、プレイヤーがアバターをカスタマイズします。
- その後、Expoに参加することができます。PC/MACで起動した場合、キーボードとマウスを使用するDesktopモードと、Meta Questヘッドセットを使用するVRモードが選択できます。
- 同じスタティックチャットバブルにいるプレイヤー同士は会話することができます。各チャットバブルにはロックボタンが用意されており、新規プレイヤーの侵入を防ぐことができます。
- また、2人のプレイヤーが接近している場合、速度の遅いプレイヤーの周囲にダイナミックチャットバブルが発生します。
- 3Dペンで3Dドローイングを作成することができます。各3Dドローイングはアンカーで移動することができます。
- また、クラシックホワイトボードも用意されています。各図面はアンカーを使用して移動することができます。
より詳細な技術的な情報は、コードコメントに直接記載されています。
技術要件
- このサンプルはShared Modeトポロジーを使用していますが、コアはShared ModeとHost Modeトポロジーに互換性あり
- PC、Mac、Meta Questで実行可能
- Unity 2021.3.3f1によるプロジェクト構築,
- Unity XR Interaction Toolkit 2.0.2 互換性あり
- 2 通りのアバターソリューションに対応(手作りのシンプルなアバターおよび Ready Player Me 1.9.0 avatars)
始める前に
サンプルを実行するには :
PhotonEngine Dashboard で Fusion AppId を作成し、Real Time Settings の
App Id Fusion
フィールドに貼り付けます(Fusion メニューからアクセス可能)。PhotonEngine Dashboard で Voice AppId を作成し、Real Time Settings の
App Id Voice
欄に貼り付けます。次に、
AvatarSelection
シーンをロードして、Play
を押します。
ダウンロード
バージョン | リリース日 | ダウンロード | ||
---|---|---|---|---|
0.0.19 | May 12, 2023 | Fusion Expo XRShared 0.0.19 Build 192 |
入力処理
デスクトップ
キーボード
- 移動 : WASD または ZQSD で歩行
- 回転 : QE または AE で回転
- Botスポーン: Bボタンを押すと、ボットが1体生成されます。1秒以上押し続けると、リリース時に50体のボットを作成します。
マウス
- 移動:マウスを左クリックするとポインタが表示されます。ポインタを離すとターゲットにテレポートします。
- 回転 : 右ボタンを押しながらマウスを動かすと、視点が回転します。
- 移動して回転 : 左ボタンと右ボタンを押したまま前進します。マウスを動かしても回転させることができます
- 掴んで使用 (3Dペン) : マウスをオブジェクトの上に置き、マウスの左ボタンで掴みます。その後、キーボードのスペースキーで使用することができます。
Meta Quest
- テレポート:A、B、X、Y、または任意のスティックを押してポインターを表示します。ポインターを離すと、任意のターゲットにテレポートします。
- タッチ(チャットバブルのロックボタンなど):ボタンに手をかざすだけで、ボタンが切り替わります。
- グラブ:まずオブジェクトに手を置いて、コントローラのグラブボタンを使用してそれをつかむ
- Botスポーン : 左コントローラーのメニューボタンを押すと、1台のロボットが生まれます。1秒以上押し続けると、リリース時に50体のボットを作成します。
フォルダ構造
メインフォルダ /Expo
には、このサンプルに固有のすべての要素が含まれています。
フォルダ /Expo/Integrations
は ReadyPlayerMe のようなサードパーティーソリューションとの互換性を管理します。
Photon
フォルダーには、Fusion と Photon Voice SDK が含まれています。
Photon/FusionXRShared
フォルダには、VR共有サンプルからのリグとグラブロジックが含まれており、他のプロジェクトと共有できるFusionXRShared light SDKが作成されています。
Photon/FusionXRShared/Extensions
フォルダには、FusionXRSharedの拡張機能が入っており、同期光線、ロコモーション検証などの再利用可能な機能があります。
Plugins
フォルダーには、Ready Player Me SDK が含まれています。
StreamingAssets
フォルダには、あらかじめ作成されたReadyPlayerMeのアバターが格納されています。これらのプリビルドアバターを使用しない場合は、自由に削除することができます。
XRI
フォルダと /XR
フォルダには、バーチャルリアリティの設定ファイルが格納されています。
ネットワーク接続とアプリケーションのライフサイクル
ConnexionManager
は、共有モードのトポロジーで Fusion セッションを起動し、接続された各ユーザーのプレハブを生成します。
SessionEventsManager
は Fusion と PhotonVoice の接続状態を監視し、それらに興味を持つコンポーネント、特に様々なサウンド効果を処理する SoundManager
に警告を発します。
オーディオ
VoiceConnection
と FusionVoiceBridge
コンポーネントは、Fusion セッションに沿ったオーディオ Photon Voice 接続を開始し、Recorder
コンポーネントは、マイク入力をキャッチします。
Oculus Quest のビルドでは、追加のユーザー認証が必要です。このリクエストは MicrophoneAuthorization
スクリプトによって管理されます。
ユーザープレハブは、頭の上に Speaker
と VoiceNetworkObject
を配置し、音声を受信すると空間化されたサウンドを投影します。
Photon Voice と Fusion の統合の詳細については、こちらのページを参照してください。https://doc.photonengine.com/en-us/voice/current/getting-started/voice-for-fusion
リグ
没入型アプリケーションでは、リグはユーザーを表現するために必要なすべての可動部、通常は両手、頭、プレイエリア(ユーザーがテレポートしたときなどに移動できる個人的な空間)を表します。
ネットワークセッション中、すべてのユーザーはネットワーク化されたリグによって表現され、その様々なパーツの位置はネットワーク上で同期されます。
ここでは、ユーザーは1つの NetworkObject
で表現され、各リグパーツに1つずつ、いくつかの NetworkTransforms
がネストされています。
ローカルユーザーを表すネットワークリグの場合、このリグはハードウェア入力によって駆動される必要があります。この処理を簡単にするために、ネットワークに接続されていない別のリグを作成し、「ハードウェアリグ」と呼んでいます。ハードウェアの入力を収集するために、Unity の古典的なコンポーネント(TrackedPoseDriver
など)を使用します。そして、ローカルユーザーに関連付けられたネットワークリグに対して、すべてのネットワークリグのパーツは、一致するハードウェアリグのパーツを単純に追従します。
ユーザープレハブにある XRNetworkedRig
コンポーネントは、ネストされたすべてのリグパーツに対してこのトラッキングを管理します。
XRNetworkRig
コンポーネントは、FixedUpdateNetwork()
の間に他のプレイヤーとリグパーツの位置を共有する以外に、外挿処理も行います。Render()
の間に、様々なリグパーツの NetworkTransforms
をグラフィック表示する内挿ターゲットを移動し、ローカルユーザーがネットワークティック間でも常に最新の位置で手を見られるようにするためです。
ローカルユーザーに関連する XRHardwareRig
とそれにマッチする XRNetworkRig
を簡単に見つけるために、コンポーネント RigInfo
が XRHardwareRig
と全ての XRNetworkedRig
を登録し、今後使用することができます。
Interaction Stack
このサンプルでは、プレイヤーが移動したり、オブジェクトを掴んだり、サーフェスに触れたりすることができます。
これらの操作はすべて、ネットワークとは無関係にローカル(ハードウェアリグ上)で行われ、その後、ネットワークリグへのFusion入力を通じてネットワークに送信されます。
このようにすることでUnity XR Interaction Toolkit (XRIT)互換性を実現しています。XRITを使用したうえで他の方法を実行すると、複雑さが増してしまいます。
XR Interaction Toolkit
XR Interaction Toolkit (XRIT)をFusionで使用するためには、いくつかの適応が必要です。
Fusionで選択したトポロジーや物理設定によっては、XR Interaction ToolkitとFusionが同じエレメントを扱おうとすることがあるため、シームレスに動作させるために、以下の変更を加えました。
- XRITのコンポーネントの中には、Unityの物理フェーズの開始時、途中、終了時に処理が分かれるものがあります。Photonは物理シミュレーションを管理できるため、XRITにとっては予期せぬ順番になることがあります。そこで、Fusionの実行順序を認識させるためにXRITクラスのサブクラスが作成されました。
注:これらの修正のいくつかは、すべてのトポロジーに必要なわけではありませんが、現在のバージョンでは、すべてのケースで動作するように同居させる方法を示しています。 - 共有モードでオブジェクトを掴む場合、FusionとXRITの両方が、そのRigidBodyのisKinematicプロパティを編集することがあります。コンポーネントでは、Fusionのプロパティで利用可能な実際の値をXRITが認識できるようにしています。
Locomotion
仮想現実では、レイベースのテレポートと、ジョイスティックのスナップターンで移動することができます。この運動は、XR Interaction toolkitによって管理され、要求されたときだけテレポートレイを有効にするように若干の変更が加えられています。
デスクトップ版では、キーボードで移動するか、マウスで地面を左クリックするとそこにテレポートすることができます。DesktopController
と MouseTeleport
コンポーネントが移動を管理し、 MouseCamera
コンポーネントがマウスの右クリックの動きを使ってカメラの移動を管理します。
リモートユーザーのために、プレイエリアの移動はスムージングされ、テレポートをターゲットへの漸進的な移動に置き換えます(instantPlayareaTeleport
オプションが true に設定されていない場合に限る)。
ロコモーションの制限
このサンプルアプリケーションでは、ユーザーがある場所に行くことができないことがあります(例えば、チャットバブルの最大容量に達している場合は、チャットバブルに入ることができません)。
そのため、ユーザーを移動させたいすべてのコンポーネントは、一般的なロコモーション検証システムと、特定のロコモーションモードに関連する制約の両方に依存することになります。
ロコモーションの検証システム
ユーザーが禁止区域に行こうとしないかどうかを判断するために、すべてのロコモーションシステムはまず XRHardwareRig
に CanMove()
メソッドでこの位置まで移動できるかどうかを尋ねます。XRHardwareRig
はまず、すべての ILocomotionValidator
の子と、ネットワーク上のローカルユーザーを表す XRNetworkedRig
インスタンスのすべての ILocomotionValidator
の子で移動が有効であるかどうかをチェックし、答えを出します。
さらに、ユーザーが禁止区域に頭を置くと表示が消え、「ズル」をしてしまうことを防止します。\
その他の制限
さらに、ロコモーションは、使用するロコモーションシステムによって、他の要素によって制限されます。
- XRIT Locomotion : ユーザーは
TeleportArea
コンポーネントを持つオブジェクトにのみテレポートすることができます。 DesktopController
ロコモーション : コントローラは移動後の頭の位置が正しいかどうかをチェックします。コライダー内にないことをチェックし、移動後に正しい歩行可能なナビゲーションメッシュポイントがその下にあることをチェックします。- ボット : ボットが歩行可能なnavmesh上に留まることを確認します。
Touchable
指が触れたらイベントが発生するようにするため、手には Toucher
コンポーネントがあり、いくつかのオブジェクトには Touchable
コンポーネントがあります。このイベントは完全にハードウェアリグで処理され、ネットワークへの自動的なリンクはありません: Touchable.OnTouch
によって呼び出されるコンポーネントで処理する必要があります。
デスクトップコントロールではマウスポインタでTouchable
をタッチできることに注意してください。
Grabbing
このサンプルでは、ペンや図面操作の処理にのみグラビングを使用しています。
グラブシステムは Tracker
クラスをベースにしています。これは、ネットワークに接続されたオブジェクトが、様々なトラッキングロジック(瞬間移動、力による移動など)で、他のオブジェクト(手のようなもの)を追跡することを可能にします。また、必要に応じて、権限の変更に伴う自然な動きや、使用するトラッキングロジックがサポートしている場合のオブジェクトとの衝突など、外挿も処理します。
トラッキングシステムは、共有トポロジーとホストトポロジーの両方をサポートするように構築されており、ここではその機能のごく一部を使用しています。注目すべきは、ここではフォースベースのトラッキングではなく、瞬間的なトラッキングにのみ使用されていることです。
ここでは、実際に掴む場合ははXR Interaction Toolkitを使って、ローカルで行われます。そして、掴まれたオブジェクトが掴んだ手を追跡("追いかける")しているという事実が、Tracker
コンポーネントでネットワーク上で共有されます。
入力システムは、この情報をつかむハードウェアの手からネットワーク上の手に伝え、最終的にTracker
に警告するために使用されます。
アバター
ユーザーは、アバターと呼ばれるグラフィカルな表現で表現されます。このサンプルでは、Ready Player Meのアバターと、よりシンプルなカスタムのアバターの2種類をサポートしています。
Gazer
Gazer
より自然なアバター表現を実現するため、アバターの前に興味のあるオブジェクトが提示されると、アバターの目はそのターゲットを見つけ、追従する自動アイトラッキング機能を備えています。より近いターゲットが提示されると、目の焦点を変更します。
ターゲットがない場合、視線は時々、ランダムに動きます。
最後に、本サンプルで利用可能なすべてのアバターシステムは、より自然に見えるように、目のまばたきを処理します。
目のターゲットになるには、GameObject に GazeTarget
コンポーネントがなければなりません。すべてのアバターの目と頭には、デフォルトでそのようなコンポーネントがプリビルドされたプレハブで用意されています。これらの GazeTarget
は GazeInfo
マネージャに登録されています。
Gazer
コンポーネントは、GazeInfo
に潜在的な (有効な目の角度とターゲット距離の) GazeTarge
のソートされたリスト (距離による) を問い合わせて、目を駆動させます。
ソート処理は、多くの利用可能なターゲットが近くにある場合、非常に重くなり、バックグラウンドスレッドで実行されます。
手
ここで使用されている手のモデルは、Oculus Sample Framework (Facebook Technologies, LLC およびその関連会社により BSD-3 ライセンスの下でリリース) から来ています。
手の動きを駆動する入力アクションはローカルで収集され、Fusion上で共有され、すべてのクライアントに手の動きを表示します。
手の動きは、変化の頻度を減らすために、リモートで少し離散化されています。
手の色は、使用したアバター表現の肌の色と一致させています。
AvatarRepresentaiton
各アバターは、NetworkRig
に配置された UserInfo
コンポーネントの Fusion [Networked] var を通して共有される avatarURL を保存します。
変更されると、このURLは AvatarRepresentation
コンポーネントによって解析され、以下を表しているかどうかが判断されます。
単純化されたアバターの説明 URL
(例: simpleavatar://?hairMesh=2&skinMat=0&clothMat=3&hairMat=0)
Ready Player Meモデル URL (ex:https://d1a370nemizbjq.cloudfront.net/0922cf14-763d-4e48-aafc-4344d7b01c2c.glb)
シンプルなアバター
シンプルなアバターシステムは、シンプルで安価なアバターモデルを提供します。
口のアニメーションはボリューム検出によるもので、正確な口パクはできません。
Ready Player Me
The Ready Player Meのアバターシステムはhttps://readyplayer.meにより提供される全てのアバターを表示することができます。
口のアニメーションは、Oculus Audio SDK ライセンスの下でリリースされた Oculus Lip Sync ライブラリに基づいて、口パクを提供します。 (https://developer.oculus.com/licenses/audio-3.3/).
アバターのダウンロードと読み込みを最適化するために、与えられたアバターURLに対して、アバターオブジェクトの読み込みは、いくつかの方法で行うことができます。
- この URL がすでにアクティブなアバターに使用されている場合、既存のアバターはクローンされます。
- この URL がプレハブに関連付けられている場合、glb ファイルをダウンロードおよび解析する代わりに、プレハブが生成されます。
- これらのオプションが適切でない場合、glb ファイルがダウンロードされ、解析されます。URLに
%StreamingAssets%/Woman1.glb
という構文を使うことで、StreamingAssetsフォルダにあるglbファイルを記述し、ダウンロードをスキップさせることができることに注意してください。その場合、Woman1.glb
の隣に、Ready Player Meが提供する関連メタデータファイルWoman1.json
を置く必要があります(ダウンロードするには、Ready Player Meが提供するオリジナルのURLの拡張子を.glbから.jsonに置き換えます)。
LOD
多数のユーザー(またはボット)が参加するセッションでは、アバターがパフォーマンスに大きな負担をかけることがあります。
これを避けるため、アバター表現は以下のいくつかのLODを管理しています。
- 通常のアバター(シンプルアバターまたはレディプレイヤーミー)。
- シンプルなアバターのローポリバージョンで、髪や肌、服の色は通常のアバターと同様。
- 常にユーザーカメラの方を向いているビルボード。
チャットバブル
このサンプルは、チャットバブルのデモです。シーンには、4つの静的なチャットバブルが含まれています。また、2人のユーザーが近づくと、動的なチャットバブルを生成することができます。
静的チャットバブル
デフォルトでは、Expoシーンはユーザー同士は声が聞こえません。
しかし、同じ静的なチャットバブルに入ると、Photon Voiceを介した空間化された音で一緒に議論することができるようになるのです。
サンプルでは、ゾーンシステムは NetworkRig
が Zone
に近い ZoneUser
コンポーネントを追跡しています。それが発生すると、ZoneUser
はゾーンに入り、様々なZone
とZoneUser
のリスナーをトリガーします。
Zone
はPhotonVoiceのインタレストグループを関連付けることができるので、ZoneAudioInterestChanger
コンポーネントは、ローカルユーザーがゾーンに入ったり出たりすると、オーディオのリスニンググループと録音グループを変更することができます。
静的チャットバブルは、最大ユーザー数に達すると自動的にロックされますが、各静的チャットバブルにあるロックボタンをタッチすることで、その前にロックすることも可能です。
動的チャットバブル
ユーザーを代表するプレハブはDynamicZoneSource
コンポーネントを含んでいます。
このコンポーネントは DynamicZonePool
コンポーネントに登録され、別の既存の DynamicZoneSource
が DynamicZonePool.size
で定義されているように近接しているかどうかをチェックできるようになります。その場合、DynamicZonepool
は両方のユーザーが使用するために生成された(または再利用された)ゾーンを提供することができます。
他のユーザーは後から参加することができ、ゾーンの最大容量に達するまで参加することができます。
ボット
このサンプルでは、多くの人が集まる博覧会がどのようにサポートされるかを示すために、通常のユーザーだけでなく、ボットを作成することも可能です。
ボットは、ネットワークに接続された通常のプレハブで、音声は無効化されており、ユーザー入力の代わりにナビゲーションメッシュによって駆動されます。
また、Bot
クラスはロコモーション検証システムを使用しているため、チャットバブルを認識することができます。さらに、話すことができないので、ロックされているかどうかに関わらず、ゾーンに入ることは禁じられています。
描画
3D 描画
Expoシーンには、3Dドローイングを作成するための3Dペンが含まれています。ラインレンダラーのグループで、共通のハンドルを持ち、つかんで移動することができます。
3Dペンは Drawer
コンポーネントを保持しており、このコンポーネントが Draw
コンポーネントを保持する描画プレハブを生成します。Draw
コンポーネントは、すべての描画点がFusion上で同期されるようにします。これは [Networked]
変数を通して行われます。無限にポイントを保持することはできないので、必要に応じて描画はいくつかのパーツに分割され、ユーザーが移動させると2番目の Draw
が最初の Draw
に続き、1つの描画として表示されます。
2D 描画
Expoのシーンにあるボードは、その周りにペンで書くことができ、そして出来上がった図面は、3Dペン図面のハンドルと同様のハンドルでボード上を移動することができます。
実際には、レイヤーカメラからは見えないが、ボードに関連付けられたカメラからは見えるレイヤーを使用した、特殊な3Dドローイングである。そして、カメラはボード上に表示されるレンダーテクスチャーに描画イメージをレンダリングします。
パフォーマンスのために、このカメラは、ボードの近くのペンが使用されるとき、またはそれらで作成された描画が移動されるときのみ有効になります。
サードパーティのコンポーネント
- Oculus Integration
- Oculus Lipsync
- Oculus Sample Framework hands
- Ready player me
- Sounds
既知の問題
- Oculus Quest : ヘッドセットが長い間スタンバイモードになっていると、アプリケーションを終了することができません。その後、ヘッドセットの再起動が必要です。
- Oculus Quest : ヘッドセットを長時間スタンバイ状態にしていると、アプリを終了できません。