Search Unity

Best way to detect if a component has been added or removed?

Discussion in 'Entity Component System' started by KoboldgamesUser, Jan 28, 2019.

  1. KoboldgamesUser

    KoboldgamesUser

    Joined:
    Nov 20, 2010
    Posts:
    11
    Context:

    I try to implement our weapon system of a networked shooter with ecs. Right now I have a weapon entity, and use several components to define its state, when it receives input:

    state components eg:
    Burst
    Reload
    Swap

    This works great so far. What I struggle with is the syncing over the network. I get the input only on the server and I'd like to send a message with the component data as soon as one of the components gets added or removed.

    Question:

    Is there another way than creating AddedBurst / RemovedBurst components for all three state components respectively and adding / removing them by the systems that originally added / removed the state components?
    This seems like way too much adding/removing components, distributes the responsibility for syncing to multiple systems and is a lot of code to write.
     
  2. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    You can use generics: Added<T>, Changed<T>, Removed<T>.

    For example:

    Code (CSharp):
    1. public struct Added<T> : IComponentData where T : struct, IComponentData {
    2.     public T value;
    3. }
    Code (CSharp):
    1. entityManager.AddComponentData(entity, new Added<T> { value = new T() });
    2. endFrameBuffer.RemoveComponent<Added<T>>(entity);
    "endFrameBuffer" is EndFrameBarrier's CommandBuffer. This way, "Added<T>" will get automatically removed at the end frame.

    If you want to centralize all the syncing into one system, you can use a dedicated ComponentData to track old values of other CDs. Sample code:

    Code (CSharp):
    1. public class SyncSystem : ComponentSystem {
    2.  
    3.     ...
    4.  
    5.     protected override void OnUpdate() {
    6.         ...
    7.         oldValuesCompData.HealthValue = Process<Health>(entity, id, oldValuesCompData.HealthValue);
    8.         oldValuesCompData.ManaValue = Process<Mana>(entity, id, oldValuesCompData.ManaValue);
    9.         ...
    10.     }
    11.  
    12.     protected T Process<T>(Entity entity, ushort id, T oldValue)
    13.         where T : struct, IComponentData, INetworkCompData<T> {
    14.         if (EntityManager.HasComponent<T>(entity)) {
    15.             var comp = EntityManager.GetComponentData<T>(entity);
    16.             var changed = !comp.Same(oldValue);
    17.             if (changed)
    18.                 SendToAllPlayers<T>(new DataPacket<T> { id = id, on = true, value = comp });
    19.             return comp;
    20.         } else {
    21.             SendToAllPlayers<T>(new DataPacket<T> { id = id, on = false, value = oldValue });
    22.         }
    23.         return oldValue;
    24.     }
    25.  
    26.     protected void SendToAllPlayers<T>(DataPacket<T> packet) {
    27.         ...
    28.     }
    29.  
    30. }
    31.  
    32. [System.Serializable]
    33. public struct Health : IComponentData, INetworkCompData<Health> {
    34.     public int value;
    35.     public int max;
    36.  
    37.     public bool Same(Health other) {
    38.         return this.value == other.value && this.max == other.max;
    39.     }
    40.  
    41.     public void Serialize(NetDataWriter writer) {
    42.         writer.Put((ushort)value);
    43.         writer.Put((ushort)max);
    44.     }
    45.  
    46.     public void Deserialize(NetDataReader reader) {
    47.         value = reader.GetUShort();
    48.         max = reader.GetUShort();
    49.     }
    50. }
     
    Last edited: Jan 28, 2019