【WIP】これだけでPUNにおける通信は9割わかる

# __0. はじめに__

## ◆ 必要なAssetのインストール
基本的な通信であれば、必要なのはPhoton Unity Networkingだけ。Asset Storeでインストールしましょう。
インストール諸々は記事いっぱいあるし簡単なんで自分でやリましょう。

## ◆ そもそもファイルがごちゃごちゃインストールされて何が何だか、、、
公式リファランスを見るとこうあります。
> PUN（Photon Unity Networkingの略）は多数のファイルで構成されます。しかし重要なのはたった1つ: `PhotonNetwork` です。このクラスは必要とされるすべての関数と変数を含んでいます。さらに独自の要件がある場合、いつでもソースファイルを編集可能です。なにしろこのプラグインはPhotonの実装そのものなのですから。

インポートの際に多くのファイルがインストールされますがおどおどする必要はありません。つまるところPhotonに関して定義されたメソッドや変数に関する定義をいじりたい場合には `PhotonNetwork` という名のファイルだけ見ればいいんです。かと言って __基本的にはこれらのファイルは全くいじらなくても実装できます__ ので最初のうちは無視で行きましょう。

## ◆ どこにスクリプトを書くか
実際のところ、Photonに関するメソッドや変数はどのファイルでも同じように昨日するものがほとんどです。ですが僕のオススメはPhotonControllerや、NetworkManagerの様な名前の空のGameObjectを作成して、同名のファイルをアタッチしてあげる形です。接続に関するメソッドは全部そいつに任せてしまい、必要があれば他のクラスからそのメソッドを叩く様にすれば。

# __1. サーバー接続について__
## ◆ PUNのサーバー接続の仕組み
PUNのサーバー接続は以下の図のように通信全体を統括する一つのマスターサーバーと複数のユーザーの個々のゲームサーバーの接続で成り立っています。
![photonserver.png](https://qiita-image-store.s3.amazonaws.com/0/118610/ce582ad2-2be8-2e30-2aed-57b42066ec23.png)

### ☆ マスターサーバーとは
１つのサーバーでゲームを回してそれを傍受している様に思えますが実はそうではありません。Photonでの通信では、userそれぞれのサーバーでそれぞれゲームが回っていて、それをマスターサーバーが統括するという形になっています。

## ◆ 具体的なサーバー接続方法
### 1. ``ConnectUsingSettings``を使う場合

```C#
PhotonNetwork.ConnectUsingSettings(string gameVersion);
```

#### ・サーバー接続の基本的なメソッド
これまでにWizard形式で設定していった ``PhotonServerSettings`` ファイルにしたがって Photon 及び Photon Cloud に接続するメソッド。普通に開発してたら使うのはまあこっちです。

#### ・引数にはstringでGameVersionを取る
GameVersion ってなによと思うかもしれませんが、実はこれ文字列ならなんでもよくて、自分で勝手に決めてしまって大丈夫です。この GameVersion は __同一アプリでマッチングを分けたい場合(アプリのバージョンが違う場合等)にゲームバージョンをそれぞれ指定し、マッチングを回避する__ 目的で使用されます。ややこしく聞こえますが、平たく言えば

> 『モンスターハンターの 2nd と 3rd は同じゲーム名だけど色々違うとこあるから通信できちゃったら困るよね。だからVersionが違うもの同士はマッチングしないようにしよう』

ということです。使わないのであれば null を渡してしまってください。

### 2. ``ConnectToMaster`` を使う場合

```C#
static bool PhotonNetwork.ConnectToMaster
(
  string masterServerAddress,
  int 	 port,
  string appID,
  string gameVersion 
)		
```
設定ファイルを無視して全パラメータを自分で指定するやり方です。ややこしいし積極的に選択する理由を見つけられていないので使っていません。

## ◆ 接続に失敗した時
原因としては主に以下の3つが考えられます。呼び出される関数の違いにも注意しましょう。

### 1. ``ConnectUsingSettings``に登録された ``AppId`` が正しくない
Photon のホームページで取得した AppId と ConnectUsingSettings に登録されたものが対応してません、凡ミスですね。

```C#
OnFailedToConnectToPhoton()
```
が呼ばれます。``ConnectUsingSettings``のInspector Viewで確認可能です。

<img width="457" alt="Unity_5_5_2f1_Personal__64bit__-_Start_unity_-_FPS_Space_-_PC__Mac___Linux_Standalone__Personal___OpenGL_4_1_.png" src="https://qiita-image-store.s3.amazonaws.com/0/118610/81c9292d-ab19-9d8f-4b89-94ff254cb873.png">


### 2. ネットワークの問題
単純に接続に問題がある場合です。ネットワーク接続（WiFiなど）を見直しましょう。同様に

```C#
OnFailedToConnectToPhoton()
```
が呼ばれます。

### 3. ``PhotonServerSettings``に登録された ``Region`` が正しくない
PUNのマスターサーバーは世界各地に点在しています。きちんとJpのものを利用しましょう。``ConnectUsingSettings``のInspector Viewで設定可能です。
<img width="456" alt="Unity_5_5_2f1_Personal__64bit__-_Start_unity_-_FPS_Space_-_PC__Mac___Linux_Standalone__Personal___OpenGL_4_1_.png" src="https://qiita-image-store.s3.amazonaws.com/0/118610/28f436d8-d95b-89e0-3f18-73f89f72c165.png">



# __2. ロビーとルームについて__

## ◆ ロビーとルームの関係
通信をするにあたってuserはロビー及びルームに入るのですが、この微妙な命名のせいで直感的にわかりづらいですね。
ロビーはめっちゃ大きい家、ルームはその中にある部屋だと思ってください。リレーションっぽく書くと ``Lobby has many Rooms`` であり ``Rooms belongs to Lobby``です。

## ◆ ロビーについて

### ・ロビーのクラスについて
ロビーは __TypedLobby__ クラスという名前のサーバー上の特定のロビー(と種別)を参照するクラスに司られています。__Name__ と __LobbyType__ の属性を持ち、これらの値の組み合わせでロビーを判別しています

```C#
TypedLobby.TypedLobby (	
  string    name,
  LobbyType type 
)	
```

__LobbyType__ とはロビー種別のオプションで、enumで定義されています。なお詳しいVersionは不明ですが、古いサーバーでは使用できないらしいので注意してください。

|Enumerator|About|
|:-:|:-------|
|Default|いずれかのサーバーで指定されない限りデフォルトでこの値になります。JoinRandomRoomにてプロパティマッチングでフィルタリングすることが可能です|
|SqlLobby||
|AsyncRandomLobby||


### ・デフォルトの設定ではロビーは一つ 
特にロビーに関する設定をいじらなかった場合、ロビーは１つだけ自動で作成され、これを使用することになります。このロビーを __デフォルトロビー__ と呼びます。デフォルトロビーに入るには

```C#
#引数なしで
bool PhotonNetwork.JoinLobby()
```
というメソッドを使用します（引数なし）。

しかし、デフォルトロビー1つのみを使用する場合はこの記述を省略できる設定があります。 ``PhotonServerSettings`` ファイルを見つけてきて Inspector View で Auto-Join Lobby にチェックを入れましょう。

<img width="458" alt="Unity_5_5_2f1_Personal__64bit__-_Start_unity_-_FPS_Space_-_PC__Mac___Linux_Standalone__Personal___OpenGL_4_1_.png" src="https://qiita-image-store.s3.amazonaws.com/0/118610/3f33435e-1493-8122-a4a6-bd013b9f5b37.png">

Photonサーバーへの接続と同時にデフォルトロビーにJoinする様になります。

### ・ロビーを複数用意することも可能
１つのロビーが持つルームの数に上限はないので、サービスが拡大するとルームの数が尋常ではないことになり管理しにくくなります。そこでロビーを複数用意することが可能になっています（それ以外にも理由は色々と考えられますが）。指定のロビーに参加するためには以下のメソッドを用います。


```C#
bool PhotonNetwork.JoinLobby( TypedLobby typedLobby ) 
```

引数に ``TypedLobby`` クラスのインスタンスを渡すことで、そのロビーに入ることができます。

### ・ロビーに入った時点で ``OnJoinedLobby()`` メソッドが呼ばれる
ロビーに入った瞬間実行したい処理はこのメソッド内に記載します。例えばロビーに入った瞬間に、そのロビーに存在するルームの中からランダムに一つ選んで入室したい場合は、

```C#
void OnJoinedLobby(){
  #このメソッドについては後述します
  PhotonNetwork.JoinRandomRoom();
}
```
の様に記述します。
しかし、詳しくは後述しますが __Room一覧の取得はこの中でしてはいけない__ ので注意が必要です。Room一覧を取得する際には必ず ___void OnReceivedRoomListUpdate()___ というメソッド内で行うので注意しましょう。

## ◆ ルームについて

### ・ルームのクラスについて

#### ★ __RoomOptions__クラスについて
名前の通り、ルームに関する設定を司どるクラスです。たくさんの属性やプロパティを持っていますが、ここではよく使用するものに限定して紹介したいと思います。もし深く知りたければ以下の二つの記事をご参照ください。
> - [Photon Unity Networking: PhotonNetwork Class Reference](https://photonengine.jp/pun-api/class_photon_network.html#a08435c2d064fd6a85e51e1520e5a63d8)
> - [UnityでPUNを使ったオンラインマルチプレイの実装 -カスタムプロパティ編- | TECH Projin](http://tech.pjin.jp/blog/2016/09/04/unity_pun_4/)

- < 属性 > maxPlayers (byte型)

- < 属性 > __customRoomProperties (Hashtable型)__  
これこそがRoomOptionsの真骨頂です。Room全体で共有しておきたい事項をHashの形で

### ・ルームの作成
ロビーとは違いルームは実際にプレイヤー同士が通信を行う場所なのでユーザーが自由に作成し、参加できるべきです。そのためルームはデフォルトで様々な属性やメソッドを持っています。PhotonNetworkクラスには簡単なものから自分で設定を書き加えられるものまで、クラス作成のためのメソッドが複数用意されています。


__1. CreateRoom（引数１つの場合）__

最も簡単なルームの作成方法で

```C#
PhotonNetwork.CreateRoom (string roomName)	
```
の様に名前（ __Name__ ）のみを与えてあげてルームを作成します。ロビー内において、ルームはこの Name で判別されるので、 __１つのロビー内に同名のグループを複数作成することはできません__ 。別のロビーであればもちろん同じ名前のルームが存在していても全く問題ありません。
もしめんどくさかったり、一意的な（つまりダブりがない）Nameを与え続ける自信がなく、かつルームの名前にこだわりがない場合は __null__ もしくは __""__ を引数として渡すと、サーバーが勝手にNameに割り当ててくれるので便利です。

__2. CreateRoom（引数３つの場合）__
以下のようにルームの名前( __Name__ )、ルームのオプション( __RoomOptions__ )、ロビーの情報( __TypedLobby__ )を与えてルームを作成します。

```C#
PhotonNetwork.CreateRoom (
  string      roomName,
  RoomOptions roomOptions,
  TypedLobby  typedLobby 
)	
```
めんどくさい様に見えてかなり役に立ちます。

TypedLobbyにロビーを設定すると、 __どのロビーがアクティブかに関わらずそのロビーの下にルームが作成__されます。それだけではなく、__仮にその条件のロビーが存在しない場合、勝手に作成してその下にルームを作成__してくれます。破壊力がすごい、、、。ちなみに null を渡すと、自動的に現在アクティブなロビーの下に作成されるという優しい設計です。

### ・ルームへのJoin
Joinの仕方も様々です。

# Special Thanks
[うら干物書き](http://www.urablog.xyz/) … 実装に当たって、最初から最後までびっくりするくらいお世話になりました。わかりやすさ、及び情報量、どちらをとっても最高です。