Overview
The greatest appeal of VRChat is that multiple players can share the same experience in the same space. When one player presses a button, a door opens in everyone else's view too. This is possible because of a mechanism called network synchronization.
Network synchronization in UdonSharp may seem complex at first glance, but once you understand a few basic concepts, the mechanism becomes very clear. This article covers the following three core elements essential for creating multiplayer-compatible gimmicks:
- Ownership: Who has the right to manipulate that object.
- Synced Variables (
[UdonSynced]): Variables shared between players. - Serialization: The process by which data is actually synchronized over the network.
1. Ownership - "Whose Object Is This?"
Every GameObject in a VRChat world always has exactly one Owner. The owner is the player who has the authority to freely change that object's state and notify other players of those changes.
- Default Owner: The owner of objects placed in the world from the start is the person who created the world instance (Master).
- Ownership Rule: Only the object's owner can change synced variable values. Even if a non-owner player tries to change a value, that change remains local (only to themselves) and won't be transmitted to other players.
This rule that "only the owner can make changes" is the fundamental mechanism that prevents state confusion when multiple players try to manipulate the same object simultaneously.
How to Acquire Ownership
When creating a gimmick where a player manipulates an object, that player first needs to become the object's owner. To acquire ownership, use the Networking.SetOwner() method.
Networking.SetOwner(VRCPlayerApi player, GameObject obj);
player: The player who will become the new owner. Usually yourself, i.e.,Networking.LocalPlayer.obj: The GameObject you want to acquire ownership of. Usually the object the script is attached to, i.e.,gameObject.
This method is typically called when a player takes some action on an object (e.g., Interact).
public override void Interact()
{
// Set ownership of this object to myself (local player)
Networking.SetOwner(Networking.LocalPlayer, gameObject);
// ... Write processing to change synced variables after this ...
}
2. Synced Variables ([UdonSynced]) - Data Shared by Everyone
For variables you want to share between players, add the [UdonSynced] attribute immediately before the declaration. This tells Udon that the variable is "subject to network synchronization."
using UdonSharp;
using UnityEngine;
public class SyncedVariableExample : UdonSharpBehaviour
{
// This bool value is synchronized among all players in the instance
[UdonSynced]
private bool isLightOn = false;
}
There are restrictions on variable types that can have [UdonSynced], but many basic data types are supported, including int, float, bool, string, Vector3, Quaternion, and Color.
3. Serialization - The Data Sync Process
Variables with the [UdonSynced] attribute are synchronized to all players through the following process. This entire flow is called Serialization.
- Acquire Ownership: Player A manipulates the object and becomes the owner with
Networking.SetOwner(). - Change Synced Variable: Player A (owner) changes the
[UdonSynced]variable value. (e.g.,isLightOn = true;) - Request Serialization: Player A calls
RequestSerialization(). This is a request saying "The variable value has changed, so please send this data to everyone else." - Send Data: VRChat's server sends the new variable data (serialized data) from Player A to all other players in the instance (Players B, C, ...).
- Deserialization: The Udon VM of other players (B, C, ...) who received the data automatically calls the
OnDeserialization()event. At the point this event occurs, their[UdonSynced]variable values have been updated to the new values set by Player A. - Reflect State: In the
OnDeserialization()method, write processing to update the world state (e.g., turn on the light) based on the updated variable values.
Implementation Example: Synced Switch
Let's look at this process with a synced switch that toggles a light ON/OFF when clicked.
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
public class SyncedSwitch : UdonSharpBehaviour
{
public GameObject targetLight;
[UdonSynced]
private bool isLightOn = false;
void Start()
{
// Reflect initial state
UpdateLightState();
}
public override void Interact()
{
// 1. Acquire ownership
Networking.SetOwner(Networking.LocalPlayer, gameObject);
// 2. Change synced variable
isLightOn = !isLightOn;
// 3. Request to notify all players of the change
RequestSerialization();
// Immediately update own appearance as well
UpdateLightState();
}
// 5. Called when data is updated on other players
public override void OnDeserialization()
{
// 6. Reflect updated state
UpdateLightState();
}
private void UpdateLightState()
{
if (targetLight != null)
{
targetLight.SetActive(isLightOn);
}
}
}
In this code, the player who Interacts changes the value and notifies with RequestSerialization(). Other players receive the notification in OnDeserialization and call UpdateLightState(), ensuring everyone's light state matches.
Summary
- The foundation of network synchronization is Ownership. Only the owner can change synced variables.
- When a player manipulates an object, first acquire ownership with
Networking.SetOwner(). - Add the
[UdonSynced]attribute before variables you want to share between players. - When the owner changes a synced variable's value, call
RequestSerialization()to notify of the change. - Other players receive the change in the
OnDeserialization()event and update the world state.
This flow of "Acquire Ownership → Change Value → Notify → Receive and Update" is the most fundamental pattern for network synchronization in UdonSharp. Understanding this pattern is the first step to creating multiplayer-compatible gimmicks.
The next article will cover "Methods and Custom Events" for inter-script communication.