【Unity】【Netcode for GameObjects】Netcode for GameObjectsでマルチプレイを作ってみた

以前、MLAPIを使ってマルチプレイを作りまして、Netcode for GameObjects(以下、NGOと呼ぶ)を実装してみようと思い、色々調べたら、まさかNGOがMLAPIの次のバージョンみたいなものだと知って驚きました。
今回はそのNGOを使ったマルチプレイの実装方法を紹介していきます。

# 事前準備
* UnityHub
* UnityEditor(<font color = "Red">**2020.3(LTS), 2021.3(LTS), 2022.2のものしか動きません**</font>)
UnityEditorのバージョンだけは特に気にしてください。上記のもの以外で実装することはできません。今回は2022.3.1f1を使います。
私はこれに気づかずに丸一日苦戦してました
# 実装方法
## プロジェクトの作成
上記のバージョンに合うバージョンで、プロジェクトを作ってください。2Dでも3Dでも構いませんが、私は2Dで作りました。
## パッケージのインストール
Windows > Package Managerを開き、「Packages」の項目を**Unity Registry**にします。
そしたらその左隣にある「+」を押し、**Add package by name**を押してから、以下の名前を記入(またはコピー&ペースト)してください。
```書き方
com.unity.netcode.gameobjects
```
## 参加用のボタンを作成する
ホストとクライアントで参加できるようにしましょう。
### 参加できるようにするスクリプトを作成する
アセット内に**NetworkUI**を作り、そのスクリプト内で、下記のコードを入力してください。
```NetworkUI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NetworkUI : MonoBehaviour
{
    public void StartHost()
    {
        Unity.Netcode.NetworkManager.Singleton.StartHost();
    }
    public void StartClient()
    {
        Unity.Netcode.NetworkManager.Singleton.StartClient();
    }
}
```
ホストの参加とクライアントの参加の処理をしています。
それぞれボタンを押したときに処理するようにします。
これを書きましたら、ヒエラルキーの中にある**EventSystem**に、先程作ったスクリプトを入れます。
### ホストとクライアントのボタンを作成する
ヒエラルキーに、**Button - TextMeshPro**を2つ追加します。
ボタンの中にある**On Click()**の項目に、先程入れたNetworkUI.csの中の処理を入れます。
こうなっていればOKです。
* ホスト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/d7f7451e-836c-bed3-867a-6b8ab82a59fb.png)
* クライアント
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/ab380ca5-495f-ce9e-ad2b-bd3278cbaf63.png)
## プレイヤーのプレハブを作成する
プレイヤーのモデルとなるものをヒエラルキー内に作ります。今回私は2Dで制作をしているので、2Dの円スプライトを使います。名前もちゃんとわかりやすいように**Player**にしておきましょう。
生成できたら、その中に以下の項目を追加します。
* Network Object
* Network Transform
* Network Rigidbody 2D(Rigidbody 2Dもここで生成される)
* Circle Collider(四角でもカプセルでもなんでもいい)
* [Player Controller](#プレイヤーの移動部分のスクリプトを作成する)(リンク先で説明します)
中身の設定につきましては、特に問題がなければそのままで大丈夫です。
物理挙動について色々設定したい場合は設定しましょう。
### プレイヤーの移動部分のスクリプトを作成する
アセットの中に**Player Controller**を作成します。
スクリプトの中身を開いたあと、必ず継承先を下記のものに変更してください。
```継承先
Unity.Netcode.NetworkBehaviour
```
そしたらあとはプレイヤーの移動を処理するプログラムを書くだけです。
例として上げておきます。
```csharp
if (this.IsOwner)
{
    Vector2 velocity = new Vector2();
    if (Input.GetKey(KeyCode.W))
    {
        velocity.y = 1.0f;
    }
    else if (Input.GetKey(KeyCode.S))
    {
        velocity.y = -1.0f;
    }
    if (Input.GetKey(KeyCode.A))
    {
        velocity.x = -1.0f;
    }
    else if (Input.GetKey(KeyCode.D))
    {
        velocity.x = 1.0f;
    }
    velocity.Normalize();
    SetMoveInputServerRpc(velocity);
}
if(this.IsServer)
{
    _rigidbody2D.velocity = new Vector2(_velocity.x * _moveForce, _velocity.y * _moveForce);
}

[Unity.Netcode.ServerRpc]
private void SetMoveInputServerRpc(Vector2 velocity)
{
    _velocity = velocity;
}
```
ここで必ずやってほしい処理を上げていきます。
* 自分の持ちキャラの場合、入力処理を行う。
* 動いた分をサーバへ渡す。
* サーバ側で移動処理を行う。
それぞれ説明します。
#### 自分の持ちキャラの場合、入力処理を行う。
```cs
if (this.IsOwner)
```
これは、**自分の持ちキャラの場合**という条件文になっています。
これがないと全キャラ操作できるようになってしまいます。必ずこの条件分を使いましょう。
注意してほしいのが、後に説明する**動いた分をサーバーへ渡す**処理でも条件文を使うので、
```cs
if (!(this.IsOwner))
{
    return;
}
```
これは控えたほうがいいかもしれません。
#### 動いた分をサーバへ渡す。
```cs
[Unity.Netcode.ServerRpc]
private void SetMoveInputServerRpc(Vector2 velocity)
{
    _velocity = velocity;
}
```
これは、**動いたものををサーバーへ渡す**処理をしています。
関数の上に、
```
[Unity.Netcode.ServerRpc]
```
これをつけることで、サーバへ渡す処理ができるようになるそうです。すごいですね。
#### サーバ側で移動処理を行う。
```cs
if(this.IsServer)
```
これは、**自分がサーバの場合**という条件文になっています。
主に物理挙動の処理を行うのに使います。
### プレハブ化させる
ここまで作ったプレイヤーのオブジェクトをアセットの中へ移動させると、プレハブ化することができます。
プレハブ化させたら、ヒエラルキーに残っているプレイヤーオブジェクトは必要ないので削除します。
## シーン内にNetworkManagerを作成する
ヒエラルキーに空のオブジェクトを作成し、そのオブジェクトの名前を**NetworkManager**にしてください。
そのオブジェクトの中に、**Network Manager**という名前のコンポーネントを追加してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/f7f3a8db-55bf-8559-c2c5-068a76debf0b.png)
そしたらこのような画面になるので、**Select transport...** のところを選択し、その中にある**Unity Transport**を選択してください。
**Player Prefab**に、アセットの中にあるプレイヤーのプレハブを、
**Network Transport**に、ヒエラルキー内にある**NetworkManager**を入れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/c2d1aad6-fe4c-14c0-31c6-3ea53478bd26.png)
こうなってたらOKです。
## ビルドの準備を行い、実際にビルドをする
**File > Build Settings... > Player Settings**この順で開いていき、**Resolution and Presentation**の中にある、**Default Screen**を**1280 * 720**にします。もっと小さくでも良いです。
そしたらビルドを行いましょう。
# 実際に動かしてみる
ビルドしたフォルダの中にある実行ファイルを2つ開きます。
一つのウィンドウではホストで参加し、もう一つのウィンドウではクライアントで参加してください。
クライアント側で動かしているものが、実際にサーバ側で動いていたら成功です。お疲れ様でした。
なにか不具合等あれば連絡してください。
# 参考記事
* [Netcode for GameObjectsを使ってみよう - Unityステーション](https://www.youtube.com/watch?v=GRUtGLL8iMQ)