Networking

Synced fields, custom events, typed network events, and the NetworkTransform component.

Updated 2026-09-03

Udonite uses VRChat's own networking underneath. The same rules apply as with any Udon behaviour: only the owner of a GameObject writes its synced state, and only a public method can be called across the network.

Synced fields

Mark a field with [UdonSynced] and set the behaviour's sync mode:

using Udonite.Package.Runtime;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon.Common;

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class Counter : UdoniteBehaviour
{
    [UdonSynced] public int count;

    public override void Interact()
    {
        if (!Networking.IsOwner(gameObject))
            Networking.SetOwner(Networking.LocalPlayer, gameObject);

        count = count + 1;
        this.RequestSerialization();
    }

    public override void OnDeserialization(DeserializationResult result)
    {
        Debug.Log("count is now " + count);
    }
}
  • BehaviourSyncMode.Manual sends when you call this.RequestSerialization(). Continuous sends on its own. None syncs nothing.
  • [UdonSynced(UdonSyncMode.Linear)] and Smooth interpolate a numeric field on the receiving side.
  • OnPreSerialization, OnPostSerialization and OnDeserialization fire around each transfer.

Reacting to a change

[FieldChangeCallback] names a property whose setter runs whenever the field's synced value arrives:

[UdonSynced, FieldChangeCallback(nameof(Lit))]
private bool lit;

public bool Lit
{
    get => lit;
    set
    {
        lit = value;
        lamp.SetActive(value);
    }
}

The property's type must match the field's exactly.

Custom events

Any public method can be sent to other clients by name:

this.SendCustomNetworkEvent(NetworkEventTarget.All, nameof(Ring));

public void Ring()
{
    bell.Play();
}

NetworkEventTarget.Owner sends only to the object's owner. SendCustomEventDelayedSeconds and SendCustomEventDelayedFrames schedule a local call later.

Typed network events

For a payload with structure, declare an event class with a Serialize/Deserialize pair and a handler on the receiving behaviour:

using Udonite.Package.Runtime;
using UnityEngine;

public class ScoreEvent : NetworkEvent
{
    public int player;
    public int score;

    public void Serialize(ByteWriter writer)
    {
        writer.WriteInt32(player);
        writer.WriteInt32(score);
    }

    public void Deserialize(ByteReader reader)
    {
        player = reader.ReadInt32();
        score = reader.ReadInt32();
    }
}

public class Scoreboard : UdoniteBehaviour
{
    public void Award(int player, int score)
    {
        ScoreEvent evt = new ScoreEvent();
        evt.player = player;
        evt.score = score;
        Udonite.Network.Send(evt, this);
    }

    [NetworkEventHandler]
    public void OnScore(ScoreEvent evt)
    {
        Debug.Log("player " + evt.player + " scored " + evt.score);
    }
}

Udonite.Network.Send(evt, receiver) serializes the event and delivers it to the [NetworkEventHandler] method on receiver that takes that event type, on every client. Write it fully qualified: with using UnityEngine; in scope, a bare Network is ambiguous with Unity's obsolete UnityEngine.Network. It is carried by synced fields, so it reaches other clients only when called by the owner of the receiver's GameObject.

NetworkTransform

NetworkTransform is a ready-made behaviour that syncs position, rotation and scale with interpolation and teleport detection. Add it to the object, compile, and move the object; with a VRC_Pickup on the same object, picking it up takes ownership and dropping it sends the resting pose. Send rate, per-channel thresholds and the teleport distance are inspector fields.

Ownership

OnOwnershipRequest can be overridden to return false and refuse a transfer. OnOwnershipTransferred fires on every client when the owner changes. ClientSim does not fire ownership requests; test those in an uploaded world.

Something went wrong. Reload