Serialization and synchronization with GhostFieldAttribute
Use GhostFieldAttribute
to specify which fields and properties of Unity.Entities.IComponentData
or Unity.Entities.IBufferElementData
should be serialized and replicated from server to client. When a component or buffer contains at least one field that's annotated with GhostFieldAttribute
, a struct implementing the component serialization is automatically code generated.
In addition to GhostFieldAttribute
, you can use GhostComponentAttribute
to further customize how replication is handled by your runtime.
Customizing GhostFieldAttribute
serialization
Use these properties to customize how components and buffers are serialized using GhostFieldAttribute
. For more details, refer to the GhostFieldAttribute
API documentation.
Property | Default value | Description |
---|---|---|
Quantization |
Disabled by default on float and unavailable on integers. | Use the Quantization property to set quantization for floating point numbers (and other supported types, refer to ghost type templates) to limit the precision of data. The floating point number is multiplied by the quantization value and converted to an integer to save bandwidth. |
Composite |
Disabled by default. | Use the Composite property to control how delta compression computes the change field's bitmask for non-primitive fields (such as structs). When set to true , delta compression templating generates only one bit to indicate whether the entire struct contains any changes. If Composite is false, each field has its own change-bit. Use Composite=true if all fields are typically modified together (for example, GUID ). |
SendData |
Enabled by default. | Use the SendData property to instruct code generation not to include a field in the serialization data. This is particularly useful for non-primitive members (like structs) which have all fields serialized by default. |
Smoothing |
Default setting is Clamp . |
Use the Smoothing property to control how a field is updated when the ghost is in GhostMode.Interpolated . Options are Clamp (every time a snapshot is received, clamp the client value to the latest snapshot value), Interpolate (interpolate the field between the last two snapshot values every frame; if no data is available for the next tick, clamp to the latest value), and InterpolateAndExtrapolate (interpolate the field between the last two snapshot values every frame; if no data is available for the next tick, the next value is linearly extrapolated using the previous two snapshot values). |
MaxSmoothingDistance |
Use the MaxSmoothingDistance property to disable interpolation when the values change more than the specified limit between two snapshots. This is useful for dealing with teleportation, for example. |
|
SubType |
Use the SubType property to specify a custom serializer for a field using the GhostFieldSubType API. |
[!NOTE] Ghosts that are marked as both static-optimized and interpolated will never extrapolate, because static-optimized ghosts do not send snapshot updates when they haven't changed, so we therefore cannot differentiate between 'this continuously changing value has since stopped changing' and 'we have not yet received the next continuous value'.
GhostField
inheritance
If a [GhostField]
is specified for a non-primitive field type, the attribute (and some of its properties) are automatically inherited by all subfields which don't themselves implement a [GhostField]
attribute. For example:
public struct Vector2
{
public float x;
[GhostField(Quantization=100)] public float y;
}
public struct MyComponent : IComponentData
{
//Value.x will inherit the quantization value specified by the parent definition (1000).
//Value.y will maintain its original quantization value (100).
[GhostField(Quantized=1000)] public Vector2 Value;
}
Note
The SubType
property always resets to the default.
Component serialization
To mark a component for serialization and replication, add a [GhostField]
attribute to the values you want to send.
public struct MySerializedComponent : IComponentData
{
[GhostField]public int MyIntField;
[GhostField(Quantization=1000)]public float MyFloatField;
[GhostField(Quantization=1000, Smoothing=SmoothingAction.Interpolate)]public float2 Position;
public float2 NonSerializedField;
...
}
For a component to be serialized, the component itself must be declared as public
. Only public
members of the component can be serialized. Adding [GhostField]
to a private
member has no effect.
Dynamic buffer serialization
To mark a buffer for serialization and replication, all public
fields must be annotated with a [GhostField]
attribute.
public struct SerialisedBuffer : IBufferElementData
{
[GhostField]public int Field0;
[GhostField(Quantization=1000)]public float Field1;
[GhostField(Quantization=1000)]public float2 Position;
public float2 NonSerialisedField; // This is an explicit error!
private float2 NonSerialisedField; // We allow this. Ensure you set this on the client, before reading from it.
[GhostField(SendData=false)]public int NotSentAndUninitialised; // We allow this. Ensure you set this on the client, before reading from it.
...
}
For a buffer to be serialized, the buffer itself must be declared as public
. Only public
members of the buffer can be serialized. Adding [GhostField]
to a private
member has no effect.
You can use the SendData
property to skip serialization and replication of a field, which means that:
- The value of the fields that aren't replicated are never altered.
- For new buffer elements, their content isn't set to default and the content is undefined (can be any value).
Dynamic buffer fields don't support SmoothingAction
so the GhostFieldAttribute.Smoothing
and GhostFieldAttribute.MaxSmoothingDistance
properties are ignored on buffers.
ICommandData
and IInputComponentData
serialization
You can annotate your input's fields with [GhostField]
to replicate them from server to client. This can be useful, for example, to enable client-side prediction of other players' character controllers on your local machine.
When using automated input synchronization with IInputComponentData
:
public struct MyCommand : IInputComponentData
{
[GhostField] public int Value;
}
ICommandData
is a subclass of IBufferElementData
and can be serialized for replication from the server to clients. As such, the same rules as for buffers apply: if the command buffer is to be serialized, then all fields must be annotated.
When using ICommandData
:
[GhostComponent()]
public struct MyCommand : ICommandData
{
[GhostField] public NetworkTick Tick {get; set;}
[GhostField] public int Value;
}
Command data serialization is particularly useful for implementing remote player prediction.
Adding serialization support for custom types
The types you can serialize with GhostFieldAttribute
are specified via templates. Refer to the Ghost types templates page for a list of the default supported types.
In addition to the default supported types you can also:
- Add your own templates for new types.
- Provide a custom serialization template for a type and target it using the
SubType
property ofGhostFieldAttribute
.
Refer to how to use and write templates for more information on creating templates.
Note
Creating templates for serialization is non-trivial. If it's possible to replicate a type by adding [GhostField]
, it's often easier to just do so. If you don't have access to a type, you can create a variant instead.