Skip to content

Synced Variables ([Synced])

Mark a public field on a NexusBehaviour with [Synced] and it replicates to every other player — and to anyone who joins later. Whoever has authority writes it; everyone else receives it.

public class Scoreboard : NexusBehaviour
{
    [Synced] public int redScore;

    bool _addPending;

    // Called from a UI button, OnUsed, OnGrabbed, etc.
    public void AddPoint()
    {
        // Ask for ownership up front — the grant is asynchronous.
        if (!Network.IsOwner(gameObject)) Network.RequestOwnership(gameObject);
        _addPending = true;
    }

    void Update()
    {
        // Write only once ownership has actually landed.
        if (_addPending && Network.IsOwner(gameObject))
        {
            redScore += 1;
            Network.RequestSerialization();   // send it
            _addPending = false;
        }
    }
}

Nothing to wire up — [Synced] adds its own networking

Just declaring a [Synced] field is enough. The runtime automatically gives that object the networking it needs — you don't add a network component, list the field anywhere, or attach an interactable, and Network.IsOwner / Network.RequestOwnership work out of the box. Each object keeps its own synced values (a per‑object channel), so two copies of the same script don't share state.

Network.RequestOwnership() is asynchronous — don't serialize on the next line

A [Synced] object is server‑owned at rest, so a client's first write has to wait. On a client, RequestOwnership(gameObject) only sends a request — you don't actually own the object until the server round‑trips a grant a frame or more later. Network.RequestSerialization() silently does nothing when you're not (yet) the owner (no error, no warning), so calling RequestOwnership() and RequestSerialization() back‑to‑back loses that first push. Take ownership early (on grab/use), then gate the write on Network.IsOwner(gameObject) in Update, exactly as above.

Send mode — Manual vs Continuous

[Synced(mode)] chooses when the value is sent:

Mode When it sends Use for
Sync.Manual (default) only when you call Network.RequestSerialization() scores, inventory, flags — reliable, cheap
Sync.Continuous automatically, whenever the value changes positions, gauges, anything smoothly moving
[Synced] public int redScore;                    // manual
[Synced(Sync.Continuous)] public Vector3 puckPos; // auto-sends on change

Smoothing — interpolation

For a Continuous number/vector, choose how the receiver eases to each new value:

Interp Behaviour
Interp.None (default) snap to the received value
Interp.Linear move toward it linearly
Interp.Smooth eased (damped) toward it
[Synced(Sync.Continuous, Interp.Smooth)] public Vector3 puckPos;

Interpolation only applies to float / Vector2 / Vector3 / Vector4 / Quaternion / Color on Continuous fields; discrete or exact values (every integer type, bool, char, string, and double) always snap to the received value.

Who writes it — authority

Authority Who writes Use for
Authority.Owner (default) whoever owns the object (take it with Network.RequestOwnership) per‑object state a player drives
Authority.Server the server only — un‑spoofable anything that must be trustworthy: health, currency, match state
[Synced(Authority.Server)] public int matchState;   // only the server can change it

One authority per script

All the [Synced] fields on a single script must use the same authority — a script is either owner‑authoritative or server‑authoritative. Mixing is a compile error. (Put server‑owned state on its own script/object.)

Choosing between [Synced] and World.*/Player.*

[Synced] is per‑object field state (a pet's mood, a vehicle's gauges, a held prop). The World.* / Player.* session variables are scoped, server‑authoritative shared state (a world score, a player's coins). Use [Synced(Authority.Server)] or World.* for anything that must be un‑spoofable.

React to changes — [SyncedChange]

Mark a method with [SyncedChange(nameof(field))] and the runtime calls it on the receiver right after that field changes:

public class Scoreboard : NexusBehaviour
{
    [Synced] public int redScore;

    [SyncedChange(nameof(redScore))]
    public void OnRedScore() { UpdateScoreUI(redScore); }
}

When [SyncedChange] fires:

  • Once per changed field — a handler runs only for the fields whose value actually changed, not once per RequestSerialization and never on a same‑value resend.
  • In declaration order — when several fields change in the same update they apply top‑to‑bottom in the order you declared them, and each field's handler runs right after that field is applied. A handler on a later‑declared field therefore sees the earlier fields already updated.
  • On the receivers only, never on the writer — the player who wrote the value doesn't get its own callback, so react to your own change directly in the code that made it rather than relying on the handler.
  • Once on join — a late joiner runs each [SyncedChange] handler once for every field whose snapshot value differs from its declared default, right after it receives the current state. A field still at its default value doesn't fire.

Reference

Call Does
Network.RequestSerialization() Send the current [Synced] values now (Manual mode).
Network.IsOwner(gameObject) Are you the current owner (allowed to write owner‑auth fields)?
Network.RequestOwnership(gameObject) Take ownership so you can write.

Supported field types: bool; the integer types byte, sbyte, short, ushort, int, uint, long, ulong; float and double; char; string; and Vector2, Vector3, Vector4, Quaternion, Color.

Anything else cannot be synced — arrays, List, Dictionary, Color32, Rect, decimal, object references, your own types. Mark one [Synced] anyway and you get told twice: the Networking Budget panel flags it in the inspector, and at runtime the field is reported by name and simply stays local to each machine rather than replicating.

[NexusVM] 'Scoreboard': field 'entries' is marked [Synced] but List<int> can't be
replicated, so it will NOT sync (it stays local to each machine).

To share a collection, use an RPC

[Synced] carries one fixed-size value per field. A whole list or table goes by RPC instead — arrays, List<T> and Dictionary<K,V> are all supported there. A common pattern is a [Synced] int version that changes whenever the collection does, with the contents sent by RPC.

There's a per‑object size cap on the combined synced payload; if you hit it, use fewer / smaller [Synced] fields (a string counts for its length).

See it before you ship — the inspector's Networking Budget

Select the object and look at the Networking Budget on its script component. It lists every synced field with its send mode and authority, estimates the payload against the per‑object cap, and flags an un‑syncable type or mixed authority before you compile — so you catch the mistake in the editor, not in a live session.

→ See Networking for World.* / Player.* session variables and RPCs.