๐ฅ 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
NetworkObjectand register network prefabs - Auto-spawn a player object per connected client
- Spawn objects at runtime from the server with
.Spawn() - Write a
NetworkBehaviourand useOnNetworkSpawn - Reason about ownership โ
IsOwner,OwnerClientId, server-owned objects, andChangeOwnership
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.
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.
IsOwneris true on the machine that owns the object;OwnerClientIdis 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:
| Property | True whenโฆ | Typical use |
|---|---|---|
IsServer | this instance is the server (incl. host) | authoritative logic, spawning, validation |
IsClient | this instance is a client (incl. host) | visuals, UI, local prediction |
IsHost | server and client in one | rare special-casing |
IsOwner | this machine owns this object | read 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.
- Make a Player prefab: a capsule with a
NetworkObjectand thePlayerControllerabove; assign it as the NetworkManager's Player Prefab. - Add simple owner-only movement in
Update(guarded byif (!IsOwner) return;) that moves the transform with WASD. - 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
NetworkObjectmakes 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
NetworkBehaviourand initialise inOnNetworkSpawn, branching on role. - Only the server spawns:
Instantiate+NetworkObject.Spawn()(orSpawnWithOwnership); 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.