unity 1week お題「ふえる」に参加しました

#お題「ふえる」に参加しました
unityRoomにて主催の1週間でゲームを作ると言うイベントが定期で開催されております。
500を超える作品の投稿と言うことで凄い盛況ぶりが伺えますね！
作品群はこちらです[unity1Week「ふえる」](https://unityroom.com/unity1weeks/17)

個人の成績はとりあえず棚に上げて置いて、今後も使いそうな話題をつらつらと

##ネットワーク対戦ゲームつくりました。
作ったゲームはこんな感じです。
[G-Online](https://unityroom.com/games/g_online)

##今回書きたい技術
今回はECSとか目新しい物は、使いませんでした。
しいて言えばpun2を初めて使ってみました。

今回の1weekで使った割と技術的なネタを書いて行こうと思います。
長いので第一回目です

・pun2使ってみました
・SRPBatcher使いました
・終わった後でスマホ化でaddressableを使いました
・終わった後でジョブ化Burstもやったので余裕があったら書きます。

##ネットワーク対戦技術考察
###最初に考えた方針
以下の要件で考えました
・とにかく転送速度が速い
・値段が安いもしくはフリー枠がある
・サーバーとか後々の面倒を見なくて良い（なるべく利用サービスが少ない）
・WebGlでも使える
でした。
これを考えると昨今流行りの通信サービスが急激に浮上します。
###何故通信サービスか？
上の考察でも出しましたが、自前でサーバーを用意しないで個人のPC間のやり取りで完結できます。なるべくならサーバー何か持ちたくないですよね。
特にプロトタイピングでは、後々Pjが育つか良く分かりません。

そんな状態で、デカいインフラを前提に置くのは嫌だし、ましてや1Week作品と言うことで、自前でサーバーを建てない！（後々の面倒を見ない！）すぐに辞められるサービスを選定すると言う方針で選定していきます。
##方針を満たしているunityで使えそうなサービス
リンクは貼りませんが、列挙します。
・Pun2
・mun2
・noble connect
選定の基準は方針の通りで、最初はフリーから始める前提で特徴を列挙します。

・pun2 free
　月間60Gまで無料、20人まで同ルーム接続可能、秒間500メッセージ制限あり
・mun2
　月間60G+30Gまでまで無料、20人まで同ルーム接続可能、秒間メッセージ制限なし
・nobleconnect
　なんかようわからん、mirror使って自前のライブラリ作ってとか大変そう

こう見ると、mun2が浮上しますが、世の知見量の差で今回は無難にpun2に決定
　500メッセージの枠を取り払いたい人はmun2と言う選択しが出そうですね。
##pun2って？
ググって見ると色々記事があり、はっきし言って、それらを熟読すればOKです
私が特に今回熟読したのはこちらでした
[PUN2で始めるオンラインゲーム開発入門【その１】](https://connect.unity.com/p/pun2deshi-meruonraingemukai-fa-ru-men-sono1)

##pun2要約
・導入方法等々は上記のリンクのその1に丁寧に書いてあります。
・まず、photonの用意しているマスターサーバーに接続します。
・マスターサーバーに接続すると呼ばれるイベントでロビーに入る。
・スタートボタンを押したら適当にランダム選定して開いているルームに入ってシーン遷移。
・通信と言うか同期の概念で考えたら良い
・同期方式は3種類
　photonviewで指定スパンで同期
　　位置やrigidbodyはやはり定期更新でないと不安が残る

　rpcで任意のタイミングで同期
　　伝家の宝刀、意外とメッセージ数が増えてこれを如何に抑えるかが鍵

　customPropertyで任意のタイミングで同期
　　スコア処理とタイムスケジュール管理とか共有データに利用

・今回は全部使った。正直photonviewの1200byte詰め込みはちゃんとギッチリ詰め込んでるんだろうか…かなり試算と合わなかった

##はじめてで分かり辛かった所
普通のゲームは色々なオブジェクトに処理を書きます。

###【オフライン】
メインはupdateに書きますね

```
private void Update(){
    //色々なAIとか
}
```

###【クラサバ】
古き良きクラサバでも似たような感じ、単純明快ですね
一か所（サーバー）にあるものを信じてupdateを書けば良い

```
private void Update(){
    //よし！他人の位置を取ってくるぞ！
    //よし！位置を考えて
    //よし！自分の位置をDBに反映するぞ！
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/714629/1ebea633-0330-41a1-ae12-f44c0be6ac8c.png)

###【p2p+リレーサーバー方式】
サーバーは介さず（リージョン選定や回線増速等中継的な意味合いのみ持つサーバーが内在。サービスの中）
各PC間でやり取りします。通信はn:nで行われます。

シーンに最初からあるオブジェクトは最初にルームに入った人の持ち物になります。
又、発生させたオブジェクトも発生させた人のオブジェクトになります。		

図を書いてみます。全体は自分のシーンの中に出てくるネットワークオブジェクト達です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/714629/a984b2d7-416c-d757-849d-e455b7e82caa.png)

図で言うところの青〇の部分を主として見てみます。
又このPCは、最初にルームに入った人です。

持ち物が多いですね。
他の部分、例えば他プレイヤーは持ち物は自プレイヤーのみと言う感じです。

シーン中の自分以外のオブジェクトの発生は（ここの図で言うところの他人PCも）自分で発生させなくても
ルームに入った途端photonが自動でinstantiateしてくれます。
自分以外の誰かがログインしたら自分のシーンに他プレイヤーオブジェクトをinsitantiateしてきます。
後から入っても、既に居る他プレイヤーやシーン中のネットワークオブジェクトは入った瞬間にinstantiateしてくれます。
自分以外の人が撃った以下のような命令も自分のクライアントでも処理される感じですね。

```
PhotonNetwork.Instantiate("Player", v, Quaternion.identity);
```

上述の機能で重要なのが、プレイヤーのみならず、ネットワークオブジェクト（photonviewが付いている）は自分で発生させなくても誰かが発生させたらphotonが自分のシーンにinstantiateしてきます。その仕組みはresourcesの仕組みを使っているので、発生の可能性があるプレハブは（プレイヤーも含め）全てresourcesに入れて置く必要があります！

この時に考えるのが、自分の持ち物は何か？と言う考え方です。

自分が動かすべき処理は？と言うのを纏めて行きます。
自分が率先して動かす物は
・自分の持ち物
　・最初にルームに入った人ならシーンに最初からある、通信するオブジェクト
　・自分で発生させたオブジェクト
　・自分
と言うことになります。

他は、他者（からの通信データ）が動かすべきオブジェクトで自分が余り干渉してはイケない物になります。
が！ここが重要ですが、他者が発生させたオブジェクトでもupdateに書かなければ何も動きません。

もう、自分で言っていてもワケワカメなので実際にオブジェクトに張り付けるべきupdateを書いてみます。

```
        private void Update()
        {
            if (photonView.IsMine){
                //自分のオブジェクトの処理
            }else{
                //ここもやはり書かないと動かない！
                transform.position = rx.position;
            }
        }
```
ismineについては他の色々なブログでも書いてある通り、自分の物か判別する物です。else側もOnPhotonSerializeView等からやってきた値で更新してあげる必要がありますね。（ここでの補間は超重要で真面目に書いたら一つの記事に成ってまう‥）
これで１セットで自分側と相手側の処理が揃います。

良く眺めてみると、分かる事があります。
isMineを持ってる量が多いと重くね？って言う気づきがありますね
上の図で青丸の外の人の場合、自分のPC分しか（ismineがPCだけ）処理しなくて良くて後は他からやってくるデータだけ（updateのelse側）だけ処理すれば良い事に気づきます。

うん、分かり辛いですね。
updateのelse側をほぼ無負荷の０としてクラサバに見立てて書くとこうです。
どこかのPC（最初に入った）をサーバーとして間借りして、他のPCがクライアントとして接続しに行ってるのと一緒！となります。
確かに最初に入ったPCのスペックが低いと、NPCやシーンのネットワークオブジェクトの受け持ちが多くカクツキますね。


初めてでよくわかり辛かったですが、他の人が動かすべき事、自分が動かすべき事（ismineの分岐）で切り分けて考えるとスッキリしました。
又、ismineのelse側は感情を込めず、他人から来た物を流すのみととらえるとスッキリします（但し補間はこだわった方がクオリティ上がります）。


私の場合飲み込みが遅いので、ここまで理解するのは最初からだと大変でしたが、分かってしまえば仕組み一通りそろっているので楽でしたphoton偉大

ちょっと長くなってしまったので残りは又後日書こうと思います。


##他
・今どんな状態かはPhotonNetwork.InRoom、InLobbyなどで探れる。
・切断した時の処理は必ず書こう(OnDisconnected)
・ルームから退散するときはPhotonNetwork.LeaveRoom()を使えばルームから出れる。
・異常値検出やクリア時の退出はOnLeftRoomに書いて置けば、leaveroomした瞬間に処理できる。