Search Unity

Getting my head around the ECS paradigm (and how to create Entities hierarchies)

Discussion in 'Entity Component System' started by NoahSmiles22, Jan 10, 2020.

  1. NoahSmiles22

    NoahSmiles22

    Joined:
    Dec 31, 2019
    Posts:
    1
    Hello fellow Unity programmers. I'm new to ECS paradigm and I'm asking for help getting my head around it. Here's my project at the moment. I'm trying to set a galaxy for a space game (nothing to difficult to conceptualize) based on a hierarchy of Galaxy > Star System > Celestial Bodies > Stations > etc...

    I've been coding it in an OOP style and there's nothing wrong with it for now. But I'm now trying to convert the whole idea into the ECS paradigm (for scale and optimization sake). I've read that entity hierarchies are not the optimal way to go but I can't figure out how to do it otherwise (maybe go with a standard gameObject OOP approach and entities for specific things ?).

    I'm trying to have a Galaxy Entity wich contains references to all the star system entities belonging to it and so on.

    In your opinion, what would be a good implementation of this idea using ECS ?

    EDIT : Maybe I'll elaborate more on what I tend to achieve. Let's say I'd like a live map of the galaxy (so basicaly stars floating around), with possibility to have access to specific systems. I don't want to have seperate scenes for each system : just a nice zoom into the details of each system.

    Also, I heard about subscenes but I can't figure out how those work. Is there any documentation available for that matter ?
     
  2. Spectralshift

    Spectralshift

    Joined:
    Sep 19, 2018
    Posts:
    8
    Disclaimer: I'm also new to DOTS and not an expert, but this is exactly a case I want to use ECS with, but I have only done simpler stuff. Just adding my thoughts for the conversation, hopefully someone else with more experience can chime in... especially around the rendering part.

    The thing to keep in mind with ECS vs OOP is that ECS is a very flat data model. For instance, there is no need to bundle things together into a galaxy! Instead, you can say that a galaxy is made up of "All Entities that Contain Star Component". So, if you wanted to ask "how many stars are there", then the answer is... "All Entities that Contain Star Component". Likewise, if you were intending to have 'paths' (eg: hyperlanes), the galaxy would have pathways equal to "All Entities that Contain Pathway Component"... and if you want to know how many planets there are in the galaxy... it's the same, even if they 'belong' to a star - "All Entities that Contain Planet Component".

    It takes a different way of thinking to design like this... which is a real struggle! It's particularly difficult to change old sequential loop problems when changing over to ECS. For example, when you process production, all you care about is "All Entities that Contain Building Component" - not "for each star, for each planet, for each building" which would be a traditional loop. The issue is that the old loop essentially has a "controller" assumption - that is, the planet you are looping through also refers and aggregates to that planet.

    With ECS, you need to make sure things are more atomic. That is, all buildings produce at the same time, on every planet, everywhere. So, in a way, you have to store what it produces in the building itself. This makes it a bit more complicated to aggregate data up the hierarchy. In this case, you may have each building cache its production, then pull that data into the planet (eg: add all buildings that are associated with the planet), then pull that data in to the star system (eg: add all planets that are associated with the planet), then pull that data into the player bank (eg: add all star systems that are linked to the player(s)). Each of those is just reading the data from it's children and writing to itself (eg: pass in all planets as read/write, and all buildings as read only).

    Having said that, you'll need lots of references. This is likely to be dynamic buffers of entities for the parent->children and/or a single reference for the child->parent. So, an aggregation job would essentially be "for all entities with a planet resources component AND building references component", do "for all building references, add to planet resource components". This keeps buildings read-only and makes it thread safe, assuming only one parent, and should be fully burstable.

    There are probably a ton of details that will make this more complicated. Fleets probably will have a "universe position" and a "system position", and may belong to multiple entities (home base, current system or pathway, etc.)

    ---

    As for separate scenes - I don't really know. You probably will want a few forms of UI overlays (not a new scene) that just reads a specific entity's data (eg: an entire planet, or a station, or a fleet/ship)... which will just be some base logic on how to access the data. You can think of that as having a data query that runs and updates ~frame. Probably have a specific entity reference, that then branches into its children, so that you aren't process excessive entities.

    There probably will be a main scene that renders all Stars/Fleet icons/pathways/borders/etc... and probably a scene and/or camera location that loads only a subset of entities (eg: a star system). Haven't done ir before, so I won't comment.

    ECS, for this part, is probably less important than it seems (other than rendering huge amounts of objects in #1). The main benefit is the massive improvement in manipulating all the data that the game logic demands. Fleets, stations, production, trade, population... all of that is data focused and is what I would focus on. It's non-trivial when you start getting into it. For example, figuring out if a planet has a bonus because of the system it is in? Or because there is a station over the planet? Or because there is a population of a particular type, or a zone on the planet, etc. There are some big systems - rendering is a pretty small part (if critical), IMO.
     
    tarkhon likes this.
  3. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,270
    That's not necessarily true. In DOTS it is all about using the right tool for the right job. You can either have the "parent" hold a dynamic buffer of entity references to all the "children", or you can have all the "children" have an ISharedComponentData with a field referencing the "parent". Both can be optimal for different entity counts and different algorithms. If you are new to this data-oriented space, I wouldn't worry so much on doing things the most efficiently and instead just try to get your game working in ECS. You'll still probably get huge gains compared to MonoBehaviours because that's just the nature of "performance by default" which is actual true (with the exception of ForEach in a ComponentSystem).
    Subscenes are simple. You put GameObjects into them from a real scene. When a subscene is open, the things inside it exist as GameObjects and can be edited. When a subscene is closed, the things inside it exist as converted entities and can't be edited but still show up in the editor for context and still function in play mode. It basically is a means to not kill the editor with a bunch of GameObjects you plan to convert to entities. And they are preconverted in a build to reduce loading times.
     
    Vacummus and florianhanke like this.
  4. vildauget

    vildauget

    Joined:
    Mar 10, 2014
    Posts:
    121
    Oh, bugger. The ForEach makes all so much simpler, are you saying we should be careful about using it? Are there specifics that makes it non-performance by default?

    Thank you.
     
  5. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,270
    Use ForEach in a JobComponentSystem instead. It uses codegen to circumvent the issues in ComponentSystem.
     
    vildauget likes this.
  6. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    This is a great discussion. Thanks for the insights. I hope it's ok to "hijack" this and ask my questions here since I feel they go in the same direction.
    I'm also wondering how to approach "traditional" hierarchical systems in an entity-conformant way. Specifically I want to query all entities wich are located in a specific solar system. To avoid floating point issues all objects are centered around 0.0 or the "middle" of the solar system. But a spaceship in Alpha Centauri should not collide with one in Sirius. So my question is how to properly and efficiently design the data and query it. And how the systems access it.

    Should each entity have a SystemID component which has an int value for the system the object is in? Since the game is procudral I can hardly add a System001, System002 etc. component to them like a tag I have seen in some examples. As the OP mentioned in OOP a solar system would have a list of everything that is contained in it. And then handle this stuff (collisions, productions etc). But in ECS all moving stuff is handled once when a system runs and I need a way to let it only interact with stuff from the same solar system. The other way beside the ID I could think of is to have the stuff "sorted" by their solar system and for example check collisions only between 0 and 8 for all entities in the first solar system. 9-13 for the second solar system and so on. Is this viable and meaningful or does this go against the design objectives of DOTS? Am I thinking in a wrong mindset?

    Anyway. I hope someone can shed some light about this to me since I really would like to use DOTS for my new project but I can't come up with a way to handle this issue in DOTS.