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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

ECS components and inharitance

Discussion in 'Entity Component System' started by Micz84, May 7, 2018.

  1. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    436
    I have a question is it a good idea to use inheritance for components?

    For example:
    Code (CSharp):
    1. public struct Buff :IComponentData {
    2.  
    3. public float Time;
    4.  
    5. }
    And some buffs like:
    Code (CSharp):
    1. public struct HealingBuff :Buff {
    2.  
    3. public float HealRate;
    4.  
    5. }
    Code (CSharp):
    1. public struct SpeedBuff :Buff {
    2.  
    3. public float SpeedIncrease;
    4.  
    5. }
    I could have one system that counts down a time for all buffs, another that removes all buffs for which has expired and one system per buff that performs buffs action. Is it a good idea?
     
  2. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    Well I believe you can do that.

    But be aware of using something like that:
    Code (CSharp):
    1. EntityManager.GetComponent<Buff>(entity);
    2.  
    3. or
    4.  
    5. struct Data {
    6.       ComponentDataArray<Buff> data;
    7. }
    this won't work at all!
     
  3. siggigg

    siggigg

    Joined:
    Apr 11, 2018
    Posts:
    247
    You cannot inherit from another struct in C#
     
  4. Tom01098

    Tom01098

    Joined:
    Jun 8, 2015
    Posts:
    42
    Your second two structs should implement IComponentData (cannot inherit from Buff). Then you can add Buff and the buff component that you want (speed, health, etc) to the entity. This is composition, inheritance doesn't work in ECS implementations.
     
    Cynicat likes this.
  5. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    335
    Let me see, you are suggesting a separate BuffTime component, am I right?

    I thought of that too, but then how could you remove the buff component when the buff time runs out? You would need some kind of a reference inside the BuffTime component that refers to the actual buff component. Otherwise the system wouldn't know which component type to query for.
     
  6. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    558
    your buff could be a separate Entity:
    Code (CSharp):
    1. public struct Buff : IComponentData {
    2.     Entity target;
    3.     float time;
    4. }
    then your systems applies buffs to the targets (plus, this way a single entity can have multiple buffs)
    to remove a buff, destroy the entire entity.

    you could also try to separate the components and have
    Code (CSharp):
    1. public struct BuffTarget : ISharedComponentData {
    2.     Entity target;
    3. }
    4. public struct BuffTime : IComponentData {
    5.     float time;
    6. }
    if you need to get all the buffs that an entity have
     
    starikcetin likes this.
  7. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    436
    But you cant filter using ComponentData field value. But this solution is OK and alternative is to create more complex components with own time field and effect field, but it would require creating timer system for each buff. A separate entity with a target is more flexible.
     
  8. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    An solution would be simply having HealingBuff and SpeedBuff be regular IComponentDatas, and then you'd have a HealingBuffSystem that operates on entities that have Buff + HealingBuff components, and a SpeedBuffSystem for Buff + SpeedBuff

    On top of that, you'd also have a regular BuffSystem that operates only on Buff, and handles the timing logic

    edit: wait nvm, it would cause problems if you want to have multiple different buffs with multiple timers on the same entity. Separate entity per buff or buff components that handle time on their own seems to be the solution
     
    Last edited: May 8, 2018
  9. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    436
    Yes, that is the case, and the first solution is better because you can have separate components for buff timer and buff effect and have just one system that updates time and one that removes all buffs.
     
  10. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Assuming it's multiplayer then you likely need an instance per buff on the server to tick them correctly. But what you send to the client can always be at least partially collapsed if not completely.

    What you will find is that if you make the system data driven you will wind up with almost no logic specific to a single buff/debuff. So I normally go with something like an Effect and EffectType. I actually use that for anything that can happen in combat. Damage, damage over time, buffs, debufs, are all effects of various types. The types I categorize by the mechanic, not what you call them in game.

    For instance here a partial list of effects from my game.

    DirectDamage,
    DamageOverTime,
    DirectHealing,
    HealingOverTime,
    DamageTakenIncrease,
    DamageTakenDecrease,
    DamageDecrease,
    HealingTakenDecrease,
    HealingIncrease,
    HealingDecrease,
    SpeedIncrease,
    SpeedDecrease,
    CriticalIncrease,
    CriticalDecrease,
    PhysicalResistIncrease,
    PhysicalResistDecrease,
    DamageReflect


    DamageOverTime/HealingOverTime for example is actually a bug. I still had it in my list and put it here to illustrate how not to do it. Healing over time should be though of as DirectHealing with a certain duration/number of ticks.

    So the hard part here is identifying the base mechanics.

    So what you will end up with is a system that is ticking all effects using a single global timer. One second is usually the simplest. From there I would probably then have systems designed around the various base effect mechanics. Like your tick system marks an effect as this effect should fire. And then your other systems that handle mechanic specific logic filter by is the effect marked as should tick, and is it an effect type that system deals with. So kind of like a pipeline flow.

    You could just start with one system and then expand as you grow, that is most likely what I would do starting out. Single system that abstracts out the handling into methods which can later move to systems of their own.

    Stacking should be abstracted out. It's potentially the most complex part of all this and it should be handled uniformly over all effects. That's not to say you can't have multiple stacking types. But make them effect specific and be prepared for some pain down the road.