MLAPIでOculus Quest2のマルチプレイ ①プロジェクトを実装するまで


レポジトリのリンク

シリーズ目次

(今ここ→)①プロジェクトを実装するまで
②GCPにサーバー用ビルドをアップ・実行する
③実機でテストするまで

開発環境

Windows10
Unity(AndroidモジュールとLinuxモジュールを開発に使った)
Quest2, RiftSを用いたが、実機テストの時以外は関係がない


対象読者と本記事の目的

黒河さんのサンプルプロジェクト
黒河さんのサンプルプロジェクト解説動画
その他の記事やUnityの公式ドキュメント
などを見て、ある程度MLAPIについて把握したVRエンジニアの方々

僕がVRでMLAPIを実装した際の工夫を把握できる

本題

0. MLAPIマルチプレイのイメージ

まず、マルチプレイのイメージを共有した方が分かりやすくなると思うのでします。ご存じの方はスルーして下さい。
自分は最初これすら知らずに実装したので苦しみました。なお、ヘッドレスサーバーに全QuestがClientとして接続するのを前提に図式を書きました。Relayサーバーを用いた場合などは図式が変わります。


①オンライン上の共通のSceneに接続するイメージではありません。

②各個人のSceneに通信で生じたデータが加えられるというイメージが正しいです。
AさんのSceneとBさんのSceneにあるGameObjectは違うものということです。

③そのため、Aさんのシーンの変化をBさんのシーンに反映させるには、サーバーを仲介するような操作が必要です。ServerRPCやNetworkedTransformはその操作の例です。フォントが黒いですね。まぁいいや。

④もちろんサーバーから対象のClientに向けて通信を行うこともあります。
新入りが接続した時に全員のシーンに新入りPlayerのプレハブを生成する場合などに使われるSpawnがそれにあたります。


具体的なスクリプトを見る場合は黒河さんのサンプルプロジェクトを覗くのをおすすめします。コメントや注意も日本語で残してあって分かりやすいです。なお、MLAPI改修後にはサンプルやドキュメントが整備されるそうなので、出来次第そちらを見るのもよいかと思います。

本記事にはネットになかった「OculusQuest2でMLAPIを用いたマルチプレイを実装する場合にどうするか」を検証した結果を書きます。基本黒河さんのスクリプトを改変させて頂きました。MITライセンスでした。

1. 接続先IPアドレス入力UIをVR向けにする

これは前提みたいなものですが、黒河さんのサンプルプロジェクトそのままのUIだとVRでIPアドレスを入力することが出来ません。RayなどVR用UIを用いる必要があります。ポート番号は7777で問題ありませんでした。

2. 新入りPlayer接続時は、全Clientに頭と両手(計3つ)のNetworkedObjectを生成させる

Networkで扱うGameObjectには全てNetworked Objectコンポーネントを付ける必要があります。加えて位置を同期する場合はNetworked Transform, アニメーションを同期する場合はNetworked Animationが必要です。

このNetworked Objectコンポーネントを頭を表す黄色い球とコントローラーのGameObjectに取り付けNetworkingManagerのリストに登録した一方で、OVRCameraRigはローカルでのみ扱うようにNetworked Objectコンポーネントを付けませんでした。その代わり、最初に頭のGameObjectをローカルのCenterEyeCameraの子になるようにしました。両手の場合はコントローラーの子にしました。

順を追って書きます。

1. 頭と両手を示すNetworkedObjectを作り、NetworkingManagerに設定した


※頭のGameObject

※ControllerのGameObject

2. Clientが接続してきたら3つのNetworkedオブジェクトをSpawnするようServerManagerを変えた

ServerManagerはサーバーの挙動が書かれたクラスです。

ServerManager.cs
// ネットワーク同期するキャラクターオブジェクトを生成します
private void SpawnCharacter(ulong clientId)
{
       var netMgr = MLAPI.NetworkingManager.Singleton;

       for (int i = 0; i < netMgr.NetworkConfig.NetworkedPrefabs.Count; i++) {
           var networkedPrefab = netMgr.NetworkConfig.NetworkedPrefabs[i];
           var gmo = GameObject.Instantiate(networkedPrefab.Prefab, Vector3.zero, Quaternion.identity);
           var netObject = gmo.GetComponent<NetworkedObject>();

           // このNetworkオブジェクトをクライアントでもSpawnさせます
           netObject.SpawnWithOwnership(clientId);
        }

        Debug.Log("Spawn");
}
3. 頭と両手のGameObjectがOVRCameraRigの中の適切な階層位置になるようSyncTransformをアタッチ

アタッチされたGameObjectがStart()時にparentTagのGameObjectの子要素となるスクリプトです。
これで頭をCenterEyeAnchorの子要素に、両手を各Controllerの子要素にしました。
OVRCameraRigはローカルにあるので、これで各自の頭や手の動きを同期できます。

NetworkedBehaviourとはNetworkに接続しているとき、その状態などを扱いたい場合に使えるクラスです。これを継承することで、IsServer, IsOwnerなどの条件も使えるようになります。基本的にMonoBehaviourに書ける内容は書けました。

!IsServer → サーバーにOVRCameraRigを置かなかったので付けた条件です。
IsOwner → Aさんのものとして扱われるGameObjectはAさんをOwnerとしており、IsOwnerがTrueになります。位置同期の都合上この制限を足しました。

SyncTransform.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MLAPI;

namespace TuneVisualizer
{
    public class SyncTransform : NetworkedBehaviour
    {
        private Transform parent;
        public string parentTag;
        public Vector3 positionOffset;

        public void Start()
        {
            if (!IsServer & IsOwner) {
                transform.parent = GameObject.FindGameObjectWithTag(parentTag).transform;
                transform.position = transform.parent.position + positionOffset;
            }
        }
    }
}

ただし、このスクリプトだとローカル内では同じ位置にGameObjectが被って存在してしまいます。
気になる方は隠して下さい。

[工事中]3. VRのみの話ではないが、Scene内で流れる曲のindexや再生時間を共有できるようにするための操作

NetworkedVarを扱いましたが上手く行かず、工事中です。
新MLAPIのサンプルに期待大。

続き

apkを作ってもサーバー用のビルドをアップ・実行しない限り、マルチプレイは出来ません。
基本黒河さんのサンプルのような内容をLinux用にビルドするだけで機能は作れます。
ローカルで実行し、そこに繋げることも可能ですが、学習がてら次回の記事ではGCP(Google Cloud Platform)の仮想Linuxサーバーにアップ・実行します。

シリーズ目次

(今ここ→)①プロジェクトを実装するまで
②GCPにサーバー用ビルドをアップ・実行する
③実機でテストするまで

Tilt Brush(OSS)をMLAPIでマルチプレイ化しようとして失敗したときの遺言メモ(自分用)

  • TiltBrushは3D絵具のMeshFilterを変えることで、3Dペイントを出力していた。

  • 出来るかもしれないと思ったのは2つ。いずれも現状のMLAPIだと自分は詰まりそうで断念してしまった

  • 新MLAPIリリースに期待。

    • 2020.12.4から数か月後と書いてあったので待つ
    • それ以前にマルチプレイを実装しなければならないならphotonかmirrorを使う