【PUN2】Unityでオンラインマルチプレイを爆速で実装する

##はじめに
Unityでマルチプレイを実装する際、調べたノウハウをまとめます。

「基本的な処理概要はどうなっているの？」
「ネットワークライブラリーの選択肢は？」
「サーバーは用意が必要？費用は？」

など、基本的な内容についても触れていくので、理解しやすい内容になっていると思います。

##ライブラリーの選択肢
早速、使用するツールについて、
調べた限りだと以下の選択肢があるそうです。

結論から言うと現在は「PUN」一択です。

![Untitled Diagram (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/d681997c-fe4c-15a2-6560-bd5cd98e947e.png)

###UNET
Unityが公式で提供しているマルチプレイ用クラウドサービス
同時接続10人までならフリープランがあるのですが、残念ながら廃止予定です。
[参考リンク](https://blogs.unity3d.com/jp/2018/08/02/evolving-multiplayer-games-beyond-unet/)
※現在は新しいサービスを作成中みたいです。

###PUN
Photon Unity Networking(PUN)はPhoton社が提供するネットワークサービス
無料枠で最大20人までの接続が可能です。

###自分で作る
herokuなどの無料サーバーを駆使し、必要な情報のみをサーバーに連携する方法
全てのIFを自分で作る必要があるので、とても大変(私もやり方はわかりません。。。)

##PUNの概要
>Photon Unity Networking(PUN)は、マルチプレイヤーゲーム対応のUnityパッケージです。 柔軟性の高いマッチメイキングによってプレイヤーはルームに入室し、ルーム内のオブジェクトはネットワーク上で同期されます。RPC、カスタムプロパティ、または「低いレベル」のPhotonイベントなどの機能があります。信頼性が高く、（オプションで）高速な通信が専用Photonサーバーによって実現されます。このため、クライアントは1対1で接続する必要はありません。
[photon公式](https://doc.photonengine.com/ja-jp/pun/v2/getting-started/pun-intro)

PUNはphotonサーバー上でマルチプレイ用のルームを作成し、
クライアントサーバー方式でP2Pよりも高速な通信を実現します。

unityで作成したゲームは各クライアント上で実行され、
ルームの作成支持を出す事で、photon上でマルチプレイ用のルームが作成されます。
※厳密に言うと先にLobyに入室して、ルームの作成・入室処理が入ります。(Lobyはデフォルトで1個です)

簡単にすると以下の図をイメージしてもらえれば大丈夫です。

![Untitled Diagram (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/e88e862e-96eb-e792-19ea-bb1185cb591f.png)

ネットワークの仕組みについては[ここ](https://qiita.com/k0mach1/items/ecfe30165653969a408d)がわかりやすい解説をしているので、興味ある方は読んでください。

##準備
では早速、PUNを使った開発を行います!

と言いたいところですが、登録などの準備が要ります。。。
順々にやっていきましょう。

###登録
以下にアクセスして、photonに登録します。
[公式サイト](https://www.photonengine.com/)

1.左上の「サインイン」からログインページに遷移し、下の方の「アカウントがない場合、こちらから作成してください。」をクリック

2.メールアドレスとパスワードを設定して登録処理を進めます。

3.登録が完了したらコンソール画面に遷移するので、「新しくアプリケーションを作成する」をクリックし、必要な情報を埋めます。
※Photonの種別は「Photon Realtime」にして、あとは必須項目の「アプリケーション名」を埋めればOKです。

![スクリーンショット 2019-09-02 8.59.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/74299089-efb2-0659-81eb-185faf578ffc.png)

4.コンソール画面に戻り、先ほど作成したプロジェクトの「詳細へ」をクリック。
「アプリケーション ID」の内容をメモに控えてください。

###インストール
Unityを開き、AssetStoreからPUNアセットを取得します。
PUNには旧バージョンである「PUN classic」と最新バージョンの「PUN2」があります。
互換性はなく、新しい機能も増えてる訳ではないですが、新規プロジェクトなので、
最新のPUN2の方を取得しましょう。
PUNのバージョンによる違いは[こちら](https://www.slideshare.net/TomohiroOkamura1/pun-2)を参照してください。

1.AssetStoreから「photon」で検索

2.色々ありますが、左下の「PUN2-FREE」を取得します。
※右上の赤枠は旧バージョンです。

![Untitled Diagram (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/2a5dbcb4-1732-49e8-ede8-eaaf4f6ca59f.png)

3.設定用のウィンドウが開くので、登録の際取得した「アプリケーション ID」を入力
※このウィンドウはWindow > Photon Unity Networking > PUN Wizardから、
SetUp Projectをクリックすると開けます。
![スクリーンショット 2019-09-02 9.15.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/822ed2fe-755c-0aa2-75cb-2a86fde590dc.png)

4.3の作業が終わると、PhotonServerSettingsファイルが作成されます。
「Fixed Region」の項のみ"jp"と入力しましょう。
※各設定項目について詳細に知りたい方は[公式の解説](https://doc.photonengine.com/ja-jp/pun/v2/getting-started/initial-setup)がわかりやすいです。

![スクリーンショット 2019-09-04 20.02.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/10398d48-1014-1a62-b09f-b840ec2932fc.png)


##実装
これでやっと実装に入れます！
とりあえず動くキャラクターをオンライン空間上で操作出来るところまでやっちゃいましょう！

1.スクリプトファイルを作成します。
スクリプトの内容は以下をそのまま貼り付けてもらえば大丈夫です。

簡単に解説すると、Start関数でPUNの設定ファイルを読み込み、
後ほど作成する「monster」プレハブをログイン後に生成するというスクリプトになります。
※細かいログイン処理は継承元の「MonoBehaviourPunCallbacks」クラスが行なってくれます。

```c#:SimplePun.cs
using UnityEngine;
using System.Collections;
using Photon.Pun;
using Photon.Realtime;

public class SimplePun : MonoBehaviourPunCallbacks {

	// Use this for initialization
	void Start () {
		//旧バージョンでは引数必須でしたが、PUN2では不要です。
		PhotonNetwork.ConnectUsingSettings();
	}

	void OnGUI()
	{
		//ログインの状態を画面上に出力
		GUILayout.Label(PhotonNetwork.NetworkClientState.ToString());
	}


	//ルームに入室前に呼び出される
	public override void OnConnectedToMaster() {
		// "room"という名前のルームに参加する（ルームが無ければ作成してから参加する）
		PhotonNetwork.JoinOrCreateRoom("room", new RoomOptions(), TypedLobby.Default);
	}

	//ルームに入室後に呼び出される
	public override void OnJoinedRoom(){
		//キャラクターを生成
  	GameObject monster = PhotonNetwork.Instantiate("monster", Vector3.zero, Quaternion.identity, 0);
		//自分だけが操作できるようにスクリプトを有効にする
		MonsterScript monsterScript = monster.GetComponent<MonsterScript>();
		monsterScript.enabled = true;
	}
}

```

2.Hierarchy上で右クリック、Create Emptyをクリックし、先ほど作成したスクリプトをアタッチしましょう

![スクリーンショット 2019-09-05 21.28.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/8581ef7c-8304-e25b-b809-1b0f830f8fa0.png)

3.自分で用意したHumanoroid型のキャラクターをインポートして、Hierarchy上に配置します。
アニメーションも同期させるので、Animatorの設定もしておきましょう。

この設定は長くなるので、超絶わかりやすい[【Unity】自分の思い描いたキャラクターを作成し、最速で動かす]
(https://qiita.com/UpAllNight/items/317708069d1ed3ff0486)を参考に作業してみてください。

※移動用のスクリプトは以下を使用してください。

```c#:MonsterScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MonsterScript : MonoBehaviour
{
  private Animator animator;
  // Use this for initialization
  void Start () {
      animator = GetComponent<Animator>();
  }

  // Update is called once per frame
  void Update () {
    if (Input.GetKey("up")) {
        transform.position += transform.forward * 0.05f;
        animator.SetBool("walk", true);
    } else {
        animator.SetBool("walk", false);
    }
    if (Input.GetKey("right")) {
        transform.Rotate(0, 10, 0);
    }
    if (Input.GetKey ("left")) {
        transform.Rotate(0, -10, 0);
    }
  }
}

```

4.以下の画像に従って、Componentを追加していきましょう。
追加対象は以下になります。

・Photon View(追加後、「Observed Component」にその他のコンポーネントを追加してください。)
・Photon Transform View
・Photon Animator View
※Monster Scriptはチェックボックスを外してください。

![スクリーンショット 2019-09-05 21.36.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/80e7af59-6a1d-55f5-e0ae-9859f777cf73.png)

ついでに、Photon Animator Viewの「Synchronize Parameters」の各値を「Discreate」に変更します。
![スクリーンショット 2019-09-05 21.55.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/07b615ab-bc02-36ed-b7ff-316fa0a88a25.png)


5.4で作成した「monster」のプレハブを/Photon/Resources配下に配置してください。
※配置後はHierarchy上の「monster」は不要ですので削除しましょう。

![スクリーンショット 2019-09-05 21.52.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/dfd3abb3-47fd-60d4-bc8f-0b1209a92ec6.png)

###実装補足
実装編で駆け足になってしまったところを補足すると、

PUNの仕組みとしては、Unityの実行自体は各端末で行い、必要なパラメータ(相手の位置情報など)のみをPUNクラウド上で管理・同期送信を行う流れになっています。

4で追加したPhoton Viewコンポーネントは、その同期を行うパラメータを管理する重要な役割を持っているのです。

図解すると以下のようなイメージです。

![Untitled Diagram (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/90c15cd7-fb08-eedd-2bf9-1085c8cc5b8f.png)
##実行
実装が終わったら、作成したSceneをビルドしましょう。
2画面で確認するため、ターミナルで以下のコマンドを入力しましょう。

`open -n ./ビルドしたファイル名.app`

また、画面が最大サイズで開かないように「Screen resolution」を最小の「640 * 480」に選択し、Windowedチェックボックスにチェックを入れて実行してみましょう！

![スクリーンショット 2019-09-05 22.00.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/189a25a7-630e-6c76-c338-7987de87051c.png)

キャラクターを操作・キャラクターの位置と向きの同期・アニメーションの同期ができていれば完成です！
![スクリーンショット 2019-09-05 21.09.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378268/1e65c568-cf3f-5c2f-0280-553676c5cb0a.png)

##まとめ
PUN2は、インターネット上の情報が旧バージョン情報ばかりなので、
「基本動かない・・・」という事象が発生し、

「公式のドキュメント読みづらい」
「そもそもの仕組みさっぱりわからないーZ」

のダブルパンチをくらい、ふてくされそうになりました。
この記事が同じ思いをしてふてくされている人の役に立ってくれればと願うばかりです。

また、マルチプレイ機能は既存のアプリに加えるだけでも、全く違うゲームに生まれ変わると思います。
案外知ってしまえばPUN2はとても便利で簡単なものなのでぜひ使ってみてください！