Skip to main content

Object Spawning

In Unity, you typically create a new game object using the Instantiate function. Creating a game object with Instantiate will only create that object on the local machine. Spawning in Netcode for GameObjects (Netcode) means to instantiate and/or spawn the object that is synchronized between all clients by the server.

Network Prefabs

A network Prefab is any unity Prefab asset that has one NetworkObject component attached to a GameObject within the prefab. More commonly, the NetworkObject component is attached to the root GameObject of the Prefab asset because this allows any child GameObject to have NetworkBehaviour components automatically assigned to the NetworkObject. The reason for this is that a NetworkObject component attached to a GameObject will be assigned (associated with) any NetworkBehaviour components on:

  • the same GameObject that the NetworkObject component is attached to
  • any child or children of the GameObject that the NetworkObject is attached to.
note

A caveat of the above two rules is when one of the children GameObjects also has a NetworkObject component assigned to it (a.k.a. "Nested NetworkObjects"). Because nested NetworkObject components aren't permited in network prefabs, Netcode for GameObjects won'tify you in the editor if you are trying to add more than one NetworkObject to a Prefab and won't allow you to do this.

When a NetworkBehaviour is assigned to a NetworkObject, the NetworkObject.NetworkObjectId is used to help determine which NetworkBehaviour component instance will receive an update to a NetworkVariable or where to invoke an RPC. A NetworkObject component can have one or more NetworkBehaviour components assigned to it.

Registering a Network Prefab

One of the requirements to be able to spawn a network Prefab instance is that it must be registered with the NetworkManager via the "Network Prefabs" property list. The two steps to registering a network Prefab with NetworkManager:

  1. Create a network Prefab by creating a Prefab with a NetworkObject component attached to the root GameObject
  2. Add your network Prefab to the Network Prefabs list poperty of the NetworkManager.

Spawning a Network Prefab (Overview)

Netcode uses a server authoritative networking model so spawning netcode objects can only be done on a server or host. To spawn a network prefab, you must first create an instance of the network Prefab and then invoke the spawn method on the NetworkObject component of the instance you created.
In most cases, you will want to keep the NetworkObject component attached to the root GameObject of the network prefab.

By default a newly spawned network Prefab instance is owned by the server unless otherwise specified.

See Ownership for more information.

The following is a basic example of how to spawn a network Prefab instance (with the default server ownership):

GameObject go = Instantiate(myPrefab, Vector3.zero, Quaternion.identity);
go.GetComponent<NetworkObject>().Spawn();

The NetworkObject.Spawn method takes 1 optional parameter that defaults to true:

public void Spawn(bool destroyWithScene = true);

When you set the destroyWithScene property to false it will be treated the same as when you set Object.DontDestroyOnLoad. Typically, you use this if you are loading a scene using LoadSceneMode.Single parameter.

Learn more about Netcode Scene Management here

Destroying / Despawning

By default, a spawned network Prefab instance that is destroyed on the server/host will be automatically destroyed on all clients.

When a client disconnects, all network Prefab instances created during the network session will be destroyed on the client-side by default. If you don't want that to happen, set the DontDestroyWithOwner field on NetworkObject to true before despawning.

To do this at runtime:

m_SpawnedNetworkObject.DontDestroyWithOwner = true;
m_SpawnedNetworkObject.Despawn();

To make this the default from the editor Inspector view:

image

As an alternative way, you can make the NetworkObject.DontDestroyWithOwner property default to true by setting it on the NetworkObject itself like in the above screenshot.

Despawning

Only a server can despawn a NetworkObject, and the default despawn behavior is to destroy the associated GameObject. to despawn but not destroy a NetworkObject, you should call NetworkObject.Despawn and pass false as the parameter. Clients will always be notified and will mirror the despawn behavior. If you despawn and destroy on the server then all clients will despawn and then destroy the GameObject that the NetworkObjet component is attached to.

On the client side, you should never call Object.Destroy on any GameObject with a NetworkObject component attached to it (this isn't supported and will cause an exception to be thrown). If you want to use a more client authority model, have the client with ownership invoke a ServerRpc to defer the despawning on server side.

The only way to despawn NetworkObject for a specific client is to use NetworkObject.NetworkHide. See: Object Visibility for more information on this.

danger

If you have GameObject children, with NetworkBehaviour components attached, of a parent GameObject, with a NetworkObject component attached, you can't disable the GameObject children before spawning or despawning. Doing so, in v1.0.0, can cause unexpected results and it's recommended to make sure all children are enabled in the hierarchy before spawning or despawning.

Dynamically Spawned Network Prefabs

Netcode for GameObjects uses the term "dynamically spawned" to convey that the NetworkObject is being spawned via user specific code. Whereas a player or in-scene placed NetworkObject (with scene management enabled) is typically spawned by Netcode for GameObjects. There are several ways to spawn a network Prefab via code:

Dynamic Spawning (non-pooled):

This type of dynamically spawned NetworkObject typically is a simple wrapper class that holds a reference to the Prefab asset. In the example below, the NonPooledDynamicSpawner.PrefabToSpawn property holds a reference to the network prefab:

    public class NonPooledDynamicSpawner : NetworkBehaviour
{
public GameObject PrefabToSpawn;
public bool DestroyWithSpawner;
private GameObject m_PrefabInstance;
private NetworkObject m_SpawnedNetworkObject;

public override void OnNetworkSpawn()
{
// Only the server spawns, clients will disable this component on their side
enabled = IsServer;
if (!enabled || PrefabToSpawn == null)
{
return;
}
// Instantiate the GameObject Instance
m_PrefabInstance = Instantiate(PrefabToSpawn);

// Optional, this example applies the spawner's position and rotation to the new instance
m_PrefabInstance.transform.position = transform.position;
m_PrefabInstance.transform.rotation = transform.rotation;

// Get the instance's NetworkObject and Spawn
m_SpawnedNetworkObject = m_PrefabInstance.GetComponent<NetworkObject>();
m_SpawnedNetworkObject.Spawn();
}

public override void OnNetworkDespawn()
{
if (IsServer && DestroyWithSpawner && m_SpawnedNetworkObject != null && m_SpawnedNetworkObject.IsSpawned)
{
m_SpawnedNetworkObject.Despawn();
}
base.OnNetworkDespawn();
}
}

Consumable and/or items that can be picked up by a player or NPC(that is, a weapon, health, potion, etc.) would be some examples of when you might want to use non-pooled dynamically spawned NetworkObjects.

caution

While the NonPooledDynamicSpawner example is one of the simplest ways to spawn a NetworkObject, there is a memory allocation cost associated with instantiating and destroying the GameObject and all attached components. This design pattern can sometimes be all you need for the netcode game asset you are working with, and other times you might want to respawn/re-use the object instance. When performance is a concern and you want to spawn more than just one NetworkObject during the lifetime of the spawner or want to repeatedly respawn a single NetworkObject, the less proccessor and memory allocation intensive technique is to use pooled dynamic spawning.

note

Really, the when we use the term "non-pooled" more often than not we are referring to the concept that a GameObject will be instantiated on both the server and the clients each time an instance is spawned.

Pooled Dynamic Spawning

Pooled dynamic spawning is when netcode objects (GameObject with one NetworkObject component) aren't destroyed on the server or the client when despawned. Instead, specific components are just disabled (or the GameObject itself) when a netcode object is despawned. A pooled dynamically spawned netcode object is typically instantiated during an already memory allocation heavy period of time (like when a scene is loaded or even at the start of your application before even establishing a network connection). Pooled dynamically spawned netcode objects are more commonly thought of as more than one netcode object that can be re-used without incurring the memory allocation and initialization costs. However, you might also run into scenarios where you need just one dynamically spawned netcode object to be treated like a pooled dynmically spawned netcode object.

Fortunately, Netcode for GameObjects provides you with a way to be in control over the instatiation and destruction process for one or many netcode objects by via the INetworkPrefabInstanceHandler interface. Any INetworkPrefabInstanceHandlerimplementation should be registered with the NetworkPrefabHandler(for multiple netcode objects see Object Pooling) to accomplish this.

The easiest way to not destroy a network Prefab instance is to have something, other than the instance itself, keeping a reference to the instance. This way you can simply set the root GameObject to be inactive when it's despawned while still being able to set it active when the same network Prefab type needs to be respawned. Below is one example of how you can accomplish this for a single netcode object instance:

public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstanceHandler
{
public GameObject PrefabToSpawn;
public bool SpawnPrefabAutomatically;

private GameObject m_PrefabInstance;
private NetworkObject m_SpawnedNetworkObject;


private void Start()
{
// Instantiate our instance when we start (for both clients and server)
m_PrefabInstance = Instantiate(PrefabToSpawn);

// Get the NetworkObject component assigned to the Prefab instance
m_SpawnedNetworkObject = m_PrefabInstance.GetComponent<NetworkObject>();

// Set it to be inactive
m_PrefabInstance.SetActive(false);
}

private IEnumerator DespawnTimer()
{
yield return new WaitForSeconds(2);
m_SpawnedNetworkObject.Despawn();
StartCoroutine(SpawnTimer());
yield break;
}

private IEnumerator SpawnTimer()
{
yield return new WaitForSeconds(2);
SpawnInstance();
yield break;
}

/// <summary>
/// Invoked only on clients and not server or host
/// INetworkPrefabInstanceHandler.Instantiate implementation
/// Called when Netcode for GameObjects need an instance to be spawned
/// </summary>
public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation)
{
m_PrefabInstance.SetActive(true);
m_PrefabInstance.transform.position = transform.position;
m_PrefabInstance.transform.rotation = transform.rotation;
return m_SpawnedNetworkObject;
}

/// <summary>
/// Client and Server side
/// INetworkPrefabInstanceHandler.Destroy implementation
/// </summary>
public void Destroy(NetworkObject networkObject)
{
m_PrefabInstance.SetActive(false);
}

public void SpawnInstance()
{
if (!IsServer)
{
return;
}

if (m_PrefabInstance != null && m_SpawnedNetworkObject != null && !m_SpawnedNetworkObject.IsSpawned)
{
m_PrefabInstance.SetActive(true);
m_SpawnedNetworkObject.Spawn();
StartCoroutine(DespawnTimer());
}
}

public override void OnNetworkSpawn()
{
// We register our network Prefab and this NetworkBehaviour that implements the
// INetworkPrefabInstanceHandler interface with the Prefab handler
NetworkManager.PrefabHandler.AddHandler(PrefabToSpawn, this);

if (!IsServer || !SpawnPrefabAutomatically)
{
return;
}

if (SpawnPrefabAutomatically)
{
SpawnInstance();
}
}

public override void OnNetworkDespawn()
{
if (m_SpawnedNetworkObject != null && m_SpawnedNetworkObject.IsSpawned)
{
m_SpawnedNetworkObject.Despawn();
}
base.OnNetworkDespawn();
}

public override void OnDestroy()
{
// This example destroys the
if (m_PrefabInstance != null)
{
// Always deregister the prefab
NetworkManager.Singleton.PrefabHandler.RemoveHandler(PrefabToSpawn);
Destroy(m_PrefabInstance);
}
base.OnDestroy();
}
}

You might run across a situation where you still want other components on the root GameObject of your network Prefab instance to remain active. Primarily, you want to be able to easily disable the components that would normally be active when the netcode object is considered spawned.

Below is an example of what a non-pooled friendly Prefab might look like:

image

The issues you might run into with the above Prefab hierarchy is that everything is on a single GameObject, and as such if you wanted to disable the MeshRenderer and the NetworkObjectLabel, one of our classes in the Netcode for GameObjects test project, you would need to get those component types before disabling them (that is, during Start or OnNetworkSpawn or get them when OnNetworkDespawn is invoked).

To reduce this level of complexity, a more "pooled dynamic spawning" friendly Prefab heirarchy might look like this:

image

The NetworkObject sits at the root GameObject of the network prefab. The child GameObject, SpawnedComponents, then has everything that you might want to have disabled when the network Prefab instance isn't spawned:

image

This reduces the complexity down to setting the SpawnedComponents GameObject to inactive, which will also disable all of the components attached to it.

tip

Using this type of a hierarchical separation is useful in many ways (especially when you have a much more complex prefab). For more complex prefabs, you can further expand this pattern into specific categories (that is, visuals, physics, sound, etc) which will provide you with a more macrocosmic way to control enabling or disabling many different components without having to have references to all of them.

In-Scene Placed NetworkObject

Any objects in the scene with active and spawned NetworkObject components will get automatically replicated by Netcode. There is no need to manually spawn them when scene management is enabled in the NetworkManager. In-scene placed NetworkObjects should typically be used like a "static" netcode object, where the netcode object is typically spawned upon the scene being loaded on the server-side and synchronized with clients once they finish loading the same scene.

Learn more about In-Scene Placed NetworkObjects

Generally, there are two modes that define how an in-scene placed NetworkObject is synchronized.

  • Soft Synchronization (Scene Management enabled)
  • Prefab Synchronization (Scene Management disabled)

Soft Synchronization

SoftSync or "Soft Synchronization" is a term you might run across if you run into any issue with in-scene placed NetworkObjects. Soft synchronization only occurs if scene management is enabled in the NetworkManager properties. If you receive a "soft synchronization error", then this typically means that a client can't locate the same in-scene placed NetworkObject after loading a scene.

note

The benefit of using scene management is that you don't have to register every in-scene placed NetworkObject with the NetworkManager as a network prefab, and it handles synchronizing clients to your current game state.

Prefab Synchronization

PrefabSync or "Prefab Synchronization" is used if scene management is disabled in the NetworkManager. With Prefab synchronization, every in-scene placed NetworkObject has to be a network Prefab and must be registered with NetworkPrefabs list. When a client starts, Netcode will destroy all existing in-scene placed NetworkObjects and spawn its corresponding Prefab from the NetworkPrefabs list instead. This also means that you will have to implement your own scene manager and handle how you synchronize clients when they join a network session.

PrefabSync is ONLY recommended for advanced development and/or multi project setups.