スポーン(応用)
概要
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