UNET Unofficial Documentation (Errors, Workarounds, Best Practices)

以下の文書はUnity forumに上がっているスレッドの翻訳です。読んでて胃がｷｭｰってなるのでシェアします。
https://forum.unity3d.com/threads/unet-unofficial-documentation-errors-workarounds-best-practices.395952/



私がドハマりして死にかけたものをシェアします。

## エラーと回避策
### Did not find target for sync message:
- 説明：UnityがSyncvarをUpdateで使って、クライアントの対象GameObjectが存在していない時に起きます。
銃弾や矢や、時間が経つと消えるものに使っているとこのメッセージがバンバン飛んできます。
- 対応策： QoS Channelを Relicable Sequencedにすると上手くいく事があります。上手くいかなかったら、もう**単純に無視しましょう**
http://hiyotama.hatenablog.com/entry/2015/07/09/090000

### Failed to spawn server object: ...
- 説明：NetworkServer.Spawnをクライアント側でspawnable指定していないオブジェクトに対して行うと起きます。
- 対応策：クライアント側のエラーが出たオブジェクトをNetworkManagerの spawnable object listに加えます。

### SyncLists aren't properly synced to the Client (the list is empty without any errors being shown)
- 説明： http://forum.unity3d.com/threads/bug-syncliststruct-only-works-with-some-file-names.384582/
- 対応策：スクリプトの名前を**動くようになるまで適当に変えます。**（つらい）

### ReadString out of Range / ReadBytes out of Range
- 説明： OnSerializeやOnDeserializeの後に起きます。これらのエラーは直接表示されず、UNETだけがエラーを表示します（つらい）パケットがヘニャヘニャになります…　バグレポとして挙がっています。

## Best Practices & ELI5(ベストプラクティスと、5歳でもわかるような説明)
### Attributes:
- [Server] クライアントが呼べないようにします。（クライアントが呼んだらエラーになります）
- [ServerCallback] クライアントから呼ばない時に付けますが**エラーや警告が出ません**。Start()やUpdtae()のようにサーバーからだけ呼ぶ場合に付けます。
-  [Client] サーバーが呼べないようにします。（サーバーが呼んだらエラーになります）
- [ClientCallback] サーバーから呼ばない時に付けますが**エラーや警告が出ません**。Start()やUpdtae()のようにクライアントからだけ呼ぶ場合に付けます。

### Commands:
- [Command] タグを使い、'Cmd' を関数名の頭に付けます。 'void CmdTest()'とか。
- Commandはクライアントからサーバに送りたいものがある時に使います。例えばクライアントの入力値をサーバに送るとか。
- Commandはサーバから呼ぶことは出来ません。もしサーバ側から呼びたくなったらラップした他の関数を用意して、そこから呼ぶことが出来ます。

### Creating and Destroying GameObjects:
- Createは通常通りのInstantiate()で呼んだ後に NetworkServer.Spawn(go).に渡します。
- Destroy は通常通りの**Destroy()は使わずに** NetworkServer.Destroyを呼びます。

### Headless Mode:
興味が無いので割愛

### NetworkProximityChecker:
- これを使うGameObject上に**Colliderがアタッチされていることを確認** してください。さもなければ、Physicsはそれを検出しないので、このチェッカーが勝手にUNETの監視対象から外します。
http://am1tanaka.hatenablog.com/entry/2015/08/15/224655

### Network Send Interval:
- スクリプト内で書き換えても、**ゲーム開始時の値が保持されます**。

### NetworkAnimator:
- NetworkAnimatorのTriggerは分離して呼ばれて、同期してくれません。
私はhealthやspeedなど、Animatorのパラメータに入っている重要そうな値は**NetworkAnimatorを使わず手動で同期させています**(つらい)

### NetworkBehaviour:
- MonoBehaviourの代わりにNetworkBehaviorを継承することで、そのオブジェクトをネットワーク経由で同期できます。
 - isServer, isClient, isLocalPlayer を使って、処理を分けることが出来ます。ただし、先の条件は排他ではなく、同時に2個以上がtrueになり得ることに注意してください(hostとして起動したクライアント等)

### NetworkIdentity:
- Server Onlyは、クライアントには表示されず、サーバー上にのみ存在することを意味します。
- ローカルプレーヤー権限は、**クライアントが不正行為を行えること**を意味します。

### NetworkManager:
- Channels: Reliable Fragmentedしか使えません。他のチャンネルを使う場合。SyncVarやSyncListが大きくなっていくにつれて**ランダムに失敗します。**
また、チャンネルは2個使い、2個ともReliable Fragmentedにしています。どこかで読んだのですが、UNETは1個目のチャンネルがいっぱいになった時に2個目のチャンネルを使うようになっている、と書いてあったためです。

- NetworkSimulationは**期待した通りに動きません** 
-  'Round Robin'方式の spawn method は、ネットワーク負荷を減らすために(例えば)プレーヤーAの生成が終わるのを待ってから別のプレーヤーBを生成します。
この方式を使うのであれば、**Timeoutsの値を凄く大きくしておきましょう。**
さもないと誰かひとりに起きた1-2秒くらいのラグで、みんなが切断されてしまいます。

### NetworkLobbyManager:
- https://www.assetstore.unity3d.com/en/#!/content/41836
↑このアセットをアセットストアからインポートして、シーンに合わせてセットアップしましょう。
PlayerPrefabとgamePlayerPrefab プロパティがありますが、**後者を使いましょう**


### NetworkTransform:
- NetworkTransformはフレーム間の補完がちゃんと効いていないのでガクガクと動きます。このフォーラムを探してマシな実装を探してください。

### RPCs:
-  [ClientRpc] タグをつけて 'Rpc' を関数の接頭語に付けます(例： 'void RpcTest()') サーバーはクライアント上のRpcメソッドを呼び出すことが出来ます。
- この呼び出しは1個のクライアントに対してではなく、全てのオブザーバー(クライアント)に対して発行されます( https://forum.unity3d.com/threads/suggestion-globalrpc-observerrpc-targetrpc.382486/ )
もし、対象を区切ったり、プレーヤー限定にしたり、特定のクライアントのRpcだけを呼び出したいならカスタムメッセージを作ってください（特定クライアントだけ呼ぶ 'TargetRpc' 　がUnity5.4に実装されました https://docs.unity3d.com/ScriptReference/Networking.TargetRpcAttribute.html）

### SyncVars
- 変数をサーバーからすべてのクライアントに自動的に同期するために使用されます。クライアントからそれらに割り当てないでください。**クライアント側での割り当ては無意味です**
- nullにならないようにしてください。エラーを吐きます。
- GameObjectに対してNetworkIdentityがアタッチされている時、int、long、float、string、Vector3などの単純な型と、NetworkIdentityとGameObject自身を利用できます。
- 値の変更をフックすることもできます。([SyncVar (hook = "OnHealthChanged")] とか) (参考URL:  http://hiyotama.hatenablog.com/entry/2015/07/15/090000 )


### SyncLists:
- 初期化タイミングが早すぎる為、Awakeで操作しないでください。OnStartServer か Startで操作してください。
- SyncListStructs はC# 構造体を使います。構造体は値渡し( http://ufcpp.net/study/csharp/resource/rm_struct/ )
なので、classと同じように
```
synclist.value = newvalue; 
```
することは出来ません。
また、こちらもSyncVarsと同様にフックすることが出来ます。

```cs
// for the official things
[SyncListString(hook="MyHook")] SyncListString mylist;
void MyHook(SyncListString.Operation op, int index) {
    // do things
}
 
// for custom structs
[SyncListString(hook="MyHook")] SyncListStructCustom mylist;
void MyHook(SyncListStructCustom.Operation op, int index) {
    // do things
}
```

### Time Synchronization:
- Network.TimeはUNET前の古いネットワークシステムから引き継がれています。なので私は自前で時間同期を実装しました。なぜならサーバ上の時間を知りたい場面があった為です。NetworkTimeと名付けたGameObjectをシーンに置いて、NetworkTimeと名付けたスクリプトを配置しました。NetworkTimeスクリプトはただ単純にサーバ上の時間をクライアントに通知する為です。
このスクリプトを通して、クライアント側でクライアントとサーバの間の時間差（オフセット）を求めておくと、サーバの時間をTime.time+offsetで求めることが出来ます。

### WebGL:
- WebGL クライアントがサーバに接続する必要がある場合、サーバ側のNetworkManagerはWebSocketsをenabledにしなければなりません。
- WebGLはクライアント側だけでしか使えず、サーバ側では別プラットフォームでビルドが必要です。
- WebGLビルドが何も言わずにクラッシュした場合、殆どの場合は**UNETのエラーです。**
エディタやビルドでエラーを確認しましょう。