In [GhostField] there exists an option “Composite” that, when set to true, generates a single change bit for an entire struct. For example a float4x4 would normally require 16 change bits, one for each float value, but with composite on, only a single change bit would be written for all four floats.
This would mean that where normally if a float in that float4x4 would change, only the float would be serialized and sent to the client for changing. When composite is true, all sixteen floats would be sent to the client as it was recorded that all floats changed, instead of a single value.
While this seems costly, for rarely changing structs containing large numbers of values on a dynamic object, a single bit instead of perhaps 16 bits every tick, which adds up quickly.
But this option is only on sub-structs within replicated components. If I had a very rarely changing but significant number of fields in a replicated component, I would need to make a wrapper struct for the component whose field has composite enabled.
Alternatively (and what i’ve been doing) is aliasing the individual fields using explicit struct layout to overlap on to existing structs (e.g., uint4) that are then a ghost field with a composite tag enabled. The components in which I do this “hack” are typically bit compressed and require post processing to use so the loss of Burst vectorization is not an issue.
But this can apply in other cases as well where explicit struct layout is unacceptable given the performance loss. In which case, i would have to generate redundant wrapper structs. Not much of a loss, given that NativeArrays have .Reinterpret<>() that removes wrappers but still very annoying.
An example taken directly from my current code below. See that the ulong located on FieldOffset(0) is aliased by the actual lighting data. If I didn’t designate the replicated ulong as the replicated data, and using default ghost field options, I would need 7 bits to indicate a change in this very rarely changing component. Instead I have 1.
/// * Combined property values for a single bit change flag.
[GhostField] [FieldOffset(0)]
public ulong _netCode;
/// * Primary light radius.
[FieldOffset(0)] private half _lightRad;
/// * Low 4 bits: Soft halo radius. High 4 bits: Soft halo count.
[FieldOffset(2)] private byte _softData;
/// * Low 5 bits: Outer angle. High 3 bits: Inner angle, normalized to outer.
[FieldOffset(3)] public byte ConeData;
/// * Light color. Note additive blending. Intensity is thus alpha.
[FieldOffset(4)] public Color32 Color;
This should not be needed if there’s an option on GhostComponent itself indicating that only a single bit should be used to indicate a change within the component for replication.
Basically, I am requesting a Composite option in GhostComponent itself that applies to all fields within it.
It would also be very nice in applications where variants can use a composite version of the networked struct like LocalTransform. If I had a dynamic entity that had components regularly changing but the actual transform very rarely updated. That way, the 3 change bits default on LocalTransform would be reduced to 1. I don’t know if the Explicit struct trick works for variants though, haven’t tried.