This document is about: FUSION 2
SWITCH TO

Advanced Spawning

Overview

Spawning in Fusion consists of calling Runner.Spawn(), Runner.SpawnAsync(), or Runner.TrySpawn(). This call creates a network entity that is replicated to all other peers.

  • In Server/Client Modes (Server/Host/Single) only the Server can spawn objects, and that spawn is replicated to all clients.
  • In Shared Mode any client may call Spawn(), but that call generates a message to the Shared Server which then initiates the actual spawn.

When Spawn() is called or replicated, a Network Object (a Unity GameObject with a NetworkObject component) is produced in response. How that game object is produced is determined by the Object Provider associated with the NetworkRunner.

Object Provider

The Object Provider is an INetworkObjectProvider implementation that defines how Runner.Spawn() and Runner.Despawn() operations commission and decommission Unity GameObjects. Every active NetworkRunner has an associated instance of an INetworkObjectProvider. By default this will be an instance of NetworkObjectProviderDefault.

Custom Object Provider Uses

Creating your own customized INetworkObjectProvider class allows precise control over the lifecycle of the actual GameObjects which are spawned, and can be useful cases such as;

  • Pooling
  • Dynamically creating Network Objects at runtime
  • Advanced handling for migration to and from single player modes

Assigning the Object Provider

There are several ways to set the Object Provider for a Network Runner.

  • Pass an instance of INetworkObjectProvider when calling Runner.GameStart().

C#

runner.StartGame(new StartGameArgs(){ ObjectProvider = new MyObjectProvider()});
  • or; Add a component which implements INetworkObjectProvider to the Network Runner game object.
  • or; Do neither, and the Runner will automatically add NetworkObjectProviderDefault to its GameObject.

INetworkObjectProvider Implementation

The INetworkObjectProvider interface requires the implementation of the following two methods:

  • AcquireInstance(): used to acquire an object from the pool when NetworkRunner.Spawn() has been called.
  • ReleaseInstance(): used to release and return an object from the pool whe NetworkRunner.Destroy() has been called.

NetworkObjectProviderDefault

NetworkObjectProviderDefault is the default fallback implementation of INetworkObjectProvider. If you do not specify a provider in the StartArgs passed in Runner.StartGame(), the Runner will search its game object for a component which implements INetworkObjectProvider. If no implementation is found, the Runner will then fallback to automatically adding a NetworkObjectProviderDefault component to its game object - which is then used as the Object Provider.

NetworkObjectProviderDefault is a very basic implementation that will produce Objects in response to Spawn() by instantiating a copy of a passed prefab - without any pooling. Conversely, Despawn() calls are handled with Destroy().

Custom Pooling

Object pooling is a common pattern used to minimize memory fragmentation as well as lower the burden placed on the CPU and the garbage collector.

For the same reasons it is advisable, albeit not necessary, to implement pooling of NetworkObjects in your INetworkObjectProvider implementation.

The object pool to be used for a Game Session needs to implement INetworkObjectPool. Furthermore, it has to be known before it is started and assigned to the StartGameArgs.ObjectPool parameter which is passed to the NetworkRunner.StartGame() method. If no object pool is specified, then the NetworkObjectPoolDefault will be used.

Providing Custom Objects at Runtime

In order to dynamically generate game objects at runtime (instead of spawning using Prefabs), you will need to create a custom INetworkObjectProvider class, and override the AcquirePrefabInstance() method to:

  1. Create a GameObject using Instantiate(), new GameObject(), or GameObject.CreatePrimitive().
  2. Add a NetworkObject component if one does not exist already on the GameObject.
  3. Add any desired child GameObjects and NetworkBehaviours.
  4. NetworkObjectBaker.Bake() the Network Object (this must be done last after all changes to the Network Object structure).
  5. Return the NetworkObject instance as the result.

It is possible (and in most cases recommended) to use NetworkObjectProviderDefault as a base class when making your own custom INetworkObjectProvider implementation.

IMPORTANT: Once spawned and attached, Network Objects CANNOT have Network Behaviours added or removed. Any customization of the Network Behaviours on a Network Object must be done before spawning. It is of course still possible to add and remove any non-networked components at any time.

Custom Object Provider Example

This example code demonstrates creating a custom Network Object at runtime, as an alternative to spawning with pre-made Prefabs.

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