UnityでマルチプレイVRを一番簡単に実現する方法

# 概要
「Unityで一番簡単に実現する方法」シリーズ第二弾です。

[前回](https://qiita.com/warapuri/items/d439559306176bd85f9f)は「Unity でVRのテレポートを一番簡単な実装」ということで、VRテレポートシステムを作りました。今回はそれを引き続いて行きます。[前回](https://qiita.com/warapuri/items/d439559306176bd85f9f)をやっていない方はそちらからやっていってください。

今回は前回やったVRテレポートを使って、多人数が同じ空間参加できるようにすることを実現しようと思います。多人数と言ってもMMO的な大規模ではなく、数人レベルで、かつ同じLAN内での利用を想定しています。

実装の順番としては

1. Forge Networking インストール
1. マッチング画面シーンのVR対応
2. Playerオブジェクトの追従
3. Network Contact Wizardを使ったコード自動生成
4. Playerクラスの実装
5. Player のPrefab化
6. NetworkManager の修正
1. GameLogic で Player の生成
1. 確認

という段階を踏んでいきます。

# Forge Networking インストール

ここではForge Networking というアセットを使って、マルチプレイを実現します。

![f690d7de-56e1-44dc-ac61-fe2740deb2b8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/39aa14bb-2001-7b80-8098-05082be6ce87.jpeg)

Asset Store からダウンロードしてインポートしてください。

# マッチング画面シーンのVR対応

スタートしていきなり多人数接続というわけにはいかず、ホストとして参加するか、そのホストにつないで参加するか、という選択画面が必要になります。それがいわゆるマッチングというものです。

マッチング画面シーンである

Assets/Bearded Man Studios Inc/Scenes/MultiplayerMenu.unity

を開いてください。このままではデスクトップ画面ですので、これをVR対応する必要があるのですが、ここでは手っ取り早く

- 何も見えないけどボタンを押す（Connect）
- ホストに繋がって次のシーンへ遷移する

という適当遷移として実装します。（本来であれば、ホストのIPアドレスを打って、ConnectというUI画面対応をしたほうが良いのですが、ここでは端折ります）

とりあえず、「File→Save As...」でこのシーンを「MultiplayerMenuVR.unity」とかに変更しておきましょう。

そして、

	private void Start()
	{
		ipAddress.text = "127.0.0.1";
		portNumber.text = "15937";
                  :

のipAddress.text の部分のIPアドレスをホストとなるPCのIPアドレスにします。
IPアドレスは コマンドラインから ipconfig(Macならifconfig)で 調べてください。
私の場合は

		ipAddress.text = "192.168.1.12";

こんな感じになります。（皆さんそれぞれで違うはずです）
次に前回も使った

Assets/VRTK/Prefabs/CameraRig/UnityXRCameraRig/InputMappings/UnityXR.OpenVR.RightController.prefab

をシーンにD&D

<img width="581" alt="Unity_2018_3_0f2_-_MultiplayerMenuVR_unity_-_MultiplayTest_-_Android__Metal_.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/b289d2f1-e4c7-1df2-80a9-4a85e5e8a11f.png">

そのUnityXR.OpenVR.RightControllerの中の項目を変更します。

1. UnityXR.OpenVR.RightController>Trackpad>Press[9] を選択
2. ＋ボタンをクリック
3. シーン中の「Canvas」を空いている項目にD&D
4. No Function から 「MultiplayMenu→Connect」に変更

<img width="582" alt="Unity_2018_3_0f2_-_MultiplayerMenuVR_unity_-_MultiplayTest_-_Android__Metal_.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/79be4298-e12d-f2f4-2f97-95fbfb92cb3c.png">

これでPadボタンを押すとシーンが遷移するようになりました。

シーンを追加します。

1. メニューから File → Build Settings...
2. Add Open Scenes (現在開いているMultiplayMenuVRシーンを追加します）
3. 追加したMultiplayMenuVRシーンを一番上にD&Dします。

<img width="850" alt="File_と_Item-0_と_Item-0_と_Item-0_と_Item-0_と_Item-0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/0727a156-b7f1-af83-f191-b9cceb6ebad2.png">

# Playerオブジェクトの追従

前回作ったPlayerオブジェクトは動かしていませんでしたね。これを動かしていきましょう。
Gameシーンを再び開いてください。

プレイヤーとして作ったPlayer オブジェクトをカメラの動きに追従するスクリプトを作ります。
FollowCamera.cs というスクリプトを作って、以下のように書きます。

```FollowCamera.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FollowCamera : MonoBehaviour
{
    private Transform _camTrs;
    
    void Start()
    {
        _camTrs = GameObject.FindWithTag("MainCamera").transform;
    }

    // Update is called once per frame
    void Update()
    {
        this.transform.position = _camTrs.position;
        this.transform.rotation = _camTrs.rotation;
    }
}
```

これをPlayerオブジェクトにD&Dします。

# Network Contact Wizardを使ったコード自動生成

次に、Forge Networkingの「Network Contact Wizard」というツールを使ってNetworkで流すデータを使うためのクラス群を作っていきます。こちらのツールを使えばそれらが自動で作ってくれるので大変便利です。

メニューから Window → Forge Networking → Network Contact Wizard を選択してWizardを立ち上げます

<img width="568" alt="Window_と_Item-0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/2ad640fa-0844-1d4b-e8d9-2a314ce27280.png">

下記画面が出るので、Createをクリックしてください。

<img width="524" alt="空白_Skitch_キャンバス.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/0305bd39-2fad-8202-6655-55bb96b0ed27.png">

その画面で次のように設定してください。

1. Name にはネットワークに流すデータ取りまとめの名前を入れます。ここでは "Player" と入れます
2. +ボタンを押して項目を追加します。２つ作ります
3. [position , VECTOR4, Interpolate, 0.15] と、[rotation, QUATERNION, Interpolate, 0.15] を項目に代入してください
4. 「Save & Compile」をクリックして、クラス群を作ります

<img width="524" alt="空白_Skitch_キャンバス.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/2002b07f-d77b-bd8a-168d-c51ea5794c0a.png">

ちょっとすると、

Assets/Bearded Man Studios Inc/Generated/UserGenerated/

以下に

    PlayerBehavior.cs 
    PlayerNetworkObject.cs

というファイルが出来ているはずです。こちらが先程のWizardで自動的に生成されたファイルです。

# Playerクラスの実装

ネットワークで位置と回転を同期するためのスクリプトを書きます。
Player.cs というファイルを作って、PlayerゲームオブジェクトにD&Dしてください。

Playerクラスは以下のようなスクリプトにしてください。

```Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BeardedManStudios.Forge.Networking.Generated;

// PlayerBehaviorは自動生成されて作られたスクリプトです。そこから継承させています。
public class Player : PlayerBehavior
{
    // Update is called once per frame
    void Update()
    {
        if (!networkObject.IsOwner)
        {
            // ホストじゃなければ、NetworkObjectから渡ってきたデータをそのまま渡す
            this.transform.position = networkObject.position;
            this.transform.rotation = networkObject.rotation;
            return;
        }

        // NetworkObjectに位置と回転を教えてあげる
        networkObject.position = this.transform.position;
        networkObject.rotation = this.transform.rotation;
    }
}
```

# Player のPrefab化

Gameシーン中の、今出来上がったばかりのPlayerオブジェクトをPrefab化します。

1. 適当なProjectの場所にD&Dすればよいだけです。
2. Prefab化したシーン中のPlayerオブジェクトは消してしまいます。

<img width="633" alt="Unity_2018_3_0f2_-_Game_unity_-_MultiplayTest_-_Android__Metal_.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/85f85310-e2bf-7407-7754-28d481eb8ef3.png">

# NetworkManager の修正

NetworkManagerというので、ネットワークに繋いだ際にPlayerの生成をしてくれるようです。そこにPlayerオブジェクトを登録しましょう。

Assets/Bearded Man Studios Inc/Prefabs/NetworkManager.prefab

を選択します。(Unity 2018以降の場合は「Open Prefab」クリックして中身を見ます)
このPrefabを直接いじっていきます。

そこの「Player Network Object」のSize を「1」にして、先程作ったPlayer PrefabをD&Dします。

<img width="804" alt="Unity_2018_3_0f2_-_Game_unity_-_MultiplayForge_-_Android__Metal_.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/25bc3b60-4b1d-ac73-d520-58b3f23c6ac3.png">

# GameLogic で Player の生成

いよいよ最後のステップです。
NetworkManagerを介してPlayerオブジェクトを生成します。

「GameLogic」という空のゲームオブジェクトを作り、GameLogic.cs というスクリプトを作ります。

```GameLogic.cs
using UnityEngine;
using BeardedManStudios.Forge.Networking.Unity;

public class GameLogic : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        NetworkManager.Instance.InstantiatePlayer();
    }
}
```

それをGameLogicゲームオブジェクトにD&Dします。
これでこのシーンに遷移したと同時にPlayerオブジェクトが生成されるということです。

# 確認

さあ、これで全部終わりました。
確認していきましょう。

まずはBuild & Run でOculus Go で実行しておきます。

インストールしてUnityロゴが表示されてアプリが立ち上がったら、ちょっと一旦何もせずにUnity Editorに戻りましょう。

![GO02.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/1a6c30e3-cc71-99ab-41f2-3c26f7d469af.gif)

Unity Editor上でMultiplayMenuVRを開いて、Playします。

そして、UIの一番下に配置している「(h)Host(〜」をクリックします。

<img width="486" alt="Unity_2018_3_0f2_-_MultiplayerMenuVR_unity_-_MultiplayTest_-_Android__Metal_.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/46bcd5f7-a366-ac40-b3ed-0f1478a873eb.png">

そして、今度はOculus Goでハンドデバイスのパッドを押して、ホストに繋ぎます。
すると、Oculus Go では普通にテレポート出来ていると思います。

  ![GO04.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/2eaf2cc0-a7d1-88b6-8ecb-99479ad4b2c8.gif)

Unity Editorでは、Oculus Go で動いているプレイヤーの様子が第三者視点で見れるかと思います。

![GO03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72960/28293144-6dca-69db-f565-90771634b03d.gif)

もしOculus Go が２台ある場合は、そちらも同様に参加してみてください。
Unity Editor上に２台のプレイヤーの様子がわかるかと思います。