Search Unity

Conway's Games of Life in ECS with source

Discussion in 'Data Oriented Technology Stack' started by ThinkHuge, Jun 12, 2019.

  1. ThinkHuge

    ThinkHuge

    Joined:
    Feb 10, 2014
    Posts:
    22
    I've re-created Conway's Game of Life using Unity ECS
    https://en.wikipedia.org/wiki/Conway's_Game_of_Life

    Playable webgl demo
    https://thinkhuge.s3-us-west-2.amazonaws.com/gol/index.html

    Source code
    https://github.com/areilly711/unity_ecs

    ECS Part
    Game logic

    Mono Part
    App startup
    UI

    My issues
    1) My cell entities have 8 neighbor cells stored as entities in an IComponentData struct. When I was counting a cell's neighbors on the main thread, I was using EntityManager.GetComponentData<Neighbor> on the neighbor entities. However, when I jobified he neighbor counting, I haven't been able to find a reliable way to do the lookup on the neighbor entity into its proper array. As a temp hack, the first entities that I create are the cells (say 10,000, so indices 0-9999), and when I run the neighbor counter system, I create an entity query for my cells and transform the results into a native array of size 10,000, so I can do the lookup. However, when I restart the app and destroy all the entities, the newly created cell entity ids don't match the same 0-9999 range as before and I get errors, as expected.

    I've tried chunk iteration, but this didn't work because the neighbor can be in a different chunk.
    I also tried combining component data from chunks in a native array, but that didn't work either. I tried to reset the World entity count back to 0, but there doesn't seem to be a way to do this. How can I find the proper array that stored the neighbors?

    2) Speaking of neighbors, instead of storing 8 separate entities in the data struct and doing random array access lookups on these entities in the job, is there a more cache friendly method? The counter system is actually quite fast, but I'm trying to think of different ways to solve this problem.

    3) The render system is my bottleneck. I'm using the standard Rendermesh/Translation/Scale/LocalToWorld setup to perform the rendering. Can I optimize using the Graphics class or some other method?

    General ECS Feedback
    1) Before setting the cells' scales to 0, I was attempting to swap between 2 different shared Rendermesh Components, but couldn't find a way to do this in a job and it was painfully slow on the main thread

    2) Reloading the scene doesn't destroy entities or systems. I was surprised but I suppose I shouldn't have been

    3) The samples and documentation helped a lot

    4) Overall, I like the ECS as it's a nice, elegant way to separate data from functionality and write really easy to read, performant code. It will be interesting to see how the API evolves over time. For example, I wonder if we'll always have to manually dispose of our arrays.
     
    Last edited: Jul 15, 2019
    GliderGuy, starikcetin and alexnown like this.
  2. francois85

    francois85

    Joined:
    Aug 11, 2015
    Posts:
    633
    I’ve always found Conways game of life fascinating.. nice job.
     
  3. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    1,408
    1) DynamicBuffers
    2) Look at 1)
    3) Hybrid Renderer is new version for DOTS which now using instead of Graphic API.
     
  4. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    4,670
    CompoenentDataFromEntity<MyComponent> can be passed on a job and does the same as EntityManager.GetComponentData / EntityManager.SetComponentData

    It seems like beginners keep on not knowing that this exists. Anyone got an idea on what we are missing in order to change that? (Docs / samples / ?)
     
    Enzi likes this.
  5. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    90
    There should probably be a sample set up that just demonstrates how you expect people to access outside entities from inside an IJFE, seeing as it's such a common thing that is not demonstrated in the HelloCube examples.

    It also wouldn't hurt to link to the CDFE docs from https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/chunk_iteration.html

    And TBH you should probably link directly to the samples from the overview page of the ECS manual, since that is by far the most comprehensive and up to date tutorial available right now.
     
    francois85 and jdtec like this.
  6. jdtec

    jdtec

    Joined:
    Oct 25, 2017
    Posts:
    86
    I think a very basic small game sample project could go a long way. There used to be one didn't there?

    At the moment there is cubes been transformed, a cool super optimised but kind of single focussed boid demo and the FPS project which is overwhelming and I'm not sure uses best practices for some DOTS things anyway?

    I find it confusing sometimes that some of the docs/info on DOTS is in GitHub docs vs Unity documentation pages.

    I use Buffer/ComponentDataFromEntity & [NativeDisableParallelForRestriction] LOTS, however, these are no where to be seen in the samples?

    In comparison with some things that are in samples; I haven't used ForEach, non-job ComponentSystems, anything that converts from GameObjects, Subscenes and I have just one or two uses of IJobChunk. I'm not saying these aren't useful but might be a good idea to see some heavier use of CDFE & [NDPFR] in some samples?
     
  7. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,263
    The type name sounds like a method, at first I also thought it's a kind of shortcut so I think I "use" it rather than creating an instance of it. (so not keeping the instance) Then when I want to do that in a job, the first thing I thought is that how could I get EntityManager into a job rather than just put that in a job. I was teaching someone months ago and it is weird to say "put ComponentDataFromEntity into a job" because the thing doesn't sounds like a tangible object but a verb.

    Then, without knowing inner of how the job need to know its dependency for read/write, it is not obvious why ceremony to create it from outside and pass to the job is needed. Where other things seems to belongs to the job, like ref args from IJFE (even though it came from system's entity query, so actually it belongs to the outside) or explicitly put in a job like NativeArray and you feel like you really put something in, putting CDFE in feels like you are trying to add an extension method to a job where it is hiding in an object. Imagine if CDFE could appear as a job field like old style [Inject], even if that is not efficient or magical, it would be more understandable that "the job wants to access this kind of component data from any entity".

    It is also weird that it has "from" in its name but it could be written "to". Putting [ReadOnly] in front of this type name in a job also give me unintuitive feeling.

    "Get"ComponentDataFromEntity() method on EM sounded weird too. It sounds like this method "get component data from [entity]" but it actually "get a new instance of [component data from entity]". If you interpret it as the former it hides the fact that there is an object that could be put in a job returned.

    When I know what I am doing using the indexer to both read and write is nice, but for beginner if the object was named something like ComponentAccessor and is forced to use .Get/.Set it would be more obvious, then maybe when scheduling a job the method to make it would be called "Create"Accessor(/ByEntity)
     
    Last edited: Jun 12, 2019
  8. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    229
    Thanks for sharing!
     
  9. jooleanlogic

    jooleanlogic

    Joined:
    Mar 1, 2018
    Posts:
    332
    I wholly agree with this on the naming side. I think the verbosity of 'ComponentDataFromEntity' gets in the way for something that has such frequent use and whose purpose is very simple. Also given that you can write to it and it only takes Entity as a parameter, the whole 'FromEntity' seems a bit counter-intuitive and redundant.
    ComponentAccessor or something short along that vein (though I can't think of anything better) would make it clearer imho. I'm far from a naming expert though. :)
     
  10. Enzi

    Enzi

    Joined:
    Jan 28, 2013
    Posts:
    199
    I didn't know the use of ComponentDataFromEntity either until I looked at chunk iteration and some older samples.
    Newer samples use GetArchetypeChunkComponentType

    In that sense, I guess the samples are too practical. Sometimes I just want to know my options so throw in as many types of ways to get data and comment it for what they can be used and how fast they are in comparison.

    Which manual should we follow now?
    I use this: https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual
    I couldn't find a mention of ComponentDataFromEntity when clicking through the articles.
    There is an unlinked article though that can be found via search: https://docs.unity3d.com/Packages/c.../ecs_in_detail.html?q=ComponentDataFromEntity

    Before derailing the thread.
    When working on grids, wouldn't a Hashtable make more sense instead of components or a dynamicbuffer?
     
  11. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    276
    What about just ComponentLookup, op does mention wanting to do a lookup and that's probably how I would've gone about phrasing how to get data from a specific entity.
     
  12. jooleanlogic

    jooleanlogic

    Joined:
    Mar 1, 2018
    Posts:
    332
    That's what I initially used to call all my CDFE's but I think accessor is more succinct, not that I ever thought of that word till 5argon mentioned it. In any event, something other than CDFE I think.
    Code (CSharp):
    1. // Implies you want to get component data for an entity from that function call.
    2. GetComponentDataFromEntity<Type>(*entity*)
    3.  
    4. // implies you're going to get back something with which you can access a component.
    5. GetComponentAccessor<Type>()
    Sorry for off topic posts. Joachim started it. :D
     
    RaL and 5argon like this.
  13. ThinkHuge

    ThinkHuge

    Joined:
    Feb 10, 2014
    Posts:
    22
    Thanks, that solved my problem. For me, the naming and parameters were the confusing part. EntityManager.GetComponentData is clear because it takes an Entity param and returns my specified type of component data. Whereas GetComponentDataFromEntity makes less sense because it doesn't return a single entity's data and it doesn't take an entity argument. The function name is written as though it's for a single entity but it's plural. Furthermore, even though it is plural, it's not returning an array. Instead returns a single struct which I can index (which also isn't apparent because the overloaded [] operator doesn't show up in visual studio intelisense)

    The name "GetComponentDataLookupTable" for the function and ComponentDataLookupTable for the struct would be clearer for me. On the struct, I would have the [] overloaded and I would have a function GetComponentData(entity)
     
  14. ThinkHuge

    ThinkHuge

    Joined:
    Feb 10, 2014
    Posts:
    22
    I should have been more clear. I am using the Hybrid render (see screenshot).

    I'm looking for ways to optimize the rendering.
     

    Attached Files:

  15. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    1,408
    It's numbers with safety checks disabled?
     
  16. ThinkHuge

    ThinkHuge

    Joined:
    Feb 10, 2014
    Posts:
    22
    That made a big difference, thank you.