Network Object
はじめに
NetworkObject
はGameObject
にネットワークIDを割り当て、ネットワーク上で共有できるようにします。すべてのプレイヤーが見ることができ、シーンの動的な部分であるGameObject
には、NetworkObject
スクリプトを設定します。
Fusionには、カスタムのネットワーク動作を派生させるための2つのNetworkObject
ベースクラスがあります。
- シミュレーション関連の動作には
SimulationBehaviour
、そして NetworkBehaviour
は、[Networked]
の状態を維持または追跡する動作のためです。
NetworkObject
GameObject
のルートノードに1つのNetworkObject
スクリプトを置くだけで、階層全体を制御することができます。ランタイム時には、NetworkObject
はその階層にあるすべてのSimulationBehaviour
とNetworkBehaviour
を検出します。
必要に応じて、NetworkObject
をネストすることができます。この場合、NetworkObject
はネストした子の中は検索せず、子のNetworkObject
にその下のすべてのSimulationBehaviour
とNetworkBehaviour
を追跡させます。典型的な使用例としては、2人のプレイヤーが操作するシャーシと砲台からなる戦車で、ドライバーがシャーシの方向(移動)を制御し、ガンナーが砲台(射撃)を制御するというものがあります。
プレハブ/オブジェクトテーブル
Prefab
パラメータがFusionにとって意味のあるものであるためには(そして、すべてのクライアントがどのプレハブであるかを正確に把握するには)、プレハブ自体がFusionに登録されている必要があります。通常であれば、Fusionツールセットが自動的にプレハブを検出して登録しますが、NetworkProjectConfig
を選択して、Unity InspectorのRebuild Object Table
を押すことで、手動で検出することもできます。
インスタンス化/スポーン化
NetworkObject
プレハブのインスタンス化には2つのステップがあります。
- 編集時: クライアントの
NetworkProjectConfig
アセットが同一で、NetworkObject
プレハブの ID が一致していることを確認します。これはNetworkProjectConfig
アセットでRebuild Object Table
を実行することで行います。 - ランタイム では、
NetworkRunner
のSpawn()
を、NetworkObject
プレハブをパラメータとして呼び出します。
重要: 一般的なUnityのInstantiate()
メソッドを呼び出すと、壊れたローカルインスタンスが作成されてしまいます!
破壊 / デスポーン
NetworkObject
を破壊するには、Runner.Despawn()
を呼び出し、パラメータとしてオブジェクトを渡します。一番上のオブジェクトだけをデスポーンするには、まずネストしたオブジェクトを手動でペアレント解除する必要があります。
DestroyWhenStateAuthorityLeaves
共有モードのみ
StateAuthority
Playerがゲームから離れた時に、このNetworkObjectが自動的にデスポーンされるかどうかを示します。
HasStateAuthority プロパティ
HasStateAuthority
プロパティは、SharedモードとServer/Clientモードの両方で有効で、次の場合にtrueを返します。
- Server/Client モード; Runner.IsServer
が true。
- Shared Mode; StateAuthority == Runner.LocalPlayer
.
このプロパティは、スクリプトで使用して、この NetworkObject の権威あるインスタンスであるかどうかを判断します。
StateAuthority プロパティ
State Authority は、どの PlayerRef
のピアが NetworkObject
の状態 (Networked Properties) の最終的な権威であるかを示し、その Networked Property の値は Tick Snapshots として他のクライアントに複製されます。
_ 注意:_ StateAuthority
プロパティは Shared Mode にのみ適用されます。Server/Clientモードでは、この値は常にPlayerRef.None
となります。Server/Hostピアは常にState Authorityとして機能するためです。
State Authorityの変更
Server/Clientモードの場合: State Authorityは常にHost/Serverに属しており、移譲することはできません。StateAuthority
は常にPlayerRef.None
となります。
共有モード: プレイヤーは、NetworkObject
に対するState Authorityを持ちます(Photon共有サーバは、NetworkObject
のState Authorityを管理しますが、それらのオブジェクトに対するState Authorityは持ちません)。
クライアントから Object.RequestStateAuthority()
を呼び出すと、PlayerRef
を NetworkObject
の State Authority として割り当てることができます。Photon Shared Server は、以下の場合にのみリクエストを許可します。
- Object が AllowStateAuthorityOverride
を有効にしている場合。
- Object に現在の State Authority
がない場合。
AllowStateAuthorityOverride
が有効になっておらず、すでにState Authorityが割り当てられている場合、リクエストは無視されます。現在のState Authority Playerは、まずそのクライアントで Object.ReleaseStateAuthority()
を呼び出してObjectを解放する必要があります。
AllowStateAuthorityOverride
共有モードのみ適用
他のプレイヤーが現在StateAuthorityである場合に、そのプレイヤーがStateAuthorityを取得できるかどうかを示します。falseの場合、現在のState Authorityは最初に ReleaseStateAuthority()
を呼び出すか、接続を切断しなければなりません。ほとんどのユースケースでは、プレハブやシーンオブジェクトに対して AllowStateAuthorityOverride
を有効にしておくとよいでしょう。
このプロパティはspawn前に設定する必要があり、spawn後に変更することはできません。
HasInputAuthority プロパティ
HasInputAuthority
プロパティは Runner.LocalPlayer == Object.InputAuthority
の場合、true
を返します。スクリプトで、NetworkRunner.LocalPlayer
が NetworkObject
の入力機関であるかどうかをテストするために使用します。
注意: Input Authorityと Fusion の INetworkInput
処理は、主に Server/Client Mode での使用を想定しています。クライアントの予測や再シミュレーションの基礎となるものだからです。しかし、Shared Modeでは、ユーザーの入力はすぐにState Authorityによって消費されるため、INetworkStruct
入力システムは必要ありません。しかし、将来的にサーバー/クライアントモードへの移行を考えていて、後々のリファクタリングを避けたい場合は、Shared ModeでもFusion入力システムを使用することができます。
InputAuthority プロパティ
InputAuthority
プロパティは、この NetworkObject
の FixedUpdateNetwork
において GetInput()
が呼ばれたときに、どの PlayerRef
の入力データ (INetworkInput
) が返されるかを示すものです。プレイヤー入力は、Input Authorityを持つクライアントと、State Authorityを持っているクライアントのみが利用できます。その他のクライアント(Proxy)は GetInput()
が呼び出されると false
を返します。
Input Authorityの変更
In Shared Mode: INetworkInput
の取り扱いは、Shared Mode ではオプションです。しかし、この場合、現在の StateAuthority
のみが InputAuthority
を変更でき、 Object.AssignInputAuthority()
メソッドを使用して変更することが可能です。
Input AuthorityはState Authorityと一致するか、PlayerRef.None
である必要があります。Input Authorityは StateAuthority
の変更に伴って自動的に変更されないので、このような場合は必要に応じて InputAuthority
を変更するようにしてください。
public class StateAuthChangeExample : NetworkBehaviour, IStateAuthorityChanged
{
void IStateAuthorityChanged.StateAuthorityChanged() {
// If this object is using Fusion Inputs, the new State Authority
// should acquire Input Authority when it gets State Authority.
if (Object.HasStateAuthority) {
Object.AssignInputAuthority(Object.StateAuthority.PlayerId);
}
}
}
In Server/Client Mode: InputAuthority
PlayerRef
は Server 上で Object.AssignInputAuthority()
メソッドによってのみ変更することができます。
SimulationBehaviour
すべての SimulationBehaviour
は、Object
プロパティを使って、関連する NetworkObject
にアクセスできます。
Simulation は、Fusionでネットワークの状態を更新する方法です。シミュレーションの一部であったり、シミュレーションの状態に影響を与える動作は、MonoBehaviour
ではなく、SimulationBehaviour
から派生しなければなりません。
例えば、TanksサンプルのLevelManager
はパワーアップを産み出します。パワーアップはNetworkObject
ですが、LevelManager
はシミュレーションの状態に影響を与え、それを知るだけでその動作を実行できます。したがって、SimulationBehaviour
を継承するのが正しい方法です。
public class LevelManager : SimulationBehaviour
{
[SerializeField] private float _powerupDelay;
[SerializeField] private NetworkObject _powerupPrefab;
private TickTimer _powerupTimer;
public override void FixedUpdateNetwork()
{
// All of the level management logic happens server-side, so bail if we're not the server.
if (!Object.HasStateAuthority) return;
// Only active duty of the LevelManager is to spawn powerups whenever the timer expires
if (_powerupTimer.ExpiredOrNotRunning(Runner))
{
// Reset timer, and check if there is a free spot to spawn a new powerup
_powerupTimer = TickTimer.CreateFromSeconds(Runner,_powerupDelay);
SpawnPoint spawnPoint = GetSpawnPoint(SpawnPoint.Type.Powerup);
if (spawnPoint != null)
{
Debug.Log("Spawning Powerup");
NetworkObject powerupobj = Runner.Spawn(_powerupPrefab, spawnPoint.transform.position, spawnPoint.transform.rotation, PlayerRef.None);
powerupobj.GetComponent<Powerup>().Init();
}
else
{
Debug.Log("Not Spawning Powerup - no free spot");
}
}
}
}
動作が [Networked]
プロパティへのアクセスを必要とする場合は、代わりに NetworkBehaviour
を派生させる必要があります。
NetworkBehaviour
NetworkBehaviour
は、同じノードまたは親ノード上のNetworkObject
を必要とします。
NetworkBehaviour
はSimulationBehaviour
であり、同期した状態を保持することができます。そのため、関連するNetworkObject
を持たなければありません。
Networked Property
内部的には、Fusion は各 tick のネットワーク状態全体を、Snapshot
と呼ばれる一つのメモリバ ッファーとして保存しています。Networked Propertyは、NetworkedBehaviour
に含まれるどの変数がそのネットワー ク状態に含まれるかを定義します。
Networked Property を定義するには、[Networked]
属性を使用します。Fusion はこれらのタグ付けされたプロパティを独自の高性能データバッファとデルタ圧縮に自動的に接続します。 プロパティのセッターとゲッターは、コンパイル時にカスタムコードに置き換えられ、メモリ割り当てのオーバーヘッドを排除し、最適なパフォーマンスを提供します。
public class PlayerBehaviour : NetworkedBehaviour
{
[Networked] public float Health { get; set; }
}
コンパイル時に、Fusionは空のget/setスタブを実際のネットワーク状態データにアクセスするコードに置き換えます。直接アクセスすることで、メモリ割り当てのオーバーヘッドをなくし、最適なパフォーマンスを提供します。手動では実装 しないでください。
このように状態バッファに直接アクセスすることで、変更があるとすぐに状態 に反映されます。
ネットワーク状態に影響するロジックを書くには、FixedUpdateNetwork()
をオー バーライドして実装してください。
public override void FixedUpdateNetwork()
{
Health += Runner.DeltaTime * HealthRegen;
}
許容される型
- プリミティブ
- byte, sbyte
- Int16, Int32, Int64
- UInt16, UInt32, UInt64
- float
- double
- float
- double
- bool (convereted to int)
- 最大長
Length
を持つ文字列は[Capacity]
属性を用いて設定します(デフォルトは 16 )。 - Unity 構造体タイプ (ILWeaver.cs で定義)
- Vector2, Vector3, Vector4
- Quaternion
- Matrix4x4
- Vector2Int, Vector3Int
- BoundingSphere
- Bounds
- Rect
- BoundsInt
- RectInt
- Color, Color32
- System.Guid
- ユーザー定義INetworkStructs
- Fusion 定義 INetworkStructs
- NetworkString<IFixedStorage>
- NetworkBool
- Ptr
- Angle
- BitSet64, BitSet128, BitSet192, BitSet256
- PlayerRefSet
- NetworkId
- NetworkButtons
- NetworkRNG
- NetworkObjectGuid
- NetworkPrefabRef
- NetworkObjectHeader
- NetworkPrefabId
- SceneRef
- TickTimer
- IFixedStorage (_2, _4, _8, _16, _32, _64, _128, _256, _512) -Fusion 型
- NetworkObject (
NetworkId
として直列化される) - NetworkBehaviour (
NetworkId
とNetworkBehaviour
インデックスとして直列化される) - PlayerRef (
PlayerRef.PlayerId
として直列化される)
- NetworkArray<T> 最大長
Length
を持つ文字列は[Capacity]
属性を用いて設定します(デフォルトは 1 )。 - NetworkDictionary<K, V> 最大値
Count
を持つものは[Capacity]
を使って設定します。 - NetworkLinkedList<T> 最大値
Count
を持つものは[Capacity]
を使って設定します。
Accuracy属性
[Accuracy]
属性を用いることで、個々の数値型プロパティの精度を制御することができます。
public class PlayerBehaviour : NetworkedBehaviour
{
[Networked, Accuracy(0.001)]
public float Health { get; set; }
}
Capacity属性
NetworkArray<T>, NetworkDictionary<K, V>, NetworkLinkedList<T>と文字列の最大サイズを定義するために使用されます。
public class MyNetworkBehaviour : NetworkedBehaviour
{
[Networked, Capacity(14)]
string MyString { get; set; }
[Networked, Capacity(8)]
NetworkArray<byte> MyArray { get; }
}
OnChangedに反応する
Fusion では、Networked プロパティが変更されたときに、ゲームコードをトリガーすることができます。リアクティブなコードを書くには、[Networked]
属性にある (OnChanged)
パラメータを使用します。
public class Powerup : NetworkBehaviour
{
[Networked(OnChanged = nameof(OnTypeChanged))] public Type type { get; set; }
// Has to be public static void
public static void OnTypeChanged(Changed<Powerup> changed)
{
changed.Behaviour.OnTypeChanged();
}
private void OnTypeChanged()
{
// Some logic reacting to the value change of the "type" property
}
}
コールバック名の指定に加えて、どのマシンでコールバックが実行されるかを制御することが可能です。
OnChangedLocal
(default false): trueに設定すると、プロパティを変更したマシン上 でも イベントフックが呼び出されるようになります。OnChangedRemote
(default true): プロパティを変更したマシン上でしかイベントフックを呼ばれないようにするには、falseに設定します。
オーバーライド可能なメソッド
NetworkObject
には、いくつかのライフサイクルメソッドが追加されており、これらはすべて、SimulationBehaviour
やNetworkBehaviour
を実装する際にオーバーライドすることができます。
関数 | 説明 |
---|---|
FixedUpdateNetwork() | Fusionの固定タイムステップのコールバックです。ゲームのコアロジックに使用します。 |
Spawned() | ポストスポーンのコールバック |
Despawned(bool hasState) | ネットワークオブジェクトがデスポーンされる前に呼び出されます。
--- bool hasState: 動作の状態にまだアクセスできるかどうか。 |
Render() | シミュレーション後のフレームレンダリングコールバック。すべてのシミュレーションが終了した後に実行されます。Fusionが物理演算を行う場合、UnityのUpdateの代わりに使用します。 |
FixedUpdateNetwork
FixedUpdateNetwork()
(FUN()と略されます)は、あるティックから次のティックへと新しい状態をシミュレートするときに呼び出されます。この間隔のタイムステップは、NetworkProjectConfig
アセットのSimulation > Tick Rate
で定義されます。このタイムステップは、任意の SimulationBehaviour
や NetworkBehaviour
から、Runner.DeltaTime
プロパティを使ってアクセスできます。
FixedUpdateNetwork()
は、同じ状態遷移に対して複数回呼び出すことができ、サーバーから受信した更新情報(ground truth)に基づいて、現在の予測状態を再シミュレーションします。 [Networked]
のプロパティでは、再シミュレーションは透過的です。なぜなら、Fusionは再シミュレーションのためにFixedUpdateNetwork()
を呼び出す前に、ネットワークの状態をリセットするからです。
重要: ネットワーク化されていない通常のローカルな状態変数(クラスメンバーなど)はリセットされず、単に状態の進行が追加されたとみなされます。