Multiplayer experiences are the heart of VRChat, so creating a world that reacts to players and synchronizes the data between them is key.
There are three ways you can synchronize data and events in your world:
Use this when you have a value that you want to synchronize across all players in your world. You will update the value on one player who 'owns' the object with the variable, and listen for the updates on all other players. This will sync for late joiners.
Example: The 'score' of each team in a basketball game. This only changes when someone makes a basket and you definitely don't want to miss an update.. See Using Variables below.
Use this to trigger an event for every player currently in the instance, or for the owner of an object. It is guaranteed to arrive, but will have a fair amount of delay and overhead. It will not be received by anyone who joins after the event was sent. See Using Events below.
Example: A laser effect that fires as part of your Dance Club. You want everyone in the club to see it around the same time, but if someone comes in 20 minutes later, it's ok that they missed it.
Some VRChat-specific objects are automatically synced. This includes:
- Avatars: Includes their colliders, voice, and IK movement.
- UdonBehaviours with 'Synchronize Position' checked: Includes the Transform and Rigidbody of the object if applicable.
In VRChat, every GameObject is 'owned' by one player (VRCPlayerApi) at a time. Only the Owner of an object can change its values. Those changes are then sent to everyone else in the instance.
These are the steps to change ownership of an object:
- Someone calls Networking.SetOwner(VRCPlayerApi player, GameObject obj) (this can be the current owner or anyone else)
- Once ownership is transferred, the event OnOwnershipTransferred(VRCPlayerApi player) is called on the GameObject for everyone in the instance. Note that it may take a few frames for this update to propagate.
- To check whether the ownership transfer has happened, you can check Networking.IsOwner(GameObject) or wait until the target player receives the OnOwnershipTransferred event.
If you want a player to be able to change a value on an object, make sure to check or request ownership first.
Using a variable to sync data takes three steps:
- Create the variable.
- Update the value on the Owner.
- React to changes in value received from the Owner.
- Click the + button in the Variables window
- Choose your variable type
- Rename your variable (optional, but do it)
- Click the arrow next to the variable name to show more options, turn on 'synced'. (The default value of 'none' is fine - this just means the value is not automatically smoothed out)
- Drag and drop the variable onto your graph while holding 'Ctrl' to make a 'Set Variable' node.
- Connect an event or flow to the Flow Port on this node, and connect a new value to the Value Port.
- Add an "OnDeserialization" node to this same graph.
- Drag and Drop the variable onto your graph without holding Ctrl to create a 'Get Variable' node.
- Use the flow coming from the OnDeserialization node and the value from the Get Variable node to update another node with this new value.
You can sync variables of the following types:
bool, byte, char, sbyte, short, ushort, int, uint, long, ulong, float, double, string, Vector2, Vector3, Vector4, VRCUrl, Quaternion, Color and Color32.
Using an Event to fire a change takes 2 steps:
- Add a Custom Event node
- Use a SendCustomNetworkEvent node to trigger this event on your target(s).
- Create an "Event Custom" node.
- Give this node a unique name using its input box
- Add a "Send Custom Network Event Node"
- Enter this same event name in the 'eventName' input.
- Leave the default 'All' as the target to trigger this event on each Player in your room, or change it to 'Owner' to only fire this event on the Owner.
- You can leave the 'instance' input empty to target the current UdonBehaviour, or connect a reference to another UdonBehaviour to fire a Custom Event on that one instead.
By checking this box on an UdonBehaviour, VRChat will automatically sync the Transform (position, rotation scale) and Rigidbody (physics) of the object you put it on.
The Instantiate node can be used to create additional local (non-synced) objects. Set its 'original' input to a GameObject in your scene, and it will return a copy of that object added to your hierarchy.
If you want to create and manage new objects, we recommend you use an Object Pool.
Our upcoming Networking Upgrade includes a built-in Object Pool component to help you manage a set of objects. In the meantime, you can create one yourself if you want the effect of synced instantiation. Here's the general way an Object Pool works, using an example where you want every player in your world to be able to grab a random hat out of a box.
- Create an UdonBehaviour with a variable array of Hat objects (the Hat Pool).
- Create one hundred hats as children of the Hat Pool, and assign them to the array.
- When someone wants a hat, find the first inactive one, set it active, and assign it to the player.
- When the next player wants a hat, find the next inactive one, set it active, and assign it to that player.
- When someone no longer wants a hat, mark it inactive and assign it back to the Owner of the Hat Pool.
It's easy to send an Network Event to everyone in your world, using SendCustomNetworkEvent with 'All' selected. But someone who joins your world after this event has already been sent out will not receive it, as they are a 'Late Joiner' for that event. The term is simply a way to refer to a player who missed something. As the creator and designer of the world, you want to make sure the world functions properly even for people who joined after certain events happen. It is helpful if you only use SendCustomNetworkEvent for temporary effects, and use Synced Variables for anything that should be 'remembered' by the world and synchronized for people who joined after they changed.
This event triggers just before serialized data will be sent out, it's a good place to set synced variables that you want to be updated for other players.
This event triggers when sync data has been transformed from bytes back into usable variables. It does not tell you which data has been updated, but serves as a jumping-off point to either update everything that watches synced variables, or a place to check new data against old data and make specific updates.
You Need Two Players!
You need to have two or more Players in a world to trigger these events, and at least one Synced Variable on the UdonBehaviour where you want them to fire. The easiest way to do this is in the VRChat SDK Window - next to Local Testing, set Number of Clients to 2 and then press Build and Test to launch two clients with the same account.
Try not to sync everything; consider what you can determine from the minimal amount of information shared. For example: if an object will move on a fixed or predictable path, then its position may not need to be synchronized and instead its initial location and velocity may be sufficient.
If there exists complicated game logic then try to have it on one object; having important logic state shared across many objects may cause de-synchronization due to ownership differences.
Variable sync is limited to roughly 200 bytes per serialization.
A single synced string can have roughly 126 characters.
You can send out about 11kb per second.
Updated about 1 year ago