Unity用ネットコード【Fish-Net】開発メモ
他PCのサーバーに接続できない場合
- ファイアウォールのポートを開ける
- ファイアウォールの設定からアプリを許可する
- 上記でもだめな場合は以下のFish-Network-Discoveryを使ってみる
Fish-Net vs PhotonFusion比較表
コンポーネント名
名称はほぼ同じ
機能 | Fish-Net | PhotonFusion |
---|---|---|
全体管理 | NetworkManager | NetworkRunner |
ゲームオブジェクト同期 | NetworkObject | NetworkObject |
コンポーネント同期 | NetworkBehaviour | NetworkBehaviour |
Transform同期 | NetworkTransform | NetworkTransform |
Animation同期 | NetworkAnimation | NetworkAnimation |
機能
機能 | Fish-Net | PhotonFusion |
---|---|---|
クライアントの接続検知 |
ClientManager.OnRemoteConnectionState のActionの引数からRemoteConnectionStateArgs.ConnectionState.Started で判定する |
OnPlayerJoined |
クライアントの切断検知 |
ClientManager.OnRemoteConnectionState のActionの引数からRemoteConnectionStateArgs.ConnectionState.Stop で判定する |
OnPlayerLeft |
変数の同期 | SyncVar(or List,HashSet,Dictionary)<T>型を使用する | [Networked]アトリビュートを付与する |
自分の所有物か確認する | IsOwner | HasStateAuthority or HasInputAuthority |
同期開始イベント | NetworkBehaviour.OnStartNetwork または OnStartClient or OnStartServer | NetworkBehaviour.Spawn |
同期終了イベント | NetworkBehaviour.OnStopNetwork または OnStopClient or OnStopServer | NetworkBehaviour.Despawn |
VContainerやZenjectを利用したNetworkObjectへの依存性注入 | DefaultObjectPoolを継承したクラスでInstantiateの箇所をContainer.Instantiateに上書きし、NetworkManagerにアタッチする | NetworkObjectProviderDefault(Fusion1の場合はNetworkObjectPool)を継承したクラス内のInstantiatePrefabを上書きしてInjectできるようにする |
ホストマイグレーション | なし | あり |
用語
- リモートクライアント
- ローカルクライアントも含まれる
- ホスト
- サーバー兼リモートクライアントとして扱われる
- リモートサーバー
- ローカルクライアントがサーバーの場合も含まれる
依存性注入
サーバー以外はSpawn内でInstantiateされるため、工夫が必要。
DefaultObjectPool内でInstantiateしているため、これを継承したクラスを作成し、上書きする必要がある。
以下はVContainerの例
※この方法だとFishNetライブラリのコードを一部protectedに修正する必要があり、バージョンアップの時にも毎回修正が必要なのでちょっと微妙かも。。。もっと良い方法があれば教えて頂けると幸いです
public class InjectObjectPool : DefaultObjectPool
{
private LifetimeScope _lifetimeScope;
[Inject] private void Construct(LifetimeScope lifetimeScope) => _lifetimeScope = lifetimeScope;
...
NetworkObject result = _lifetimeScope.Container.Instantiate(prefab, pos, rot, parent);
...
NetworkObject nob = _lifetimeScope.Container.Instantiate(prefab);
...
}
変数関連
- サーバーしか書き込みすることができないので、以下の様に[ServerRpc]アトリビュートを付与し、サーバーに更新命令を与えることで値を更新することができる。
public readonly SyncVar<int> Hoge = new SyncVar<int>();
[ServerRpc]
public void SetHoge(int value) => Hoge.Value = value;
- 値の変更イベント登録
Hoge.OnChange += OnHogeChanged;
サーバークライアントはサーバーとして一回、クライアントとして一回の合計2回イベントが発行される。
他のクライアントと同じ様に動作させたい場合は以下の様に記述する。
private void OnHogeChanged(int prev, int next, bool asserver)
{
if (!asserver)
{
Debug.Log($"Hogeが更新されました : {prev} -> {next}");
}
}
NetworkObjectコンポーネント
ネットワーク上で同期させたいオブジェクトにアタッチするコンポーネント
このコンポーネントをアタッチしたオブジェクト内のコンポーネントがNetworkBehaviourを継承することで、ネットワークオブジェクトに関する機能を利用することができる
プロパティ
- IsNetworked
- オンライン状態、Spawnedな状態
- IsSpawnable
- クライアントと同期時にスポーン可能な状態
- IsGlobal
- どのSceneにいてもSpawn対象になる、Scene機能を利用しない場合はTrueに設定する必要がある
- InitializeOrder
- 同じティック内の他のNetworkObjectと一緒にスポーンされた際に発生するコールバックの優先順位。値が低い方が優先度が高くなる
- DefaultDespawnType
- Despawn時に削除するかプールするかを設定できる
NetworkBehavior
- ネットワークオブジェクトに関する機能を利用することができる
- IsOwnerはタイミング(Spawn後からOnStartServer()またはOnStartClient()発生までの期間)によってはSpawn後であっても必ずfalseになるタイミングがあるため、
Owner.IsLocalClient
を使った方が安全
トラブルシューティング
NetworkConnectionが同じタイミング且つ同じ参照先でも取得の方法(イベントの引数から取得 or ClientManger.Clients[ClientId])によってなぜか中身に差異が出ることがある
asServerなタイミング等でClientMangerを参照すると引数で受け取るNetworkConnectionと差異が出る場合がある、ClientsはSeverManger側にもあるのでSeverManager.Clients[ClientId]を利用すると良い。
Fish-Netでは全体的にasServerなタイミングではSeverManagerを優先して利用し、!asServerの時はClientMangarを使った方が良さそう。
NetworkObjectの所有者が切断してもDespawnしないオブジェクトがある
Spawn時に利用したNetworkConnectionが前述のClientsから取得していた場合にそのような動作になる場合があるので、SeverManagerを試していないなら試してみる
RPC
- 他のConnection(ピア)のメソッドを実行する機能
- メソッドのアトリビュートを記述することで実行することができる
- NetworkObjectがアタッチされたGameObject内のNetworkBehaviourを継承したスクリプト内に記述することで機能する
- クライアント間に対してはサーバーから(SeverRpc)しか実行することができない、そのため各クライアント間でRPCを実行したい場合は、一度クライアントからサーバーへServerRPCした後に、サーバーからクライアントに対してObserversRpcする必要がある
[ServerRpc]
- サーバー側でメソッドを実行させることができる
- NetworkObjectの保有者のみ呼び出すことができる
-
[ServerRpc(RequireOwnership = false)]
と書いた場合は保有者じゃなくても呼び出すことができる - 引数に
NetworkConnection networkConnection = null
と書くとRPCを呼びしたConnectionを取得することができる -
[ServerRpc(RunLocally = true)]
と書くと呼び出したローカルクライアントでも実行される - 以下の様に書くとデータ長さに制限を付与できる。単位はByte
[ServerRpc(DataLength = 3500)] private void ServerSendBytes(byte[] data) {}
- 実行できるタイミングはIsClientInitializedがtrueに変化した後
- それ以前に実行した場合は以下のワーニングが発生し、RPCは実行されない
Cannot complete action because client is not active. This may also occur if the object is not yet initialized, has deinitialized, or if it does not contain a NetworkObject component.
- それ以前に実行した場合は以下のワーニングが発生し、RPCは実行されない
[ObserversRpc]
- サーバーからクライアント側のメソッドを実行させることができる
-
[ObserversRpc(ExcludeOwner = true)]
と書くとNetworkObjectの所有者が実行されない -
[ObserversRpc(ExcludeOwner = true, BufferLast = true)]
と書くとNetworkObjectの所有者が実行されないことに加え、後から参加したクライアントにも最新の値で実行される。
[TargetRpc]
- 第一引数で指定したNetworkConnectionに対してRPCを実行することができる
[TargetRpc]
private void RpcSetAmmo(NetworkConnection conn, int newAmmo)
- 個別チャット等で使えそう
[ObserversRpc][TargetRpc]
- 第一引数がNetworkConnectionであれば個別RPC、異なれば全Connectioonに対してRPCを実行する
Cannel
引数にChannelを加えると信頼性を設定することができる。
- Channel.Reliable : 到着保証
- Channel.Unreliable : 非保証
[ServerRpc]
private void RpcTest(string txt, Channel channel = Channel.Reliable)
イベント
- クライアントの接続状態変更イベント
- SceneManager.OnClientLoadedStartScenes
- クライアントSceneLoad完了イベント
- SceneManager.OnClientLoadedStartScenes
- サーバーの接続状態変更イベント(サーバー権限ありの場合のみ発行される)
- ServerManager.OnServerConnectionState
Scene
- NetworkObjectはSceneの配下のオブジェクトとして管理されるため、LoadしているSceneが異なるNetworkObjectはHierarchy上に表示されない。
- FishNetのSceneManagerのメソッドはサーバー権限のあるConnectionのみ実行することができる
GlobalScene
- 同一サーバーへ接続している全クライアントが(強制的に)ロードするScene
- LoadGlobalScenes(loadSceneData)で読み込むことができる
ConnectionScene
- Connection毎に読み込み可能なScene
- LoadConnectionScenes(connection,loadSceneData)で読み込むことができる
単一Sceneとして読み込む
SceneLoadDataのReplaceOptionプロパティをAllに変更することで単一Sceneとして呼び出すことができ、Unity標準のSceneManager.LoadScene()と同じような動作となる。
SceneLoadData sld = new SceneLoadData(_rideSceneData.NextScene.Name);
sld.ReplaceScenes = ReplaceOption.All;
InstanceFinder.SceneManager.LoadGlobalScenes(sld);
ロードできるScene
- サーバーのConnection上でロードしているSceneのみロード可能
~をしてみたい
ホスト(サーバー)のコネクションを取得したい
FishNetの機能として提供されていない。(IsHostを取得できるのは自分自身のみ)※公式確認済み
どうしても必要であればネットワークプロパティやRPCで通知する。