Synced Object
The Synced Object component lets you mark a scene object or prefab as replicated. The object's transform, lifetime, and custom variables will be synced across all clients in a server instance.
Warning
This component has been deprecated. Please use Network Objects instead. Synced Objects will continue to work and migrating is not strictly necessary, but we recommend using Network Objects for new projects. Check out the Migration Guide for more information.
How To Use
Synced Objects can either be embedded in a scene or be instantiated as a prefab using visual scripting. The core behavior of the component acts the same between the two, but the following properties have slightly unique behavior depending on how the object is created.
Embedded Space Objects
Creating an embedded synced object is as simple as just adding a Synced Object component to any of your scene objects. From the hierarchy view, you can easily see which objects are synced by looking for the sync icon on the side.
Prefab Objects
Creating a synced prefab is just as easy but requires a few extra steps. First, create a prefab that has a Synced Object component on the root object like so:
This prefab can live anywhere in your project folder. To instantiate this prefab, you can simply use the GameObject.Instantiate
node like normal. Spatial will make sure the object gets instantiated on all other connected clients and syncs properly.
Network Variables
You can optionally enable network variables on your synced object. This allows you to keep Visual Scripting variables synchronized on the same object.
Network Variables follow the same ownership rules as the transform.
Currently the following variable types are supported:
- Primitives:
bool
,byte
,int
,float
,double
,long
,string
- Unity types:
Vector2
,Vector3
,Color32
Note
Network Variables were previously called Synced Variables.
Properties
See the API reference for a full list of properties.
Save with Space
If checked, the value of this variable will be saved with the space. This means that when a user leaves and rejoins the space, the value of this variable will be the same as when they left.
If unchecked, the behavior of the object is different depending on how it was created:
- Embedded scene objects: When a new session starts (such as switching servers), embedded scene objects will always be instantiated, even if they where deleted in a previous session.
- Instantiated prefab: When a session ends all instantiated prefabs will be deleted. In other words, the synced objects are maintained by the server instance, and teleporting to another server instance will not carry over the instantiated prefabs.
Note
Currently, only the "host" server instance (the first server that spawns) will save variables. Non-host server instances will not save variables; their values will be lost.
Master Client Object
If checked, this object will always be owned by the master client. If a new actor is designated as the master client, the ownership of this object will be transferred to the new master client.
Destroy on Creator Disconnect
If checked, the object will be destroyed its creator actor disconnects.
This property is not available for synced objects embedded in the scene.
Destroy on Owner Disconnect
If checked, the object will be destroyed if the current owner of the object disconnects.
This property is not available for synced objects embedded in the scene.
Limitations
- Only the local transform is synced: If you re-parent a synced object on one client, that change will not propagate to the other. The synced object will still behave normally, but it's position might become de-synced since it will be relative to its parent.
- A synced object can not be a child of another synced object.
Scripting with Synced Objects
When working with synced objects, it's important to keep in mind the rules of object ownership.
Here's an example of an object that follows its owner and can be picked up by other users, which helps explain the concept of object ownership.
Carry-able Object Example
I want to make an object that follows a user around, but another user can "grab" it, in which it will follow that user instead.
- Create a Synced Object
- To get started we need to add a Synced Object component as well as a script machine to our object. The object in question is a scene object, so we don't need to worry about instantiating it.
- Make the object follow its owner
- Inside our script, we first want to write the code that updates the object's position. Remember that only the client that owns the object should be modifying the transform state.
- To accomplish this we use the
If Owned Locally
synced object node. This lets us branch off thetrue
port, causing the subsequent code to only be run when the local client owns the object. - The result of this will be an object that follows its owner.
- Allow other users to take ownership
- Now we need to add the ability to claim ownership of the object. I accomplished this through adding a Spatial Interactable as a child to the object.
- Then in VS we can use an
On Interact
event and trigger theTakeover Ownership
synced object node. This means when a client uses the Interactable, they will become the owner of the object.
- Hide the Interactable when not owned
- Now we can actually improve this a little further. By using the
On Owner Changed
synced object event, we can enable/disable the Interactable to only be visible when we don’t own it.
- Now we can actually improve this a little further. By using the
Migration Guide
This guide will help you migrate from Synced Objects to Network Objects.
Note
Network Objects do not currently support "Save with Space". If you need this feature, you should continue using Synced Objects.
Component Migration
Converting the component is made easy with the Convert to Spatial Network Object
button. This will take all of the properties from the Synced Object and apply them to the Network Object, then remove the Synced Object component. Note that this button is only available if you do not have the "save with space" option set.
This operation is undoable, but it's always a good idea to make a backup or use version control for your projects when making large complex changes.
You will need to manually fix any references you may have in the scene or prefab to this component if it was referenced through custom C# code.
Visual Scripting Migration
If you have visual scripting graphs logic, you will need to replace the Synced Object nodes with the equivalent Network Object nodes.
Synced Object Node | Network Object Node |
---|---|
Game Object Instantiate | (Coroutine) Network Object: Spawn |
Synced Object: Takeover Ownership | Network Object: Request Ownership |
Network Object: Release Ownership | |
Synced Object: If Owned Locally | Network Object: If Has Control |
Synced Object: Get By ID | Network Object: Find Object |
Synced Object: Get Is Synced | Network Object: Get Is Spawned |
Synced Object: Get ID | Network Object: Get Object ID |
Network Object: Get Is Mine | |
Synced Object: Get Owner | Network Object: Get Owner Actor Number |
Synced Object: Get Has Control | Network Object: Get Has Control |
Synced Object: On Owner Changed | Network Object: On Owner Changed |
Synced Object: On Object Initialized | Network Object: On Spawned |
Network Object: On Despawned |
To replace a node, you can right click the node and click Replace...
button.
Since Visual Scripting Graphs are serialized as JSON
you can search your project for any occurrences of these nodes using a text editor like Visual Studio Code, which will help you find all the places you need to update. The nodes are referenced by their C# class type, like "$type":"SpatialSys.UnitySDK.VisualScripting. SpatialSyncedObjectEventOnOwnerChanged"
, so you will need to search for the class names instead of the above node names. Here are all the node types you may want to scan for:
C# Scripting Migration
If you have any references to SpatialSyncedObject
type in your C# scripts, you will want to replace them with SpatialNetworkObject
. Don't forget about re-assigning any MonoBehaviour property references after you replace the types.
To customize a Network Object
you write your own Network Behaviour script and attach it to the object. This gives you all the event hooks you need to handle equivalent functionality.
Here is the public api comparison between the two components:
Synced Object API | Network Object API |
---|---|
SpatialSyncedObject.syncTransform | SpatialNetworkObject.syncFlags |
SpatialSyncedObject.syncRigidbody | SpatialNetworkObject.syncFlags |
SpatialSyncedObject.saveWithSpace | Save with space is not supported on Network Object |
SpatialSyncedObject.destroyOnCreatorDisconnect | SpatialNetworkObject.objectFlags |
SpatialSyncedObject.destroyOnOwnerDisconnect | SpatialNetworkObject.objectFlags |
SpatialSyncedObject.isMasterClientObject | SpatialNetworkObject.objectFlags |
SpatialSyncedObject.syncedObjectID | SpatialNetworkObject.objectID |
SpatialSyncedObject.ownerActorID | SpatialNetworkObject.ownerActorNumber |
SpatialSyncedObject.hasControl | SpatialNetworkObject.hasControl |
SpatialSyncedObject.isLocallyOwned | SpatialNetworkObject.hasControl |
SpatialSyncedObject.TakeoverOwnership | SpatialNetworkObject.RequestOwnership |
SpatialNetworkObject.ReleaseOwnership | |
SpatialNetworkObject.TransferOwnership | |
SpatialNetworkObject.TryFindObject | |
SpatialSyncedObject.isLocallyOwned | SpatialNetworkObject.hasControl |
SpatialSyncedObject.onObjectInitialized | This has no direct equivalent, but you can get a Spawned() callback when you implement a Network Behaviour script |
SpatialSyncedObject.isSynced | This has no direct equivalent, but you can use a Network Behaviour script to set a public bool isSpawned { get; private set; } property on your behaviour that you can check externally |
SpatialSyncedObject.onOwnerChanged | Ownership change callbacks can be handled in a Network Behaviour script when implementing the IOwnershipChanged interface |
SpatialSyncedObject.onVariableChanged | Variable change callbacks can be handled in a Network Behaviour script when implementing the IOwnershipChanged interface |