Search Unity

HealthComponent in ECS

Discussion in 'Data Oriented Technology Stack' started by Enzi, Aug 14, 2019.

  1. Enzi

    Enzi

    Joined:
    Jan 28, 2013
    Posts:
    189
    One of the most prominent components I found in ECS projects is a HealthComponent
    Usually this has:
    Code (CSharp):
    1. float health;
    2. float maxHealth;
    3. bool isDead;
    I never thought about it too much but isn't this bad design for ECS?
    The component could be split up in 3. One for health, one for maxHealth and a DeadTag.
    There certainly is some risk of making too many components but is that actually bad for ECS?
    I don't think so, right?
     
  2. mnarimani

    mnarimani

    Joined:
    Mar 27, 2017
    Posts:
    184
    I believe it's bad. Since every time that you want to change health, you're going to read other values too. It's better to split up components.
    I only keep values in one component if they are always required together.
     
  3. Creepgin

    Creepgin

    Joined:
    Dec 14, 2010
    Posts:
    249
    I used to think ECS would mean an explosion of ComponentData as the game gains new features. But in practice, this only happens in the start of the project. If you design the components with reusability and generality in mind, and as the project matures, new functionality/feature of the game should be using more and more existing components instead of introducing new ones.

    One tangible example is the Overwatch Workshop where you can make seemingly unlimited new gameplay based on an existing set of components.
     
    Last edited: Aug 14, 2019
    Draveler likes this.
  4. Ferazel

    Ferazel

    Joined:
    Apr 18, 2010
    Posts:
    339
    The counter argument, from my understanding, is that if you have a lot of unique components you're going to have a lot of unique archetypes and those archetypes are going not going to have a lot of cache coherency between them. I believe that it is often a balancing act.

    I do agree that IsDead should probably be its own tag, as that represents more of a state. However, current vs max health seem like they should go together. In particular if you have a concept of healing in your game.
     
  5. Enzi

    Enzi

    Joined:
    Jan 28, 2013
    Posts:
    189
    Good read and arguments!
    Archetypes are at a weird state for me right now. They are the closest to traditional OOP thinking when you make the connection between an entity and a list of components.
    I feel like, in my projects, I throw them together like the objects they are and not actually what's the best memory layout for it.
    Like when you have enemies/players that are different with their components, they are in different chunks. How much more speed up will you get when you iterate over their HealthComponent when you could have a whole chunk of HealthComponents. The more compact one, without unneccesary data is faster of course.

    Technical question in that regard. When you call ToComponentDataArray<Translation> for jobs, we should get a perfect block of memory laid out for that component from GatherComponentDataJob, right? Then we have GetArchetypeChunkComponentType. Archetypes seem more rigid with calls like that.

    I think I need to create a tier list of calls, from fastest to slowest or something because at some point I could confuse myself with all these options.
     
  6. Ferazel

    Ferazel

    Joined:
    Apr 18, 2010
    Posts:
    339
    So if there is 1 component difference between your enemy and your player entities, they are going to be different archetypes. An archetype is going to define a new memory chunk. The speed difference between cache coherent call and a new chunk/archetype call is a huge speed hit. However, that speed hit is likely about the same speed as iterating through an array of OOP class objects. So it's probably not worth overthinking it, but just something to keep in mind as you look into your component/data layout.

    In regards to your component data array question. This talk might help you out:

    Basically, you are iterating through memory chunks even though it "acts/looks" like an array it isn't contiguous memory. There are checks that determine if you need to jump chunks to find the index that you're looking for.
     
  7. LazyGameDevZA

    LazyGameDevZA

    Joined:
    Nov 10, 2016
    Posts:
    76
    Looking at the documentation and types for ToComponentDataArray it does look like it'll provide the data in a continuous block of memory. The video @Ferazel linked is somewhat outdated if you ask me. It's because of this indirection that Unity had gotten rid of the idea of the ComponentDataArray type and rather made the interaction more explicit. I'm referencing the documentation from this link:https://docs.unity3d.com/Packages/com.unity.entities@0.1/api/Unity.Entities.EntityQuery.html

    The key thing to remember though is that ToComponentDataArray is copying the data from chunks into a NativeArray, so you're not completely getting away from chunk iteration. That said it might have an impact on removing the indirection from your jobs to enable them to perform as optimal as possible.
     
  8. RandyBuchholz

    RandyBuchholz

    Joined:
    Sep 23, 2016
    Posts:
    21
    Enzi, your example brought to mind a general area I'm struggling with - data constraints and Data Domains.Most of my data has some type of constraint like (min < data < max). These are data level constraints, not business level, so I want them available at the point of calculation. A simple adder [input1] [input2] => [output] could, worst case, be something like [(min, data, max)] [(min, data, max)] => [(min, data, max)]. I've played with creating `DataDomainSharedComponent` to match `DataDataComponent` (where Data in DataDomain), and "decorating" each chunk with the constraints matching the contained components, but this is a little cumbersome.

    What is a good way to implement data domains or data constraints?
     
  9. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    371
    You can use public properties and private fields to sanitize writes to the component assuming the code doesn't use any sort of unsafe type punning to work around it. While you generally don't want to use properties as it makes nested fields (especially the Unity.Mathematics types) more cumbersome to work with, this is a use case where properties on components makes total sense.
     
  10. RandyBuchholz

    RandyBuchholz

    Joined:
    Sep 23, 2016
    Posts:
    21
    Sounds good. I think I was too data focused and didn't event think about putting method/property based rules on the component.