Search Unity

  1. Unity 2019.1 is now released.
    Dismiss Notice

Some beginner questions about pure ECS

Discussion in 'Data Oriented Technology Stack' started by JacketPotatoeFan, Apr 2, 2018.

  1. JacketPotatoeFan

    JacketPotatoeFan

    Joined:
    Nov 23, 2016
    Posts:
    15
    Hello,

    Thanks to all the examples & talks people have shared here. While a lot of it has gone over my head, it's appreciated.

    I've been looking at everything relating to ECS for a few days, but there are things that puzzle me (I'm not highly experienced with C# yet).

    I want to focus on the pure ECS only. Some of these questions might be pretty dumb, so I apologize.
    1. How do you expose properties in the inspector when using IComponentData?
      So currently, I can attach a MonoBehaviour script to a game object prefab, and the public fields (i.e name, color, speed) will show up in the inspector for me to change anytime. But I am not sure how you would do this using pure ECS.

    2. How do you create instances of your prefabs if you can't access them?
      This is kinda related to #1. I like to drag and drop my prefabs onto public fields of MonoBehaviours so then I have a reference to them to create instances. In the TwoStickShooter code, I saw that the game objects were in the scene (i.e "EnemyRenderPrototype", and had a MeshInstanceRendererComponent. In code, these are then searched for.

    3. Does every object you plan to create an instance of need this MeshInstanceRendererComponent?

    4. I don't fully get the deal with archetypes. Is this only useful if you have prefabs that will be instantiated multiple times?
      So if I have a cube prefab that could get instantiated 1,000 times, then it would be good to create an archetype? Whereas, if there is a instance of an object where only 1 will ever exist, then it may be pointless creating an archtype?

    5. When creating archetypes, does this need to be done via code?
      If I am understanding archetypes, then my game will have a lot of unique objects that can be placed in the world by the player. Each unique object can be placed multiple times (instances of that object), so creating all these unique archetypes in code will take a long time.

    I have tons of question, but I know that part of the problem is me not understanding the ECS right now. I hope there will be beginner material when it's released.

    Thanks
     
    avvie, alexchesser and MarkusGod like this.
  2. deplinenoise

    deplinenoise

    Unity Technologies

    Joined:
    Dec 20, 2017
    Posts:
    23
    You can use component wrappers to do this. It's a way to put an IComponentData on a game object, and you actually edit the game object in the inspector. You then spawn a prefab to make ECS data - this is an annoyance right now as we don't have a full definition of a pure ECS scene yet.

    MeshInstancerenderComponent simply means the entity will be rendered using the mesh instance render. So any entities you want to have rendered will need to have one.

    Entities always belong to an archetype. The archetype is the unique set of components. Think of it as a "type signature". When you add or remove components, the archetype changes and the entity's component data will move around in memory to live in chunks occupied by other entities with that component makeup.

    So if you think about adding 20 components each in turn, the entity will change archetype 20 times and be copied around in memory 20 times. You can short circuit that by creating an explicit archetype and tell the entity manager where the entity is going to end up right away, and then alter the data instead of adding it.

    So in terms of advice on archetypes, it's a good idea to create explicit archetypes for things you'll be spawning a lot of.
     
  3. JacketPotatoeFan

    JacketPotatoeFan

    Joined:
    Nov 23, 2016
    Posts:
    15
    Thanks.

    With the MeshInstanceRendererComponent, there are fields to set the mesh and material. What if your model has multiple materials?
     
  4. Cynicat

    Cynicat

    Joined:
    Jun 12, 2013
    Posts:
    254
    Wait what? dear god why?! A basic part of the ECS pattern is adding message components, eg: if something is damaged you add a damage component and you add other systems consume that component on the fly.
     
    Last edited: Apr 3, 2018
  5. superpig

    superpig

    Quis aedificabit ipsos aedificatores? Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,153
    And that's fine - when you do, the entity will be copied to a new archetype, with the extra component. That way the entity is tightly packed with all the other entities that also have the damage component, and when your other systems consume that component they are still operating on tightly packed data.
     
  6. Cynicat

    Cynicat

    Joined:
    Jun 12, 2013
    Posts:
    254
    Doesn't this mean though that if you have a few message types that many objects receive per frame, that the object is going to be copied once for both add and remove? eg: if 50 entities recieve 3 messages you have 300 memory copies. That seems like a lot to me.
     
  7. Cynicat

    Cynicat

    Joined:
    Jun 12, 2013
    Posts:
    254
    Wait ignore me, I'm an idiot. I don't know why it sounded like so much to me. That's nothing for blocks of structs. I shouldn't math at 4 a.m.
     
    FROS7 likes this.
  8. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    4,571
    It is indeed quite cheap. The thing is invoking a callback, executing some random code somewhere in comparison loads plenty of code into the cache. It just so happens that in object oriented code those kind of things don't show up.

    That said we are planning to reduce the copying we do specifically for empty / tag components.
     
    pvloon, FROS7, Cynicat and 1 other person like this.
  9. StephanK

    StephanK

    Joined:
    Jul 10, 2012
    Posts:
    46
    Although copying data around seems to be quite cheap in some cases it's annoying that adding a component to an entity might change all the indices within a ComponentDataArray. For instance if you essentially operate on a grid it is quite useful if components keep their initial order, so you can find entities by index (without making a by entity lookup).
     
    mx-bug and Cynicat like this.
  10. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    546
    If you need stability, you can use PostUpdateCommand to buffer changes until after the current system has run, and you can look at BarrierSystems (such as EndOfFrameBarrier) to buffer changes to when those systems run (as all they do is execute buffered changes).
     
  11. StephanK

    StephanK

    Joined:
    Jul 10, 2012
    Posts:
    46
    How do these BarrierSystems work with command buffers? I thought those were just there to "mark" the order of execution of the systems. My problem is also not so much indices changing while iterating (although that is also a pita) but more in general that the order is never guaranteed and therefore i can't do something like (idx + rowSize) to move "up" in my grid, or in other words i loose direct access to my data.
     
  12. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    546
    Here's an example I whipped up from a test project, just to play around with how stuff works:


    Code (CSharp):
    1.  
    2. [System.Serializable]
    3. public struct ATag : IComponentData { }
    4.  
    5. public struct BTag : IComponentData { }
    6.  
    7. //... elsewhere ...
    8.  
    9.     public sealed class TestEndFrameSystem : JobComponentSystem
    10.     {
    11.         struct Group
    12.         {
    13.             public EntityArray Entities;
    14.  
    15.             public ComponentDataArray<ATag> ATags;
    16.  
    17.             public SubtractiveComponent<BTag> BTags;
    18.  
    19.             public int Length;
    20.         }
    21.  
    22.         [ComputeJobOptimization]
    23.         struct MassBTaggingJob : IJobParallelFor
    24.         {
    25.             [ReadOnly]
    26.             public EntityArray Entities;
    27.  
    28.             public EntityCommandBuffer CommandBuffer;
    29.  
    30.             public void Execute(int index)
    31.             {
    32.                 CommandBuffer.AddComponent(Entities[index], new BTag());
    33.             }
    34.         }
    35.  
    36.         [Inject] Group _group;
    37.         [Inject] EndFrameBarrier _endFrameBarrier;
    38.  
    39.         protected override JobHandle OnUpdate(JobHandle inputDeps)
    40.         {
    41.             var job = new MassBTaggingJob() {
    42.                 Entities = _group.Entities,
    43.                 CommandBuffer = _endFrameBarrier.CreateCommandBuffer(),
    44.             };
    45.  
    46.             return job.Schedule(_group.Length, 64, inputDeps);
    47.         }
    48.     }
    Note that the Barrier has a "CreateCommandBuffer()" function which returns an EntityCommandBuffer struct. This can go on job threads (or be used on the main thread). All barrier systems have that function and creating that buffer is one of the key methods.

    I haven't played with it but you can use the order control attributes, so it's easy to make your own barriers for specific needs, then ensure your other jobs run before or after them. EndFrameBarrier just comes out of the box.
     
  13. sledgeman

    sledgeman

    Joined:
    Jun 23, 2014
    Posts:
    371
    ECS means completly GC free ?
     
  14. StephanK

    StephanK

    Joined:
    Jul 10, 2012
    Posts:
    46
    @recursive nice. That CreateCommandBuffer method is really handy. That still doesn't solve my original problem with changing order between frames, but it's a very good way to preserve order within a frame.
     
    recursive likes this.
  15. GenPhi

    GenPhi

    Joined:
    Dec 27, 2014
    Posts:
    11
    Did you ever get an answer to this? I was wondering the same thing since most models come with multiple meshes/materials.
     
  16. Zarenityx

    Zarenityx

    Joined:
    Dec 2, 2012
    Posts:
    9
    I guess my biggest concern will be what the actual interface looks like in the editor and how this will effect development flow. ECS is really interesting from a technical standpoint, and seems a lot more unified in its design than the GameObject-Monobehaviour model, but GO-MB was highly representative of the intended behavior:

    "This is a thing, it does this stuff"

    Wheras ECS seems to be

    "This thing exists and it's full of properties that other unseen stuff can manipulate".

    Not to mention, at the current moment it seems like there's just gonna be two models, ECS and GO-MB, until eventually GO-MB just gets deprecated.
    While ECS is exciting in a number of ways from a technical standpoint, I can't see myself really taking it seriously until:

    The GameObject-MonoBehaviour model starts working off of ECS internally:
    Every GameObject is fairly analogous to an entity anyway. Transform could be broken down to a collection of 3 components for Pos, Rot, and Scale. Mesh Filter is basically a component, and Mesh Renderer is basically a tag for a system that uses it. Monobehaviours are a bit tricky, but really even they can be broken down into a data part and systems part most of the time, which is why hybrid ECS is easy to port to.

    Every System inherently has its own tag component and cluster of 'required components':
    Think about the [RequireComponent] attribute in the current GO-MB. Every system starts out by searching through existing entities, so why not create the following structure:
    • Every system REQUIRES an empty 'tag' component. This prevents unexpected system clashes when two systems operate on the same data. (An enemy and player health might both operate on a 'health' component for instance)
    • Entities appear in the hierarchy and scene view (since GameObjects would be entities anyway there's not much of a change there), and the inspector could have a 'systems that affect this' section and a 'properties' section. The systems section wouldn't even have to do anything except show what systems will have this type of object show up in their queries (since every system now needs its own tag this is simply a query of the names of 'empty' components). The properties section would allow editing of the individual components and data, which could be grouped by component but also by system. If an entity uses two systems that rely on the same data, that data would just be displayed in both sections.
    • SYSTEMS can be dragged onto or otherwise added in the inspector to Entities. This would add the necessary tag and also add each of the components the system requires if they're not already there. Systems can also be removed, which removes their tag but only removes other components if other systems don't use them.
    • Components which aren't system tags can still be added at will to store custom data. Since every system has its own tag, there will never be unintended consequences
    This will make ECS more maintainable across larger projects, more intuitive (every entity now not only is associated with what it is but what it does, the one biggest strength of GO-MB. Not to mention, this means prefabs are inherently archetypes now, and in fact entire commonly used systems could be considered archetypes, and you now have a design-time definition for the most common archetypes, and I'm sure there could be some optimization done to give search priority to those archetypes (maybe even cache them separately so they don't need as much rearranging, or they're all in one spot by default?)

    There is sufficient (although removable) abstraction available to suit the needs of different team members:
    The programmers will want to see everything, of course, down to most minute detail. In fact the entire framework above is really just a helper to make ECS maintainable, but I'm sure there's some percentage for which it might get in the way. This could be abstracted down to user-definable Entity-views. An entity view with full access can do anything, even peer behind the veil to access the component list directly given the obligatory warning that they might get unexpected results. Programmers, system designers, etc. would use this view. An entity view for level designers or environment artists, on the other hand, would only allow changes to specific components. Say, the usual Pos, Rot, Scale, and then stuff like material properties, speed and damage of enemies, etc. They shouldn't be able to change what a prefab does, but can change what a prefab is. These sorts of entity views could be user-defined (perhaps via attributes), so that teams can adapt to their structure. This would reduce clutter and allow all members of the team to stay structured and in the flow of design, and enforces a separation of concerns that is incredibly useful for even moderately sized projects.




    I would honestly argue that one of the biggest reasons to choose Unity over Unreal is the fact that Unity promotes much better design flow. It's been part of Unity's image problem (easy for low-skill devs to make bad games), but also a big part of Unity's success (flexible enough to make great games of a large variety, and prototype quickly). Frankly, I can't see ECS, however fast and however uniform, furthering that. GO-MB was slow, weird, and clunky, but it was intuitive, and I would love to see ECS also be intuitive to use. If the goal is performance by default, then it should be default to achieve that performance.
     
  17. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    4,569
    Zarenityx,
    Lot of your concerns has been already discussed over, in different forums, topics, or conferences.
    Whether OOP is going to be phased out, I haven't seen definite answer to that. But mainly emphasizing, on transiting OOP structure into ECS format. Which will speed up OOP itself as well. So there are mutual benefits.

    Eventually we will see way, of interacting with entities directly, via inspector, or debugger. This was mentioned somewhere few times already. I wouldn't be concerned about ECS at such early stages.It is definitely very interesting approach.

    Slow
    / Fast is relative term, depending on many factors. From hardware, to OS, to coding. It is not OOP (GO-MB) problem. Mostly is the design of the game itself, where lies the issue. Exaggerating, but if you put Find 1000 game objects in Update (), no matter how your base components are fast, you will introduce unnecessary overhead.

    I maybe missed something, but Unreal engine is irrelevant in this topic. There is lots of discussion on Unreal vs Unity already. Mostly fanboys rumble. I have no experience in Unreal, so I will not expand on the matter here.
     
  18. jack_leng

    jack_leng

    Joined:
    Jan 3, 2017
    Posts:
    3
    hello,I want to focus on the pure ECS only too, but I have more than one material but only one mesh.how should i use the MeshInstanceRenderer
     
  19. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,247
    Not sure why you needed to bump 2 threads.

    But you can't.

    You need to split your mesh or merge your materials.
     
  20. jack_leng

    jack_leng

    Joined:
    Jan 3, 2017
    Posts:
    3
    upload_2019-1-12_11-6-49.png like this , must i change the mat or the mesh? i use
    upload_2019-1-12_11-7-55.png ,it can work . but change the MeshInstanceRendererSystem is terrible.I want to find a good solution
     
  21. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    4,569
    For pure approach, follow tertle.advise.
     
  22. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    232
    You can have submeshes MeshInstanceRendererComponent has a submesh field. So you can have different material per submesh, but you can't have two materials per submesh.