How to parent CoherenceSync objects to each other
Out of the box, coherence offers several options to handle parenting of networked entities. While some workflows are automatic, others require a specific component to be added.
Generally there is a distinction if the parenting happens at runtime vs. edit time, and whether the two entities are direct parent-child, or have a complex hierarchy. See below for each case.
At runtime:
CoherenceSyncs as a direct child: when you create a parent-child relationship of CoherenceSync
objects at runtime.
Deeply-nested CoherenceSyncs: when you create a complex parent-child relationship of CoherenceSync
objects at runtime.
At edit time:
Nesting connected Prefabs assets: the developer prepares several connected Prefabs and nests them one to another before entering Play Mode. This covers both Prefabs in the scene and in the assets.
Creating complex hierarchies of CoherenceSyncs at runtime
While the basic case of direct parent-child relationships between CoherenceSync entities is handled automatically by coherence, more complex hierarchies (with multiple levels) need a specific component.
An example of such a hierarchy would be a synced Player Prefab with a hierarchical bone structure, where you want to place an item (e.g. a flashlight) in the hand:
Player > Shoulder > Arm > Hand
To prepare the child Prefab that you want to parent at runtime, add the CoherenceNode
component to it (in addition to its CoherenceSync
). In the example above, that would be the flashlight you want your player to be able to pick up. No additional changes are required.
This setup allows you to place instances of the flashlight Prefab anywhere in the hierarchy of the Player (you could even move it from one hand to the other, and it would work).
You don't need to input any value in the fields of the CoherenceNode
. They are used at runtime, by coherence, automatically.
To recap, for deep-nesting network entities to work, you need two things:
The parent: a Prefab with CoherenceSync
that has some hierarchy of child transforms (these child transforms are not networked entities themselves).
The child: another connected Prefab with CoherenceSync
and CoherenceNode
.
One important constraint for using CoherenceNode
is that the hierarchies have to be identical on all Clients.
Example: if on Client A an object is parented to Player > Shoulder > Arm > Hand, the hierarchy on Client B needs to be exactly: Player > Shoulder > Arm > Hand.
Removing or moving an intermediate child (such as Shoulder or Arm) would lead to undesirable results, and desynchronization.
Position and rotation
Similarly to the above, intermediate child objects need to have the same position and rotation on all Clients. If not, that would lead to desync because the parented entity doesn't track the position of its parent object(s).
If you plan to move these intermediate children, then we suggest to sync the position and/or rotation of those objects as part of the containing Prefab.
Following the previous example, if an object is parented to Player > Shoulder > Arm > Hand, you might want to mark the position and rotation of Shoulder, Arm and Hand as synced, as part of the prefab Player.
This way if any of them moves, the movement will be replicated correctly on all clients, and the object parented to Hand will also look correct.
Keep in mind that there is no penalty for syncing positions of objects that never or rarely move, because the position is not synced every frame if it hasn't changed.
This section is only useful to you if you want to understand deeply how CoherenceNode
works under the hood.
CoherenceNode
works using two public fields which are automatically set to sync using the [Sync]
attribute.
The path
variable describes where in the parent's hierarchy the child object should be located. It is a string consisting of comma-separated indexes. Every one of these indexes designates a specific child index in the hierarchy. The child object which has the CoherenceNode
component will be placed in the resulting place in the hierarchy.
The pathDirtyCounter
variable is a helper variable used to keep track of the applied hierarchy changes. In case the object's position in the parent's hierarchy changes, this variable will be used to help settle and properly sync those changes.
For an example of a CoherenceSync
parenting and unparenting at runtime in a deep hierarchy, check out the First Steps sample project, lesson 5.
CoherenceSync direct parent-child relationships at runtime
Objects with the CoherenceSync
component can be connected at runtime to other objects with a CoherenceSync
component to form a direct parent-child relationship.
For example, an item of cargo can be parented to a vehicle, so that they move together when the vehicle is in motion.
Keep in mind that on this page we deal with direct parenting of two CoherenceSync
GameObjects. If it's not practical to parent a network entity directly to the root of another, see instead how to deeply nest CoherenceSyncs.
When an object has a parent in the network hierarchy, its transform (position and orientation) will update in local space, which means its transform is relative to the parent's transform.
A child object will only be visible in a LiveQuery if its parent is within the query's boundaries.
Parenting network entities directly doesn't require any extra work. Any parenting code (i.e. Unity's own transform.SetParent()
will work out of the box, without any need for additional action.
You can add and remove parent-child relationships at runtime – even from the Unity editor, by drag-and-drop.
If the child object is using LODs, it will base its distance calculations on the world position of its parent. For more info, see the Level of detail documentation.
When the parent CoherenceSync
is destroyed, by default its CoherenceSync
children get destroyed together with it. This can be changed via the Preserve Children option on the parent, under Advanced Settings:
When Preserve Children is enabled, if the authority destroys or disables the parent entity, child entities get unparented instead of being destroyed together with the parent. Those children will now reside at the root of the Scene hierarchy.
For an example of direct child CoherenceSync
components parenting and unparenting at runtime, check out the First Steps sample project, specifically lesson 4.
Preparing nested connected Prefabs at edit time
coherence supports all Prefab-related Unity workflows, and nesting is one of them. It can make a lot of sense to prepare multiple networked Prefabs, parent them to each other, and either place them in the scene, or save them as a complex Prefab, ready to be instantiated. This page covers these cases.
When preparing a networked Prefab that contains another networked Prefab, one extra component is needed to allow coherence to sync the whole hierarchy: PrefabSyncGroup
.
For instance, let's suppose we have a vehicle in an RTS that can carry cargo, and it comes with cargo pre-loaded when it's instantiated:
In this example Spacetruck is a synced Prefab, with 4 instances of the synced Prefab Cargo nested within. To make this work, we add a PrefabSyncedGroup
to the root:
The component keeps track of child Prefabs that are also synced Prefabs. Now, whenever Spacetruck is instantiated, PrefabSyncGroup
makes sure to take 4 instances of Cargo and link the Prefab instances to the correct network entities.
Please note that if the nested Prefabs are more than one level under the root object, you still need to add a CoherenceNode
component to the child ones (in the example above, Cargo), to enable deep nesting at runtime.
So to recap:
The outermost Prefab needs CoherenceSync
and PrefabSyncGroup
.
The child Prefabs need CoherenceSync
and, optionally, CoherenceNode
.
When dealing with synced Prefabs that are hand-placed in the scene before connecting, such as level design elements like interactive doors, you need to ensure that they are seen as "unique". This is also covered in the Uniqueness page, but it's worth talking about it in the context of nested synced Prefabs.
When preparing such a Prefab, you need to set the Uniqueness property to No Duplicates. This ensures that, once multiple Clients connect and open the same scene, the synced Prefabs contained within are not spawned on the network multiple times.
Let's suppose we have a networked Prefab that represents a structure in an RTS (a LandingPad) that can be pre-placed in the scene. This structure also contains a networked vehicle Prefab (a Lander). This Prefab is synced as an independent network entity because at runtime it can detach, change ownership, be destroyed, etc.
To achieve this, all we need to do is ensure that both Prefabs are set to be unique. When we drag-and-drop the LandingPad Prefab into the scene, coherence automatically assigns a randomly-generated Prefab Instance Unique ID as an override. This number identifies these particular instances of these two Prefabs in the scene.
With this setting, we don't need to do anything else for these compound Prefabs to work.
Like for runtime-instantiated Prefabs, keep in mind that if the Lander is nested 2 or more levels deep in the hierarchy, it will also need a CoherenceNode
component.
If you plan to also instantiate this Prefab at runtime, you can add a PrefabSyncGroup
to the root as described in the previous section. This makes the Prefab work when instantiated at runtime, while the uniqueness takes care of copies in the scene.
To recap:
The outermost Prefab needs its Uniqueness set to No Duplicates. Optionally, you can add PrefabSyncGroup
to enable runtime-instantiation.
Any child Prefab also needs its Uniqueness set to No Duplicates. It also needs a CoherenceNode
if it's parented deep in the hierarchy.
An important thing to keep in mind when working with compound Prefabs in the scene: when you add a new nested synced Prefab to an existing one that has already been placed in the scene a few times, the Prefab Instance Unique ID for these instances will initially be the same.
For this reason, once you play the game, you might see all children disappear (except one). That is normal: coherence thinks that all these network entities are the same, because they have the same uniqueness ID.
You need to ensure that these new children have an overriden and unique ID on each instance in the scene. To do so, click on the button next to the Prefab Instance Unique ID for each child that needs it: