Networked Objects ======================================= Networked objects (alongside RPCs) make up the bulk of what the networking library supports. Most games are built from a combination of **networked objects** for continuous synchronization (like positions and states) and **RPCs** for discrete events (like opening a door or firing a weapon). In general, a networked object is a GameObject that exists on **all clients** connected to the server. When the object’s state changes on its owning machine, that information is sent through the server and broadcast to the other clients. This ensures all connected players see the same simulation. A simple example is a player’s character: - When a character is instantiated on one client, it is also created on all other clients. - As the player moves, their character’s transform is synced across the network. - All other players see the character move smoothly in real time. Instantiating ------------- Instantiating a network object is similar to Unity’s built-in ``Instantiate()``, but you must use ``ClientNetwork.Instantiate``: .. code-block:: csharp // Example: Instantiate a networked player object GameObject player = clientNetwork.Instantiate( "PlayerPrefab", // prefab name (must be in Resources/) spawnPosition, Quaternion.identity ); Key differences from Unity’s ``Instantiate``: - The first parameter is a **string name** of the prefab. - The prefab must be located in a **Resources/** folder (required for dynamic loading by name). - The networking system assigns a **unique network ID** for this object and registers it with the server and other clients. .. note:: Unity discourages using ``Resources`` for modern projects. This system still works, but you may want to adapt it later to use **Addressables** or **Asset Bundles** for dynamic loading. **Server-instantiated objects** The server can also create objects. These objects are owned by the server by default and synchronized to clients. For more details, see the :doc:`Ownership` page. Unique IDs and ID Pools ^^^^^^^^^^^^^^^^^^^^^^^ Every networked object has a unique **network ID**. To make this possible: - The **server issues each client a pool of IDs** (default size: 500). - When a client instantiates an object, it picks the next free ID from its pool. - The server keeps track of ID pools and replenishes them as needed. This ensures **immediate local instantiation** (so players see their objects instantly) while still guaranteeing global uniqueness. Destroying ---------- Destroying a networked object must go through the networking layer, not Unity’s ``Destroy``. .. code-block:: csharp // Client destroys their own object clientNetwork.Destroy(objectId); This sends a ``Destroy`` message to the server, which in turn informs all other clients to remove the object. If a **client tries to destroy an object they don’t own**: - The server checks ownership. - If the client is not the owner, the server may ignore the request or send an ``OwnershipLost`` message back to the client. This ensures only the authoritative machine can remove objects. NetworkSync Component --------------------- All networked objects must include a **NetworkSync** component. This script provides: - **Unique ID management** – each object is tied to a global network ID. - **Synchronization** – sends position, rotation, and custom data to the server. - **RPC routing** – allows RPCs to target this object specifically. - **Callbacks** – methods invoked when the object is created, initialized, or destroyed. Typical setup: .. code-block:: csharp public class PlayerSync : MonoBehaviour { NetworkSync sync; void Start() { sync = GetComponent(); } void NetworkInitialized() { Debug.Log("NetworkSync ready with ID: " + sync.GetId()); } void OnGainOwnership() { Debug.Log("Now controlling this object."); } void OnLoseOwnership() { Debug.Log("No longer controlling this object."); } } LiteSync ^^^^^^^^ Sometimes you want to sync **custom data** without transform updates. For example, ammo count or animation state. - ``SyncUpdate`` – sends full transform + data. - ``LiteSyncUpdate`` – sends *only* the custom data payload. This reduces bandwidth when positions/rotations are unchanged. Extending Sync Data ^^^^^^^^^^^^^^^^^^^ ``NetworkSync`` lets you send extra byte data during sync messages: .. code-block:: csharp byte[] extraData = BitConverter.GetBytes(currentHealth); sync.SendSyncData(extraData); On the receiving end: .. code-block:: csharp void ReceiveSyncData(byte[] data, Vector3 pos, Quaternion rot) { int health = BitConverter.ToInt32(data, 0); UpdateHealth(health); } Remote Procedure Calls on Objects --------------------------------- RPCs can be targeted at **specific networked objects**. This is useful when the event only relates to that object. Example: making a chest open: .. code-block:: csharp // Call RPC on a specific chest chestSync.CallRPC("OpenChest", MessageReceiver.AllClients); On each client, the chest GameObject must implement: .. code-block:: csharp public void OpenChest() { // Play animation, enable loot, etc. } Smooth Movement --------------- Synchronization messages are sent periodically, not every frame. If you apply updates immediately, objects may appear to "teleport" or jitter due to latency. **Interpolation** and **extrapolation** smooth out movement: - **Interpolation**: Render objects *between* the last two known positions. - **Extrapolation**: Predict the next position based on velocity if new data hasn’t arrived yet. Example interpolation approach: .. code-block:: csharp void Update() { float t = (Time.time - lastUpdateTime) / updateInterval; transform.position = Vector3.Lerp(oldPos, newPos, t); transform.rotation = Quaternion.Slerp(oldRot, newRot, t); } Considerations: - Fast-paced games may need higher update rates. - Physics objects should be driven by the owner, with remote clients only interpolating replicas. - Keep bandwidth in mind: syncing fewer objects at lower rates saves performance. Summary ------- - Networked objects are created with ``ClientNetwork.Instantiate``. - Each object has a **unique ID** assigned from the server. - ``NetworkSync`` is required for tracking, syncing, and RPC routing. - Objects are destroyed through the networking system, not Unity’s ``Destroy``. - ``LiteSync`` messages allow efficient custom data updates. - Smooth movement requires interpolation/extrapolation to hide latency. - Use RPCs for discrete events; use network sync for continuous state.