Skip to main content

๐Ÿ‘ฅ Lesson 6.2: NetworkObjects, Spawning & Ownership

A session with nothing in it is just a handshake. This lesson populates it: you'll make GameObjects network-aware with NetworkObject, auto-spawn a player for each connection, spawn objects from the server, and โ€” the concept that trips everyone up โ€” sort out ownership: who controls what, and who's allowed to.

๐ŸŽฏ Learning Objectives

By the end of this lesson, you will be able to:

  • Mark a GameObject as networked with NetworkObject and register network prefabs
  • Auto-spawn a player object per connected client
  • Spawn objects at runtime from the server with .Spawn()
  • Write a NetworkBehaviour and use OnNetworkSpawn
  • Reason about ownership โ€” IsOwner, OwnerClientId, server-owned objects, and ChangeOwnership

Estimated Time: 60 minutes  ยท  Prerequisite: Lesson 6.1 (topologies & authority)

In This Lesson

The NetworkObject

An ordinary GameObject exists on one machine. To make it exist across the network โ€” spawned on the server and replicated to every client โ€” it needs a NetworkObject component. This is the unit of network identity: it gets a unique NetworkObjectId, and everything networked (behaviours, variables, RPCs) lives on a GameObject that has one.

Any prefab you intend to spawn over the network must have a NetworkObject and be registered in the NetworkManager's network-prefabs list (or a Default Network Prefabs List). Registration is how a client, receiving "spawn object of type X", knows which prefab to instantiate. Forget to register a prefab and spawning it throws โ€” the second-most-common beginner error after forgetting authority.

๐Ÿ“– Definition

Replication: when the server spawns a NetworkObject, Netcode automatically creates a matching copy on every connected client and keeps them associated by NetworkObjectId. Despawn it on the server and it's removed everywhere. Clients never spawn networked objects themselves.

The Player Prefab

The most common spawn is "one avatar per player." Netcode automates it: assign a Player Prefab on the NetworkManager, and when a client connects, the server automatically spawns that prefab and gives ownership to the connecting client. Two players connect, two player objects appear on every machine, each owned by its respective player.

Server-authoritative spawning and replication The server spawns two player objects, P1 owned by client 1 and P2 owned by client 2. Each client's view shows both player objects replicated, but only the locally-owned one is highlighted as controllable. Server (authority) P1 (own: C1) P2 (own: C2) spawn โ†’ replicate to all clients Client 1's view P1 โ˜… mine P2 (remote) Client 2's view P1 (remote) P2 โ˜… mine
Figure 1: The server spawns both players and replicates them everywhere. Each client sees both avatars, but only the one it owns (โ˜…) is the one it controls; the other is a remote representation.

NetworkBehaviour & OnNetworkSpawn

Scripts on a networked object inherit from NetworkBehaviour instead of MonoBehaviour. That gives them the network context โ€” IsServer, IsClient, IsOwner, OwnerClientId โ€” and the crucial lifecycle hook OnNetworkSpawn(), which fires when the object becomes network-ready (this is where you set things up, not Awake/Start, because the network identity isn't ready that early):

using Unity.Netcode;
using UnityEngine;

public class PlayerController : NetworkBehaviour
{
    [SerializeField] Camera playerCamera;

    public override void OnNetworkSpawn()
    {
        // Runs on every machine that has this object. Branch by role.
        if (IsOwner)
        {
            // Only the player who owns this avatar drives it.
            playerCamera.enabled = true;
            enabled = true;               // enable local input handling
        }
        else
        {
            // Remote copies: no local camera or input.
            playerCamera.enabled = false;
        }
    }

    void Update()
    {
        if (!IsOwner) return;             // never move someone else's avatar
        // ... read input, request movement (Lesson 6.4) ...
    }
}

The pattern is everywhere in netcode: guard behaviour by role. if (!IsOwner) return; at the top of an input method ensures each machine only drives the avatar it owns; the remote copies just display what the network tells them.

Spawning from the Server

Beyond players, you'll spawn projectiles, pickups, enemies. The rule from Lesson 6.1 holds: only the server spawns. You instantiate the prefab and call .Spawn() on its NetworkObject โ€” Netcode replicates it to all clients:

// Runs on the SERVER only (e.g. inside a server-side method or an RPC target).
public void SpawnPickup(Vector3 position)
{
    if (!IsServer) return;                       // guard: server authority

    GameObject go = Instantiate(pickupPrefab, position, Quaternion.identity);
    NetworkObject netObj = go.GetComponent<NetworkObject>();
    netObj.Spawn();                              // replicate to every client
}

To spawn something a specific player should control, use netObj.SpawnWithOwnership(clientId) instead. To remove it, the server calls netObj.Despawn() (optionally destroying it). A client that tries to call Spawn() gets an error โ€” spawning is a server privilege.

โš ๏ธ Don't Instantiate networked prefabs on clients

A client that instantiates a networked prefab locally creates a "ghost" that isn't part of the session โ€” it won't replicate, won't sync, and desyncs the game. If a client needs something to appear, it asks the server (an RPC, Lesson 6.4), and the server spawns it for everyone.

Ownership

Ownership is which client (or the server) a NetworkObject "belongs" to. It matters because ownership decides who's allowed to drive certain things โ€” an owner can be given write permission on a NetworkVariable, and RPCs can target the owner. Key facts:

  • By default, spawned objects are owned by the server (owner client id 0 for the host). Player prefabs are the exception โ€” owned by their player.
  • IsOwner is true on the machine that owns the object; OwnerClientId is that client's id.
  • The server can transfer ownership with netObj.ChangeOwnership(clientId) (e.g. a player picks up a movable crate and should now drive it).
  • Ownership is not a security guarantee by itself โ€” even the owner's inputs should be validated by the server for anything competitive. Authority still lives on the server.

A useful way to hold it: ownership answers "who drives this?", authority answers "who decides the truth?" Often the server keeps authority even over objects a client owns โ€” the client requests, the server confirms.

Branching on Role

Because one build runs as server, host, or client, your networked code constantly asks "which am I?" The properties on NetworkBehaviour:

PropertyTrue whenโ€ฆTypical use
IsServerthis instance is the server (incl. host)authoritative logic, spawning, validation
IsClientthis instance is a client (incl. host)visuals, UI, local prediction
IsHostserver and client in onerare special-casing
IsOwnerthis machine owns this objectread input, drive the avatar

Most bugs in early netcode come from missing one of these guards โ€” moving a non-owned avatar, running server logic on a client, or spawning off the server. Reach for the right check first and the logic follows.

Hands-on Challenge

๐Ÿ‹๏ธ Exercise 1: Two players in a scene

Objective: See replication and ownership for real.

  1. Make a Player prefab: a capsule with a NetworkObject and the PlayerController above; assign it as the NetworkManager's Player Prefab.
  2. Add simple owner-only movement in Update (guarded by if (!IsOwner) return;) that moves the transform with WASD.
  3. Build; run one as Host and one as Client. Move each โ€” confirm each window only controls its own capsule, while both capsules appear in both windows.
๐Ÿ’ก Hint

Because there's no NetworkVariable or RPC yet, the moved position may not sync to the other machine โ€” that's the next two lessons. For now, the win is seeing two owned player objects replicate and each obey only its owner's input.

๐Ÿ‹๏ธ Exercise 2: Who owns it?

State the default owner of: (a) a player avatar spawned when client 5 connects; (b) an enemy spawned by the server with netObj.Spawn(); (c) a crate after the server calls netObj.ChangeOwnership(3).

โœ… Answers

(a) Client 5 โ€” player prefabs are owned by the connecting client. (b) The server โ€” plain Spawn() is server-owned by default. (c) Client 3 โ€” ownership was transferred. In all cases the server still holds authority over the game rules.

๐ŸŽฏ Quick Quiz

Question 1: What must a prefab have to be spawned over the network?

Question 2: Where should you initialise a networked script โ€” and why?

Question 3: A client calls Instantiate on a networked enemy prefab locally. What happens?

Summary

๐ŸŽ‰ Key Takeaways

  • A NetworkObject makes a GameObject network-identifiable and replicable; spawnable prefabs must be registered on the NetworkManager.
  • Assign a Player Prefab and Netcode auto-spawns one per client, owned by that client.
  • Networked scripts extend NetworkBehaviour and initialise in OnNetworkSpawn, branching on role.
  • Only the server spawns: Instantiate + NetworkObject.Spawn() (or SpawnWithOwnership); clients ask the server instead.
  • Ownership (IsOwner/OwnerClientId, ChangeOwnership) says "who drives it"; authority still says "who decides the truth" โ€” usually the server.
  • Guard everything by role โ€” if (!IsOwner) return;, if (!IsServer) return; โ€” to avoid the classic netcode bugs.

๐Ÿš€ What's Next?

Your player objects exist on every machine but don't yet agree on their state. In Lesson 6.3: NetworkVariables & State Synchronization we make the server's values automatically replicate to clients โ€” health bars, scores, positions that stay in sync.

๐Ÿ‘ฅ The session has players

NetworkObjects replicate, players auto-spawn, ownership decides control. With the objects in place, the next step is keeping their state agreed across the network.