Search Unity

Official API usability

Discussion in 'Entity Component System' started by Adam-Mechtley, Sep 4, 2018.

  1. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,683
  2. thebanjomatic

    thebanjomatic

    Joined:
    Nov 13, 2016
    Posts:
    36
  3. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    Check out the SystemBase code: upload_2020-5-13_20-37-39.png
     
  4. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Any API that includes Interfaces like this:

    From line 45 of the Boids example here: https://github.com/Unity-Technologi...s/Assets/Advanced/Boids/Scripts/BoidSystem.cs

    Is not Keeping It Simple Stupid!

    What elements of the DOTS API have you found that are becoming way too complex/longwinded to be useful?

    Also the lack of warnings, feedback and good documentation (linked to by the warnings) when working with in built DOTS systems.

    Forum thread on the joys of trying to work with DOTS systems https://forum.unity.com/posts/5861107/
     
    Last edited: May 19, 2020
    Cynicat and galaskap like this.
  5. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    What happens when the solution domain (DOTS) becomes more complex than the problem domain (GAMES)?

    Could we end up with a situation where games become simpler as developers spend more time and energy working though the complexity of game development with DOTS than they do on the games development?

    What if DOTS just opens the door to massive unit count games but shallower games e.g. Bullet Hell and Boids Wars style games?
     
  6. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    451
    Why simpler? You can make a complex game in DOTS, it is harder because most of us are trained in OOP and it takes time to learn new things and get rid of old habits. Games like city builders, economics simulators can only benefit from DOTS, and I would not call games like Capitalism II a shallow game.
     
    florianhanke likes this.
  7. colin_young

    colin_young

    Joined:
    Jun 1, 2017
    Posts:
    243
    I disagree that DOTS makes things more complicated. I've adopted it not because I need the performance, but because I find it far, far easier to reason about my code with DOTS than with GameObjects. It does require a different mindset if you are used to the GameObject style. Personally, I've never liked it, possibly because I'm coming from a long history of business/website/database software background with a heavy dose of dependency injection, but I can see why it could be difficult to make the adjustment.

    Right now, a lot of the complexity comes from the rapid pace of change and lack of really good documentation (the documentation of the specifics is quite good, the bigger-picture is what I find lacking). I expect that will change as the dust settles.
     
    cultureulterior and shmafoo like this.
  8. Cynicat

    Cynicat

    Joined:
    Jun 12, 2013
    Posts:
    290
    Honestly, once we have better core editing workflows ECS and some API improvements it will be much simpler to work with ECS than the old paradigm ever was.

    Unity's default paradigm was originally: GameObjects for scene structure->MonoBehaviours for user data and logic->SendMessage for decoupling and exposing events. While this paradigm is very simple and powerful it also is hella expensive. Sendmessage is based on reflection and is extremely expensive for large codebases, gameobjects have tons of overhead, Monobehavior added quite a bit of extra memory/overhead to your own logic and data. So we'd have to use other approaches, tight coupling via GetComponent with interfaces to expose events, handling large datasets ourselves, ungodly multithreading, etc.. Games ended up having to take on lots of tech-debt to implement their systems efficiently.

    Enter ECS: Entities for structure -> Components for user data -> Systems for user logic -> and components/dynamicBuffers act as your events/decoupling as well. The kicker is, all these scale well performance wise. You can have thousands of systems that recognize different cases and respond, you can expose tons of events via components/dynamicBuffers, these also have a variety of advantages as decoupling tools in terms of the ability to filter/modify them in multiple stages (eg: having a fire resistance system that iterates over all your damage events and reduces it by your fire resistance). These allow huge amounts of code decoupling and allow lots and lots of gamerules to exist without stepping on eachother or making eachother more complex.

    HOWEVER: Unity's ECS still has a long way to go on their API, there are numerous problems with how their API works that make it more complex to work with, systembase was a big step in the right direction but it has several places where it makes code more complex than needed. They also haven't exposed enough via dynamicbuffers and such, especially in the physics packages(eg: trigger/collision events are still hellish to get and interpret).
     
  9. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Isn't one of the issue of DOTS scheduling systems. Take your example fire resistance system, how would you ensure it runs before any damage is dealt and only run once per new fire damage entity with resistance?

    I believe the default pattern is to use an empty tag component that can then be consumed how would this work for flammable items that have a fire resistant coating e.g. players driving a vehicle on fire?

    Is this a bad example and should fire resistance be some additional data held by flammable entities?
     
  10. Cynicat

    Cynicat

    Joined:
    Jun 12, 2013
    Posts:
    290
    There's an attribute for making your systems run before other systems: [UpdateBefore(typeof(SystemToUpdateBefore))] where you can define that something should run before a certain other system. The system only runs once in the frame so we know it won't get applied multiple times. You don't need any kind of tag component or something to make it handle that.

    For something like an actual "being on fire" effect, not just a damage type, you'd probably add a component that says that that entity is "On fire". A good trick for this would be having one component called Flammable which stores all the information about how that entity burns and another component called Burning (or some other better name) that stores the information about how the entity is currently on fire. eg: how hot the fire is, a reference to the particle effect entity, etc..
     
  11. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    Can you give some examples of what's fine, what's too complicated and what it should be?
    That'll help check temperature, see if the river of molten gold has cool down enough to dip toes.
     
  12. KwahuNashoba

    KwahuNashoba

    Joined:
    Mar 30, 2015
    Posts:
    110
    I would say that you are gonna burn your self that even Freddy Krueger himself would envy your skin only by the looks of it :)
    I'm currently working on my game in spare time and came sort of late to the DOTS party so I needed to stay alone at the bar, drinking hard liquor, until I got drunk enough to join the party. Why I'm telling this is that there is a lot written and spoken material about the DOTS out in the wild, also with the lack of proper documentation, so it's very easy to find more then you need solutions for one single thing which can introduce lots of confusion until you grasp what's the current state of the things. This is due to the rapid development of whole DOTS ecosystem so the whole approaches of doing things get deprecated repeatedly.
     
    laurentlavigne likes this.
  13. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    :D:D:D
     
  14. Orimay

    Orimay

    Joined:
    Nov 16, 2012
    Posts:
    304
    GetBuffer on SystemBase would help a lot!
     
    NotaNaN and Cynicat like this.
  15. Orimay

    Orimay

    Joined:
    Nov 16, 2012
    Posts:
    304
    Adding components of another entity with its values is a must-have. And it must be on EntityManager and EntityCommandBuffer. Something like

    Code (CSharp):
    1. EntityManager.CopyComponents(eSource, eTarget);
    2.  
    3. ecb.CopyComponents(eSource, eTarget);
    4.  
    5. cecb.CopyComponents(entityInQueryIndex, eSource, eTarget);
    This may be extremely helpful for networking and some prefabs manipulations where we need to make some existing entity matching a prefab.
     
    Zeffi and NotaNaN like this.
  16. SamOld

    SamOld

    Joined:
    Aug 17, 2018
    Posts:
    333
    NotaNaN, Orimay, Zeffi and 2 others like this.
  17. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    The past few weeks I've seen intellisense struggle with the vanilla API of Unity because it is so nested that unless you list the entire tree of sub classes in the using list then you have to google proper usage.
    This made me realize that the super verbose names that are the new flavor in DOTS are a very good thing.
    Just type a few keywords of what you want and voila it just appears.
    When reading code too, each name looks so funky that you recognize it at a glance.
     
    destructor465 and Zeffi like this.
  18. reeseschultz

    reeseschultz

    Joined:
    Apr 1, 2018
    Posts:
    21
    Speaking of API usability, I created a spatial events package anyone is free to use in their project. I wish something like it was included by default. I'm using it in a game I'm building to determine power connectivity, construction space availability, etc. As an intro, with it you can quickly check which "activators" have entered a "trigger's" bounds, which are overlapping, and which have exited.

    Handling entries and exits is easy via a change filter. For example:

    Code (CSharp):
    1. Entities // Example handling of the spatial entry buffer.
    2.     .WithAll<Cat, SpatialTrigger, PhysicsCollider>()
    3.     .WithChangeFilter<SpatialEntry>() // Allows us to process (only) new entries once.
    4.     .ForEach((in DynamicBuffer<SpatialEntry> entries) => // Do NOT modify the buffer, hence the in keyword.
    5.     {
    6.         for (var i = entries.Length - 1; i >= 0; --i) // Traversing from the end of the buffer for performance reasons.
    7.         {
    8.             Debug.Log(entries[i].Value + " is making me purr! Purrrrrrrr!");
    9.         }
    10.     })
    11.     .WithoutBurst()
    12.     .WithName("CatEntryJob")
    13.     .ScheduleParallel();
    Note that the
    SpatialTrigger
    and
    PhysicsCollider
    marked in
    WithAll
    is kind of redundant. Any entity with a
    SpatialEntryBufferElement
    should have a trigger. Triggers should have colliders, which is emphasized in the documentation. How pedantic you want your code to be is obviously up to your discretion, I'm just making sure my example is clear.

    Some tips:
    • Obviously you can check components on entities to see which types collided, and then your logic can handle each case.
    • Whether you're working with the
      SpatialEntry
      ,
      SpatialOverlap
      , or
      SpatialExit
      , you do not need to clear the buffer. That's handled for you (relax, you're the user!) in the
      SpatialEndSystem
      .
    • It's possible to have triggers that are also activators — don't worry, they are prohibited from self-activation.
    • I should note another buffer. While collider layers apply as always, I've additionally provided tags (
      SpatialTag
      ). These retain
      FixedString128
      s that are compared between triggers and activators, ensuring only those with at least one match will raise an event.
    • Don't forget the
      EntityManager
      ! It can be helpful, on the main thread, to check if the overlap buffer has at least one element.
    There's authoring components for everything noted above which you can nest conveniently in prefabs. If you have multiple triggers / activators in a prefab, for now I'll just say the easiest thing is to make them siblings, because if the parent also has a collider, then all child colliders will be compounded inside it, resulting in the parent entity being returned as an entry, exit and/or overlap. I have an open GitHub issue I'm triaging that should eventually resolve that issue.

    Find a problem or have a suggestion? Submit a pull request or issue and keep it constructive.
     
    Last edited: Feb 8, 2021
    Deleted User likes this.
  19. reeseschultz

    reeseschultz

    Joined:
    Apr 1, 2018
    Posts:
    21
    Another thing I think should exist by default is the ability to get the root entity from any child. Is it redundant? I guess, but I don't want to check for a parent of a parent of a parent and so on. That's just annoying.

    So if you install my utility package, all children of an entity instantiated from a prefab at runtime will have a
    Root
    component with the singular value being the root entity. If you have a different/better way of achieving that, great, but regardless having the reference is critical for any serious game logic. I run jobs that only apply for children (they don't run per the root), but sometimes I need information regarding the root entity's state.

    This, I think, also drastically improves the power of composition. With the expectation that all of your children have an immediately accessible root, you can then combine that knowledge with the fact that different kinds of components may apply for various different kinds of root entities, allowing for less code duplication overall.

    F̶i̶n̶a̶l̶l̶y̶,̶ ̶I̶ ̶s̶h̶o̶u̶l̶d̶ ̶m̶e̶n̶t̶i̶o̶n̶ ̶t̶h̶a̶t̶ ̶I̶'̶m̶ ̶c̶o̶n̶s̶i̶d̶e̶r̶i̶n̶g̶ ̶c̶r̶e̶a̶t̶i̶n̶g̶ ̶a̶ ̶B̶u̶r̶s̶t̶-̶c̶o̶m̶p̶a̶t̶i̶b̶l̶e̶ ̶l̶o̶g̶g̶i̶n̶g̶ ̶p̶a̶c̶k̶a̶g̶e̶ (never mind, I was informed there's support for Burst-compatible logging now). I've also pondered a
    DynamicMap
    , which would be analogous to a
    DynamicBuffer
    , except a HashMap per entity. This would make so many problems so much easier and performant to solve, and I have no idea why it doesn't exist yet (and no, the
    NativeMultiHashMap
    is not a suitable replacement in a discussion on usability). What would be great, though, is if Unity implemented these things instead of me. I thought after the navigation package I would stop making more packages, but... I can't help myself. I need adequate tools / APIs to make games.
     
    Last edited: Feb 5, 2021