This document is about: FUSION 1
SWITCH TO

This page is a work in progress and could be pending updates.

Prefabs

Overview

Any prefab with NetworkObject script on its root GameObject and with the Is Spawnable checkbox ticked can be spawned at runtime.

Prefab NO with IsSpawnable
A Spawnable Network Object Prefab

Fusion avoids creating hard references to NetworkObject prefabs whenever possible. If a prefab is addressable or a Resource, Fusion will not reference it directly and store the address/resource path instead. In other case, a hard reference to the prefab will be stored in the config.

To make sure user code does not form hard references as well, use NetworkPrefabRef type to reference prefabs. NetworkPrefabRef is based on the prefab's GUID, so it is resilient to renaming/moving prefabs in the project.

Registering Prefabs

Fusion needs prefabs being registered before it is able to perform a networked spawn. Prefabs GUIDs and data needed to load them (address, resource path or a static reference) are stored in NetworkPrefabTable accessible with NetworkProjectConfig.PrefabTable property.

All the spawnable NetworkObject prefabs in the project present at build time are detected and registered automatically. You can verify this by inspecting Prefabs property of the NetworkProjectConfig.

  • [A] stands for Addressable prefabs.
  • [R] stands for Resources prefabs.
  • [S] stands for Statically-referenced prefabs.
Prefab list
Spawnable prefab info stored in the config.
Prefabs cannot be added, removed or reordered manually. Use the Is Spawnable checkbox on the prefab instead.

Alternatively, you can inspect Prefab Source label field of a prefab.

PrefabSource
Prefab Source for a prefab that is statically referenced (not a Resource and not addressable).

If either the table is incomplete or the Prefab Source displays an invalid value, use Fusion > Rebuild Prefab Table menu item or Rebuild Prefab Table button in the config inspector.

All the spawnable prefabs are tagged with FusionPrefab label as well. This means l:FusionPrefab can be used to as a term in the Project Window search box:

FusionPrefab label
Searching for assets with label `FusionPrefab`

Prefabs that are not present at built time (e.g. procedurally generated or new ones added with Addressables during a build's lifetime) can be registered at runtime with NetworkPrefabTable.TryAdd.

C#

// this will register an Addressable prefab with a know guid without having to load it first
NetworkPrefabId RegisterAddressablePrefab(NetworkProjectConfig config, NetworkObjectGuid guid) 
{
    var source = ScriptableObject.CreateInstance<NetworkPrefabSourceUnityAddressable>();
    source.Address = new AssetReferenceGameObject(guid.ToUnityGuidString());
    
    if (config.PrefabTable.TryAdd(guid, source, out var id)) {
        return id;
    }
    
    throw new ArgumentException($"Failed to register addressable guid: {guid}");
}

// this will register any prefab, but it needs to be loaded first (from whatever the source)
NetworkPrefabId RegisterPrefab(NetworkProjectConfig config, NetworkObject prefab) 
{
    var source = new NetworkPrefabSourceStatic {
        PrefabReference = prefab
    };
    
    if (config.PrefabTable.TryAdd(prefab.NetworkGuid, source, out var id)) {
        return id;
    }
    
    throw new ArgumentException($"Failed to register prefab with guid: {prefab.NetworkGuid}");
}
All the clients need to register prefabs in exactly same order (NetworkPrefabId assignment is sequential). Make sure a prefab is registered on all the clients before there is an attempt to spawn it (e.g. register it before starting a runner).

Spawning Prefabs

Using NetworkPrefabRef

To spawn prefabs use one of NetworkRunner.Spawn methods. The recommended way is to use NetworkPrefabRef fields to reference and to spawn prefabs, as it prevents hard references from being formed.

C#

public class SpawnWithNetworkPrefabRef : NetworkBehaviour {
  public NetworkPrefabRef PrefabRef;

  public NetworkObject SpawnPrefab() {
    return Runner.Spawn(PrefabRef);
  }
}

If a prefab is addressable or a Resource, Fusion will load the actual prefab before returning from Spawn. In case of Addressables, AsyncOperationHandle.WaitForCompletion will be used to ensure the prefab has been loaded. In case of Resources, Resources.Load is used. If this introduces a significant delay, consider preloading prefabs - FusionAddressablePrefabsPreloader provides a sample implementation.

Asynchronous loading is planned but not yet supported.

Using Prefab References

Alternatively, NetworkObject, GameObject or SimulationBehaviour subclass references can be used. Spawn will throw an InvalidOperationException if the reference passed does not belong to the spawnable prefab's root GameObject. In case of NetworkObject and GameObject references, [NetworkPrefab] attribute can be used to limit the inspector selection to only spawnable prefabs.

C#

public class SpawnWithReferences : NetworkBehaviour {
  [NetworkPrefab]
  public GameObject GameObjectRef;
  [NetworkPrefab]
  public NetworkObject NetworkObjectRef;
  public NetworkTransform SimulationBehaviourSubclassRef;

  public NetworkObject SpawnWithGameObjectRef() {
    return Runner.Spawn(GameObjectRef);
  }

  public NetworkObject SpawnWithNetworkObjectRef() {
    return Runner.Spawn(NetworkObjectRef);
  }

  public NetworkTransform SpawnWithSimulationBehaviourSubclassRef() {
    return Runner.Spawn(SimulationBehaviourSubclassRef);
  }
}
The snippet above creates hard-references. If a referenced prefab is addressable, this will lead to it being effectively duplicated: one copy in the build and one copy in Addressables data.

Using NetworkPrefabId

After a prefab is registered with NetworkPrefabTable, a NetworkPrefabId is returned. This type is size and network-optimized and meant to be used in runtime only, as its value for a prefab might be different once another prefab is added to/removed from the project. It is especially useful when registering additional prefabs manually with NetworkPrefabTable.TryAdd.

C#

public class SpawnWithNetworkPrefabId : NetworkBehaviour {
  [NonSerialized]
  public NetworkPrefabId PrefabId;
  public UnityEngine.AddressableAssets.AssetReferenceGameObject PrefabRef;

  public void RegisterPrefab() {
    var source = ScriptableObject.CreateInstance<NetworkPrefabSourceUnityAddressable>();
    source.Address = PrefabRef;
    if (!Runner.Config.PrefabTable.TryAdd(NetworkObjectGuid.Parse(PrefabRef.AssetGUID), source, out PrefabId)) {
      Debug.LogError($"Failed to register {PrefabRef}");
    }
  }

  public NetworkObject SpawnPrefab() {
    Debug.Assert(PrefabId.IsValid);
    return Runner.Spawn(PrefabId);
  }
}

Dynamically registered prefabs can be spawned with their GUIDs or object references (if they have been loaded already) as well.

Custom

It is possible to use other means of referencing prefabs. Existing projects that use Addressables and AssetReferenceGameObject fields already can instead first load the prefab and then spawn it, using the resulting reference:

C#

public class SpawnWithAddressables : NetworkBehaviour {
  public UnityEngine.AddressableAssets.AssetReferenceGameObject AddressablePrefabRef;

  public void SpawnAsync() {
    AddressablePrefabRef.LoadAssetAsync().Completed += asyncOpHandle => {
      Runner.Spawn(asyncOpHandle.Result); // asyncOpHandle.Result is of GameObject type
    };
  }
}

Similar approach can be used for projects already using Resources paths to reference prefabs.

Custom Content Delivery

If a project depends on a custom content delivery system, implementing custom prefab source should be considered. This allows the user to keep using NetworkPrefabRef, asset references and editor prefab auto-discovery regardless of how the prefab is loaded at runtime. What follows is a simplified sample of how custom prefab source based on Asset Bundles can be implemented.

First, a runtime class implementing INetworkPrefabSource is needed. The interface represents data and logic needed to load a single prefab. Instead of implementing INetworkPrefabSource directly, the following snippet uses NetworkPrefabSourceUnityBase - a ScriptableObject-based base class that provides the basic implementation.

C#

public class NetworkPrefabSourceUnityAssetBundles : NetworkPrefabSourceUnityBase {
  // name of the asset bundle
  public string AssetBundleName;
  // name of the asset in the asset bundle
  public string AssetName;

  private GameObject _prefab;

  // this is what gets displayed in prefab's Prefab Source label field
  public override string EditorSummary => $"[AssetBundle: {AssetBundleName}[{AssetName}]]";

  public override void Load(in NetworkPrefabLoadContext context) {

    // load the bundle, if not yet loaded
    var bundle = AssetBundle.GetAllLoadedAssetBundles()
      .FirstOrDefault(x => x.name == AssetBundleName);

    if (bundle == null) {
      var bundlePath = System.IO.Path.Combine(Application.streamingAssetsPath, AssetBundleName);
      bundle = AssetBundle.LoadFromFile(bundlePath);
    }

    if (bundle == null) {
      context.Error(new InvalidOperationException($"Unable to load asset bundle {AssetBundleName}"));
    } else {
      // actually load the prefab
      _prefab = bundle.LoadAsset<GameObject>(AssetName);
      context.Loaded(_prefab);
    }
  }

  public override void Unload() {
    if (_prefab) {
      Destroy(_prefab);
      _prefab = null;
    }
  }
}

Next, an editor-only implementation of INetworkPrefabSourceFactory is needed. The resulting class detects whether a spawnable prefab at assetPath should use custom asset loading - if yes, then it returns an initialized instance of NetworkPrefabSourceUnityAssetBundles. There is no need to register the factory itself, as it is automatically detected by Fusion editor scripts.

C#

public class NetworkPrefabAssetFactoryAssetBundles : INetworkPrefabSourceFactory {
  int INetworkPrefabSourceFactory.Order => 0;
  Type INetworkPrefabSourceFactory.SourceType => typeof(NetworkPrefabSourceUnityAssetBundles);

  NetworkPrefabSourceUnityBase INetworkPrefabSourceFactory.TryCreate(string assetPath) {
    var prefabBundle = AssetDatabase.GetImplicitAssetBundleName(assetPath);
    if (!string.IsNullOrEmpty(prefabBundle)) {
      var result = ScriptableObject.CreateInstance<NetworkPrefabSourceUnityAssetBundles>();
      result.AssetBundleName = prefabBundle;
      result.AssetName = System.IO.Path.GetFileName(assetPath);
      return result;
    }
    // returning null instructs Fusion to use other factories with greater Order
    return null;
  }
}

With these in place, any spawnable prefab assigned to an Asset Bundle should display its Prefab Source as Asset Bundle and the entry in the Prefabs property in the config inspector should be prefixed with [AB]. If that is not the case immediately, consider using the Fusion > Rebuild Prefab Table menu item.

Prefab assigned to an Asset Budle
A prefab assigned to `testassetbundle` Asset Bundle.
Prefab assigned to an Asset Budle
An entry in the config `Prefabs` property.
A production-ready implementation will need to deal with bundles lifetime and dependencies management. Also, to avoid having to use Rebuild Prefab Table repeatedly, some editor integration code is needed to listen to Asset Bundle assignment changes (check NetworkPrefabAssetFactoryAddressable).
Back to top