Ghost groups
Use ghost groups to synchronize replication timings across multiple ghost instances, and prevent common gameplay state errors.
Ghost group usage
Configure a ghost group
To create a ghost group, you need to define a ghost group root, then define said ghost group root's children.
- Add a
GhostGroupbuffer to a ghost prefab at authoring time using the Ghost Group toggle in the GhostAuthoringComponent's Inspector window. This defines the ghost group root, and allows ghost group membership (by other ghost instances). - For each ghost group child instance;
- Add the
GhostChildEntitycomponent to said child. - Add the child ghost
Entityto theGhostGroupbuffer on the root.
- Add the
Ghost group behaviour
- All ghost group children added to the ghost group root are guaranteed to be sent whenever the root ghost entity is.
- The root ghost entity is implicitly defined as the ghost instance with the
GhostGroupbuffer component, and without theGhostChildEntitycomponent.
Ghost group limitations
- There can only be one root ghost entity per group.
- Ghost groups do not support nesting. I.e. A ghost group entry cannot be a member of multiple ghost groups.
Relevancyisn't supported for ghost group child entities while they are within a ghost group. They inherit the relevancy of their root ghost entity, and marking a child as irrelevant has no effect.
Note
Known issue as of 03/2025: If a relevant ghost group root becomes irrelevant, its children will not currently be made irrelevant - they will be left stranded.
GhostOptimizationMode.Staticisn't supported for ghost groups. You also can't mark children asStaticwhile the root isDynamic(or vice-versa). AllGhostGroupghosts are forcedDynamic, even if authored asStatic.- The
Importance,GhostImportanceImportance Scaling, andMax Send Rateof child ghosts is ignored when they are part of a group. Only theGhostGrouproot ghost chunks values are used. GhostGroupserialization is significantly slower (relatively speaking), as we must traverse to the chunk of each individual child (similar to how replicatingGhostField's on components onUnity.Transforms.Childchild ghost entities is slower). Consider the potential impact on performance when using ghost groups.- Serializing
GhostGroupentries can cause a single snapshot to be larger than the default (ofNetworkParameterConstants.MaxMessageSize), which may increase the frequency of snapshot packet fragmentation.
Note
For performance reasons, errors are not reported for incorrect GhostGroup usage.
Ghost group example use case
You have a Player character controller ghost in a First-Person Shooter, that can pick-up, drop, and carry three individual Gun ghost instances.
When carried by the player, each gun is attached to different points on the characters body (using a faked parenting approach), and can drive the characters hand animation state.
Your assets may look like this:
'Player' Ghost Prefab
Importance:100, Max Send Rate:60, Owner Predicted, Has Owner, Dynamic,
RelevantWithinRadius:1km, DynamicBuffer<GhostGroup> (Count:0)
'Gun' Ghost Prefab
Importance:10, MaxSendRate:10, Owner Predicted, Has Owner, Static,
RelevantWithinRadius:200m
Without ghost groups
Without ghost groups, you may experience the following issues during gameplay:
- When other players observe you picking up and firing a gun, they see your characters hand animation update before the gun physically moves into your hand.
- The held gun may not perfectly follow the players hand position and rotation, either.
- They may even see the gun firing FX appear from the gun on the ground (or in the process of being picked up).
- Distant players may appear to be empty-handed (due to differences in Relevancy and/or Importance leading to gun spawn delays).
- Exceptions may be thrown inside client
Gunsystems if they (incorrectly) assume that aGunentity'sHoldingPlayerentity reference will always exist, but it's been deleted in a previous snapshot before the deletion of thisGunwas replicated (or vice-versa).
With ghost groups
To use ghost groups in this example:
- Add the
GhostGroupbuffer to thePlayerghost (by checking the GhostGroup option on the GhostAuthoringComponent's Inspector window). - At runtime, when picking up a gun instance, add said
Gunghost entity to thePlayer'sGhostGroupbuffer... - ...and add the
GhostChildEntitycomponent to saidGuninstance. - Similarly, when dropping a gun, remove it from the
Player'sGhostGroupbuffer, and remove theGhostChildEntitycomponent from the (now dropped) gun.
This makes the Player ghost instance the ghost group root, and each picked up Gun ghost instance a ghost group child.
Each Gun ghost instance will now be replicated every time the Player ghost instance is, preventing the issues described in the without ghost groups section.
It also means:
- Each
Gun'sImportanceis now effectively 100 and theirMax Send Rateis 60 (the same as thePlayerghost group root). - Each
Gunghost instance is no longer static-optimized (and therefore, won't be forced intoUseSingleBaseline:true). - Each
Guninstance is only considered relevant to a connection if its ghost group root is and spawns the moment thePlayeritself spawns (once within 1km of each connection's ownPlayer).