Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Creating one system for all component variations?

Discussion in 'Entity Component System' started by Ardenian, Nov 22, 2020.

  1. Ardenian

    Ardenian

    Joined:
    Dec 7, 2016
    Posts:
    313
    Hello, coming from the vanilla Unity Editor and GameObject-MonoBehaviour, I try to learn DOTS. However, something that I don't understand is whether I have to create an unique system for every component variation or whether there is a way to generalize it to only use one system for all component variations.

    Imagine an entity Player that has the following components:
    • Health
      ,
      Health Regeneration

    • Stamina
      ,
      Stamina Regeneration

    • Mana
      ,
      Mana Regeneration
    What I need here is a Regeneration System that handles my regeneration types. For health, for instance, every frame I want to add the value of the
    Health Regeneration
    component to the value of the
    Health
    component, multiplied by
    deltaTime
    .

    Now what I could do is write a Health Regeneration System, a Stamina Regeneration System and a Mana Regeneration system. These all operate after the same logic, just with different components. What I wonder here though, is there no way to create exactly one system, a Regneration System, that is able to handle all instances of all variations of regeneration types?

    In this particular case, writing an unique system for each regeneration type isn't a big deal, because there will not be many types of regeneration, however, I do can imagine cases in which you are forced to write an unique system for every single variation, with each single variation maybe not even having a lot of entities alive. Something that I can think of, for instance, is an equipment system. Each item has modifier components that change player components. However, what I have to do here is creating a unique system for every single existing combination of modifier components? Let's say an item has a modifier to
    Health
    and
    Mana
    , then I need to create a system for that which handles equipping and unequipping for that item, adding the stats to the player components. That's one system. If the item has a modifier for
    Health
    and
    Stamina
    , that's yet another unique system. I feel like I am missing a point here with entities and archetypes. Do you have an advice?

    EDIT: Even if you simplify the modifier system for an item to only have one system per possible modifier (one modifier for Mana, so one system for it, one modifier for Health, so one system for it), you would end up with a lot of systems, since each modifier component requires a new system that handles it.
     
    Last edited: Nov 22, 2020
  2. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    207
    I think in principle you could use a generic system, eg RegenerationSystem<T> where T : IRegenerated, struct, IComponentData. You will still need to create the individual systems, but their implementation becomes as simple as HealthRegenerationSystem : RegenerationSystem<Health> {}

    You may find the below thread relevant:
    https://forum.unity.com/threads/reactive-system-generic-way.919997/#post-6032807
     
  3. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Your concept is in some how from OOP, inheritance or polymorphism. You can not do it in DOP by default. It is not a core feature for Data-Oriented programming. ComponentData Type in ECS is more of a tag specifying what the value of the data type means.
    You can wrap your Regeneration function as a static function like
    public static float Regeneration(float value, float deltaTime, float regenRate)
    and used in different systems.
    But you will need your system to tell which ComponentData to which. So, for example, the damage is not applied to Stamina or Mana.

    You can Query Health Stamina and Mana together and Regeneration them together in one Query and one job.
    But you will not do it with inheritance or polymorphism.

    Here in DOP ComponentData Type is the interface for all communication.
     
  4. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    676
    You can have the shared logic in a static method and simply do one single system for all you stuff, just ensure to manually handle the dependencies to still allow the logic to run in parallel to each other:

    Code (CSharp):
    1. public interface IFloatValue
    2. {
    3.     public float Value { get; set; }
    4. }
    5.  
    6. public interface IFloatRegenData
    7. {
    8.     public float Amount { get; set; }
    9.     public float Speed { get; set; }
    10. }
    11.  
    12. public struct Health : IComponentData, IFloatValue
    13. {
    14.     private float _value;
    15.  
    16.     public float Value
    17.     {
    18.         get => _value;
    19.         set => _value = value;
    20.     };
    21. }
    22.  
    23. public struct HealthRegen : IComponentData, IFloatRegenData
    24. {
    25.     // you got the idea
    26. }
    27.  
    28. public class RegenSystem : SystemBase
    29. {
    30.     protected override void OnUpdate()
    31.     {
    32.         JobHandle healthHandle = Entities.ForEach((ref Health health, ref HealthRegen healthRegen) =>
    33.         {
    34.             Regen(ref health, ref healthRegen
    35.         }).Schedule(Dependency);
    36.  
    37.         JobHandle manaHandle = Entities.ForEach((ref Mana mana, ref HealthRegen manaRegen) =>
    38.         {
    39.             Regen(ref mana, ref manaRegen
    40.         }).Schedule(Dependency);
    41.  
    42.         Dependency = JobHanndle.CombineDependencies(healthHandle, manaHandle);
    43.     }
    44.  
    45.     private static void Regen<T, U>(ref T target, ref U regenData)
    46.         where T : struct, IFloatValue
    47.         where U : struct, IFloatRegenData
    48.     {
    49.         // do your stuff
    50.     }
    51. }
    52.  
    53.  
     
    davenirline likes this.
  5. Ardenian

    Ardenian

    Joined:
    Dec 7, 2016
    Posts:
    313
    Thank you, this looks familiar from GameObject and MonoBehaviour, so this is probably what I am going to end up using for Regeneration, since as I said, there aren't many types of regeneration in my game.

    Thanks for the suggestion, doesn't this scale awfully, though? If I have a lot of different types to describe different things, I could imagine that this becomes difficult to deal with, because it would require many systems and thus potentially many jobs, dependent on how it is handled.

    Thanks, that looks convenient when dealing with a couple of types, but what is wrong if one has a good bunch of types. Is this a sign of an inherent design flaw then?