MLAPIを使ったマルチプレイを作ってみた


## はじめに
* 初めて書く記事ですので、色々不備があるかもしれません。そこはご了承ください。
* 後にPUN2等の通信技術についても触れていく予定です。この記事では比較等はありません。
* あくまで実装するだけなので詳しい解説は特にないです
## 参考記事
https://qiita.com/mayochan32/items/c999764729fab2645519
## 導入方法
### プロジェクトの作成
ここは普通にプロジェクトを作成していきます。
僕はわかりやすいように2Dで作成しました。
### MLAPIのパッケージを出力する
[GitHubのリンク](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/releases?page=2)からパッケージをダウンロードしていきます。
今回は後ろの方にあるv12.1.7を使用します。
* MLAPI.unitypackageのみダウンロードする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/af9177b8-52f5-48e7-dc35-7cde2f910c04.png)
* Assets > Import Package > Custom Package を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/c42c3a36-c93e-d746-4975-2b60c7df73cd.png)
* ファイル選択にて、先程ダウンロード下MLAPIのunitypackageを開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/e8f13b2a-05f2-38a0-a145-8031ece06b6f.png)
* パッケージのアセットをすべてImportする(すべてにチェックマークがついていたらOK)

これでプロジェクトにMLAPIがインポートされました。
### テストで動くものを作る
動きがわかれば特になんでもいいです。
僕は左右にしか動けないプレイヤーのプレハブを作りました。
### NetworkManagerの作成
* ヒエラルキーに空のオブジェクトを作り、名前をNewworkManagerにする
* Hierarchy > NetworkManager > Add Component からNetworkManagerコンポーネントとUnet Transportを追加する
    NetworkManagerはnetworkで、
    Unet Transportはunetで検索すればすぐ見つかる
* Hierarchy > NetworkManager > NetworkingManagerのNetwork TransportにNetworkManager自身を設定
画像のようになればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/f6e438fe-e258-4df0-7004-46b272eba5b2.png)
### プレイヤーをネットワークオブジェクトとして設定
* Hierarchy > Player(prefab)にNewworkObjectコンポーネントを追加する
    (networkで検索すればすぐ見つかる)
* NewworkManagerのNetworkedPrefabにプレイヤーを設定する。その差異Default Player Prefabをチェックする。
画像のようになればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/d0a15d2a-1daf-e5b1-9210-dbb4299dea1f.png)
* Hierarchy > Player(prefab)に
    * NetworkedTransform
    を追加する
    (networkで検索すればすぐ見つかる)
* コンポーネントを追加したプレイヤーのPrefabをApplyする
    ApplyAllを押せばOK
### ホスト、クライアントが参加できるようにボタンを作成
* ヒエラルキーにUI用キャンバスを作成
* 作成したキャンバスにボタンを追加する
* わかりやすいようにオブジェクト名と表示テキストをStartHostに変更
* 同じようにStartClientボタンを作成する。
    位置はどこでもいいが今回は左上にした
### スクリプトの作成
* 「プレイヤー操作スクリプト」「ホスト、クライアント参加用スクリプト」を作成する。名前はわかりやすいようにする。
    僕はPlayerControllerとStartManagerにしたが、もっといい名前はあると思う
* StartManagerに次のコードを追加する
```C#:StartManager.cs
    public void OnClickStartHost()
    {
        Debug.Log("Start Host");
        MLAPI.NetworkingManager.Singleton.StartHost();
    }

    public void OnClickStartClient()
    {
        Debug.Log("Start Client");
        MLAPI.NetworkingManager.Singleton.StartClient();
    }
```
* 先程作ったスクリプトをStartHostボタンにドラッグ&ドロップする
    StartHostボタンのコンポーネント内にスクリプトがあればOK
* OnClick()のオブジェクトにStartHostボタンを設定し、Functionに StartManager > OnClickStartHost() を設定
    画像のようになればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/3e1b4287-e414-cf95-bba3-f0359b1c4936.png)
* 同様にStartClientも設定する
* PlayerControllerの基底クラスをMLAPI.NetworkedBehaviourに変更する。
* 自分自身だけ更新処理を行わせるように、FixedUpdateの処理をIsOwnerのときだけ処理するようにIf文で条件を作る
```c#:PlayerController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MLAPI.NetworkedBehaviour
{
    // Start is called before the first frame update
    public float force = 0.2f;
    private new Rigidbody2D rigidbody2D;
    void Start()
    {
        rigidbody2D = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        if(!(this.IsOwner))
        {
            return;
        }
        Vector2 vector2 = new Vector2();
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            vector2.x = -force;
        }
        else if(Input.GetKey(KeyCode.RightArrow))
        {
            vector2.x = force;
        }

        rigidbody2D.AddForce(vector2);
    }
}
```
Playerに設定するのも忘れずに
### ビルド
* ヒエラルキーにいるPlayerは起動時に複製されるため、削除する
* 検証しやすくするため、Project Settings > Player > Resolution and Presentation > Fullscreen Mode をWindowedに変更
    画像の様になればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/a1214616-7aa3-067b-db08-5bb0649caf3a.png)
* Project Settings > Player > Other Settings > Api Compatibility Level を.Net 4.xに変更
    画像の様になればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/78a73f22-51fd-ad41-825b-28f1edf74350.png)
これが終わったらビルド先のフォルダを作って選択肢、ビルドを開始する
### 動作確認
* ビルドして出てきたexeファイルを２つ実行する。
    * 1つ目のウィンドウはホストで実行させる
    * 2つ目のウィンドウはクライアントで実行させる
2つプレイヤーが出てきて、動きが同期されていたら成功
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952304/f4873a7e-f1a8-7e1b-31a9-8179b85a8a56.png)