スポーン(応用)
概要
Fusionでスポーンを行うには、Runner.Spawn()
/Runner.SpawnAsync()
/Runner.TrySpawn()
を呼び出します。これによって、他のすべてのピアで複製されるネットワーク上のエンティティが生成されます。
- サーバーモード/ホストモード/シングルプレイヤーモード:サーバーのみがオブジェクトをスポーン可能で、そのオブジェクトはすべてのクライアントに複製されます
- 共有モード:どのクライアントでも
Spawn()
を呼び出せますが、呼び出しは共有サーバーへのメッセージを生成し、それから実際のスポーンが行われます
Spawn()
が呼び出された/複製されたことに応じて、ネットワークオブジェクト(NetworkObject
コンポーネントが付いたUnityのゲームオブジェクト)が生成されます。オブジェクトをどのように生成するかは、NetworkRunner
に関連付けられたObject Providerによって決まります。
Object Provider
Object Providerは、INetworkObjectProvider
の実装です。Runner.Spawn()
やRunner.Despawn()
の動作において、Unityのゲームオブジェクトをどのように生成/破棄するかを定義します。すべてのアクティブなNetworkRunner
は、INetworkObjectProvider
のインスタンスが関連付けられていて、デフォルトではNetworkObjectProviderDefault
になります。
独自Object Providerの使用
独自のINetworkObjectProvider
クラスを作成することで、スポーンした実際のゲームオブジェクトのライフサイクルを正確に制御できるようになるため、以下のようなケースで役立ちます。
- オブジェクトプールの作成
- 実行時の動的なネットワークオブジェクトの生成
- シングルプレイヤーモードとの実践的な統合処理
Object Providerの割り当て
NetworkRunner
にObject Providerを設定する方法はいくつかあります。
Runner.StartGame()
呼び出し時にINetworkObjectProvider
インスタンスを渡す
C#
runner.StartGame(new StartGameArgs(){ ObjectProvider = new MyObjectProvider()});
NetworkRunner
のオブジェクトに、INetworkObjectProvider
を実装したコンポーネントを追加する- 何も設定しない(自動的に
NetworkRunner
のオブジェクトにNetworkObjectProviderDefault
が追加される)
INetworkObjectProviderの実装
INetworkObjectProvider
インターフェースは、以下の2つのメソッドを実装する必要があります。
AcquireInstance()
:NetworkRunner.Spawn()
が呼び出された時に、オブジェクトを取得するために使用されますReleaseInstance()
:NetworkRunner.Despawn()
が呼び出された時に、オブジェクトを返却するために使用されます
NetworkObjectProviderDefault
NetworkObjectProviderDefault
は、INetworkObjectProvider
のデフォルトのフォールバックとなる実装です。Runner.StartGame()
のStartArgs
でObject Providerを指定しなければ、NetworkRunner
は、自身のゲームオブジェクトからINetworkObjectProvider
を実装しているコンポーネントを探します。もし実装が見つからない場合、NetworkRunner
はフォールバックとして、自身のゲームオブジェクトに自動的にNetworkObjectProviderDefault
コンポーネントを追加し、それをObject Providerとして使用します。
NetworkObjectProviderDefault
の実装は、非常に基本的なものです。Spawn()
では(オブジェクトプールを使用せずに)プレハブをインスタンス化することでオブジェクトを生成し、Despawn()
ではDestroy()
によってオブジェクトを破棄します。
独自のオブジェクトプール
オブジェクトプールは、メモリ断片化をを最小化し、CPUとガベージコレクションの負荷を抑える一般的なパターンです。
必須ではありませんが、様々な理由から、独自のINetworkObjectProvider
では、NetworkObject
のオブジェクトプールを実装することを推奨します。
実行時の独自オブジェクト生成
(プレハブを使用してスポーンするかわりに)実行時に動的にゲームオブジェクトを生成するには、独自のINetworkObjectProvider
クラスを作成して、AcquirePrefabInstance()
メソッドをオーバーライドする必要があります。
Instantiate()
/new GameObject()
/GameObject.CreatePrivitive()
を使用して、ゲームオブジェクトを生成するNetworkObject
コンポーネントを追加する(追加されていなければ)- 希望する子ゲームオブジェクトや
NetworkBehaviour
を追加する NetworkObjectBaker.Bake()
でネットワークオブジェクトをベイクする(ベイクは、ネットワークオブジェクトの構成がすべて完了した後に行う必要があります)- 戻り値として、ネットワークオブジェクトを返す
独自のINetworkObjectProvider
は、NetworkObjectProviderDefault
を基底クラスにして実装することが可能です(多くのケースではこれを推奨します)。
重要: ネットワークオブジェクトをスポーン/アタッチした後は、NetworkBehaviour
を追加/削除することはできません。ネットワークオブジェクトのNetworkBehaviour
のカスタマイズは、スポーンする前にすべて完了している必要があります。ネットワークに関連しないコンポーネントなら、もちろんいつでも追加/削除することは可能です。
独自Object Providerの例
このコード例は、既製のプレハブからスポーンするかわりに、実行時に独自のネットワークオブジェクトを生成する方法を示ています。
C#
public class BakingObjectProvider : NetworkObjectProviderDefault
{
// For this sample, we are using very high flag values to indicate custom.
// Other values will fall through the default instantiation handling.
public const int CUSTOM_PREFAB_FLAG = 100000;
// The NetworkObjectBaker class can be reused and is Runner independent.
private static NetworkObjectBaker _baker;
private static NetworkObjectBaker Baker => _baker ??= new NetworkObjectBaker();
public override NetworkObjectAcquireResult AcquirePrefabInstance(NetworkRunner runner, in NetworkPrefabAcquireContext context, out NetworkObject result)
{
// Detect if this is a custom spawn by its high prefabID value we are passing.
// The Spawn call will need to pass this value instead of a prefab.
if (context.PrefabId.RawValue == CUSTOM_PREFAB_FLAG)
{
var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
var no = go.AddComponent<NetworkObject>();
go.AddComponent<NetworkTransform>();
go.name = $"Custom Object";
// Baking is required for the NetworkObject to be valid for spawning.
Baker.Bake(go);
// Move the object to the applicable Runner Scene/PhysicsScene/DontDestroyOnLoad
// These implementations exist in the INetworkSceneManager assigned to the runner.
if (context.DontDestroyOnLoad)
{
runner.MakeDontDestroyOnLoad(go);
}
else
{
runner.MoveToRunnerScene(go);
}
// We are finished. Return the NetworkObject and report success.
result = no;
return NetworkObjectAcquireResult.Success;
}
// For all other spawns, use the default spawning.
return base.AcquirePrefabInstance(runner, context, out result);
}
}
Back to top