This document is about: FUSION 2
SWITCH TO

NetworkObjectProvider

INetworkObjectProvider

When Spawned is called, the INetworkObjectProvider that was passed in StartGame() StartArgs handles getting/creating the GameObject that will be attached.

NetworkObjectProviderDefault

The default implementation of INetworkObjectProvider is NetworkObjectProviderDefault. If you do not specify a provider in the StartArgs when starting a Runner, the Runner will search the Runner game object for an INetworkObjectProvider component. If none is found there, the Runner will automatically add NetworkObjectProviderDefault.

This very basic implementation 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().

Pooling

The simplest way to create a INetworkObjectProvider that pools objects instead of instantiating and destroying them is to derive from NetworkObjectProviderDefault and override the InstantiatePrefab and DestroyPrefabInstance:

C#

public class PooledNetworkObjectProvider : NetworkObjectProviderDefault
{
    protected override NetworkObject InstantiatePrefab(NetworkRunner runner, NetworkObject prefab)
    {
        // Get object from pool and return it.
    }

    protected override void DestroyPrefabInstance(NetworkRunner runner, NetworkPrefabId prefabId, NetworkObject instance)
    {
        // Return the instance to the pool.
    }
}
Reset Local State

When borrowing an object from the pool or returning it, make sure to reset any local (non-networked) state on the object, e.g. rigidbody velocities, animators, particle and trail effects, timers and other plain C# fields. Otherwise, that state bleeds through the lifetimes of the pooled object into its next use. Networked state does not need to be reset manually, it is reset by Fusion on its own.

Spawning Custom Objects

Instead of relying on remade Prefabs, it is possible to build a custom NetworkObject at runtime.

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.

C#

public class BakingObjectProvider : NetworkObjectProviderDefault
{
  // For this sample, we are using very high flag values to indicate that we need to do a custom instantiate
  // Values lower than this 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 AcquireInstance(NetworkRunner runner, in NetworkObjectAcquireContext 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.TypeId.IsPrefab && context.TypeId.AsPrefabId.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 (regular prefabs AND scene objects, provided via context.AttachableInstance),
    // use the default behaviour.
    return base.AcquireInstance(runner, context, out result);
  }
}

Last updated on

Back to top