はじめに

こんにちは!

私は戦略技術センターの空間コンピューティンググループの松岡です。

私たちのグループは将来の様々なサービス、インテグレーション、さらに先の社会課題の解決にむけ、空間コンピューティング技術の研究開発を行っています。
空間コンピューティングは現実空間とデジタル情報が融合し、人々が現実空間と直感的にインタラクションを行えるようにする技術です。
今回、空間コンピューティングアプリのデザイン検証のため、TISのUXデザイン専門チームであるXD Studioと協働し、この分野を代表するデバイスの一つであるApple Vision Proを活用したマルチプレイアプリケーションを開発しました。

本記事では、実際に私たちが行ったUnityとPhoton Fusion 2を用いたApple Vision Pro向けマルチプレイアプリの開発過程で得られた具体的な技術知見を紹介します。Unityで開発経験がある方、特にApple Vision Proを活用したアプリ開発を検討している方にとって参考になる内容です。

Photon × Apple Vision Proでの基礎実装

Photonの公式ページには、Apple Vision Proで動作するサンプルやApple Vision Pro専用のアドオンが紹介されています。
Arvr – Apple Vision Pro イントロ | Photon Engine

いくつかのサンプルが提供されていますが、今回はこちらのページで紹介されているチュートリアル動画を参考に、Meta QuestとApple Vision Proのクロスプラットフォームで動作するシンプルなサンプルシーンを構築してみました。

Part1

Part2

 

ただし、今回はMeta Questでの動作確認は行っていません。

開発環境

  • MacBook Pro M1
  • Unity:2022.3.5f1
  • Fusion SDK:2.0.5
  • Fusion XR Addons:2.0.5
  • Photon Voice 2:2.58
  •  XR Plugin Management:4.5.1
  •  Unity OpenXR Meta:1.0.2
  •  XR Hand:1.4.3
  •  com.unity.polyspatial:1.3.11
  •  com.unity.polyspatial.visionos:1.3.11
  •  com.unity.polyspatial.xr:1.3.11
  •  visionOS:2.4

チュートリアル動画との差分

基本的にはチュートリアル動画の通りにアセットを追加し、XR Creation Guideを押していけば、ボイスやオブジェクトの位置などはマルチプレイで同期できました。

しかし、動画で明示的に説明されていない設定があったため、以下に記載します。
動画で説明されていた内容は省略します。

1. XR Plug-in Management

Project SettingのXR Plug-in ManagementのvisionOSタブで、「Apple visionOS」にチェックを入れる必要があります。これを設定しないとApple Vision Proでアバターに自分の動きが反映されません。

2. Apple visionOSタブ

Hand Tracking Usage Descriptionにも適切な説明文を記入しましょう。

3. ハンドモデルの追加

XR Creation GuideのHardware RigのHANDS MODELSとNetwork Rig PrefabのHANDS MODELSの両方を追加する必要があります。これがないとApple Vision ProやMeta Questのハンドトラッキングモードで手のモデルが動作しません。

4. マイク使用権限

ボイス機能を使用する場合、Player Settingsにマイクの使用についても記述しないと、Unityのビルド時にエラーが発生します。

5. その他の設定

シーンのデフォルトMain Cameraを削除し、LayerにlocomotionLayerMask、PolySpatialIgnored、InvisibleForLocalPlayerを追加しましょう。

これらの設定を適切に行うことで、ボイスチャット、手の動き、アバターがApple Vision Proでも正常に動作するようになります。

作ったもの紹介

このようにして実装したサンプルシーンの上に、今回はボードゲームの要素を実装しました。このボードゲームは家族の会話を引き出すことを目的とした陣取りゲームです。お題に基づき参加者同士で投票し、投票で得たアイテムをもとに暖炉に薪をくべることで陣地を拡大し、最も多くの薪を置いた人が勝利します。

同期実装トピック

以下では、このゲームを作るにあたり実装したマルチプレイ機能の同期処理について詳しく説明します。AppleVisionProでの実装に特化した話ではないので、Photon Fusion2でのマルチプレイ実装全般で参考になるかもしれません。

共有モードとホストモード

Photon Fusion 2のマルチプレイには「共有モード」と「ホストモード」の2つのモードがあります。実装前にどちらのモードを採用するか決定する必要があります。

以下の記事を参考に、今回のアプリでは共有モードを選択しました。

Fusion 2 – Fusion 2 トポロジーの選択 | Photon Engine

Photon Fusion 入門の入門

共有モードを選んだ理由は、以下の点が本ゲームに適していたためです

  • プレイヤー間で対等な関係を維持したかった(ホスト/クライアントの区別なし)
  • ボードゲームのような比較的シンプルな状態同期で十分であった
  • 全プレイヤーが同じ状態を共有する必要があるゲーム性

 

オブジェクトのInstantiateをSpawnに置き換える

今回のゲームでは、最も投票数が多かったプレイヤーのテーブルには薪オブジェクトが、それ以外のプレイヤーにはカードオブジェクトが表示されます。

一人プレイ用に実装していた時はオブジェクトの表示はInstantiateで実装していましたが、Photon Fusion 2ではすべてSpawnに置き換える必要があります。

networkRunner.Spawn(cardPrefab, spawnPosition, cardSpawnPoint.rotation, networkRunner.LocalPlayer);

スポーンするオブジェクトには必ずNetworkObjectコンポーネントを追加しておく必要があります。

スポーンに関しての詳しい情報は公式ドキュメントを参照してください。

Fusion 2 – スポーン(基礎) | Photon Engine

状態の同期

お題の提示などのゲーム状態を同期するために、静的なRPC(Remote Procedure Call)を使用して、関数呼び出しの同期を行いました。

参考:Fusion 2 – リモートプロシージャコール | Photon Engine

例として、ランダムでお題を表示した時にそのお題の表示を他のプレイヤーに共有するのは以下のような流れで実装しました。

//ランダム関数
private void DisplayRandomTopic(int playerIndex)
{
int randomIndex;
do
{
randomIndex = UnityEngine.Random.Range(0, topics.Length + firewoodCount + sandCount);
} while (usedIndices.Contains(randomIndex) && randomIndex < topics.Length);

//RPCの関数呼び出し
RPCSyncRandomIndex(networkRunner, randomIndex, playerIndex);
}
[Rpc]
public static void RPCSyncRandomIndex(NetworkRunner runner, int cardIndex, int playerIndex)
{
instance.DisplayTopic(cardIndex, playerIndex);
}
//randomIndexに従ってお題を表示する
private void DisplayTopic(int randomIndex, int playerIndex)
{
// お題表示の実装
}

アバターのスポーンの独自実装

ゲームに参加したときのアバターのスポーン同期はチュートリアルにも含まれています。しかし今回は丸い暖炉を囲むように円形にスポーンさせたかったため、既存のConnectionManagerコンポーネントを使わずに新しくGameMasterコンポーネントを作成し、独自のスポーン実装を行いました。

GameModeの設定など、ConnectionManagerに記述されていた内容は、GameMasterに移行しています。

以下のコードでゲームを開始します。

var result = await networkRunner.StartGame(new StartGameArgs
{
GameMode = GameMode.Shared,
SceneManager = networkRunner.GetComponent()
});

スポーンに関しては以下のように実装します。

var playerObj = networkRunner.Spawn(playerAvatarPrefab, circularLayoutGroup.position, Quaternion.identity, networkRunner.LocalPlayer);

アバターのプレハブだけでなくカメラなどが含まれるHardwareRigの位置も、アバターをスポーンさせたい場所に移動させる必要があります。

また、今回はプレイヤーごとに担当色があったため、アバターの服の色を担当色に設定しました。このようなスポーンするオブジェクトごとのカスタマイズ処理は、プレハブ自身に付けたスクリプトでNetworkBehaviourを継承し、public override void Spawned()関数内で設定する必要があります。この設定をしないと他のプレイヤーに同期されません。

ちなみにこのアバターのスポーンの独自実装でConnectionManagerを使わなかった結果、チュートリアルの時点では機能していた、シーン内に最初から存在するオブジェクトを掴んで動かした際の位置同期ができなくなってしまいました。この問題は解決できていません。

おまけ:実写の手の前面表示について

Photonとは直接関係ありませんが、Apple Vision Proでの手の表示に関する補足情報です。

Apple Vision Proで自分の実際の手がオブジェクトより前面に表示されるようにしたいときがあります。Unity 6000ではデフォルトで手や腕がオブジェクトの前面に表示されますが、Unity 2022.3.5では表示されません。

これは、XR Plug-in ManagementのApple visionOSセクションにある「Upper Limb Visibility」をオンにすることで、手を前面に表示できます。

Upper Limb Visibilityオン状態

Upper Limb Visibilityオフ状態

最後に

今回の取り組みを通じて、空間コンピューティングデバイスでのアバターを介したコミュニケーションは、離れた場所にいるメンバーでも同じ空間に一緒にいる感覚があり、リモートワークや拠点が離れているメンバーとのチームビルディングなどにも活用できると感じました。

またこの記事では触れていませんが、平面上で遊ぶ2Dのアナログゲームを、空間を活かして体験できる3Dゲームへと再設計するにあたり、空間コンピューティングデバイスの特性を最大限に活かすため、さまざまな工夫を凝らしました。

これらの実際に複数人でプレイして得られた気づきや、空間デザインに関してより詳しく知りたい方は、XD Studioがnoteで公開した記事をご覧ください。

SIerのUXデザイン組織がApple Vision Proで遊べるゲームを作ってみた|TIS XD Studio

空間コンピューティングはまだ発展途上の分野ではありますが、私たちはその可能性と価値を強く信じています。

今後も研究開発を継続しながら、その価値を広く届けていくための発信にも力を入れていきます。こうした情報発信を通じて、共に取り組んでいただける協業パートナーとの出会いにも期待しています。

空間コンピューティングの取り組みについて、詳しく知りたい方や協業をご希望の方は、ぜひこちらまでご連絡ください。