Search Unity

Singleton components.

Discussion in 'Entity Component System' started by wusticality, Jun 9, 2018.

  1. wusticality

    wusticality

    Joined:
    Dec 15, 2016
    Posts:
    71
    Hey folks,

    What is the best way to have one off components? For example, a component with references to game settings, loaded assets, etc. It seems like ISharedComponentData is not the right option as it's used to share data between multiple entities. In other ECS systems, there's sometimes the notion of a singleton component - that is to say, a component that only ever has one instance. What is the canonical way to do this with this system?

    Thanks, -gormulent
     
  2. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    328
    You could do that in Shared, and just have the same shared component passed around. If there is ever only one data set of it, then in practice it would be a singleton
     
  3. vanxining

    vanxining

    Joined:
    Mar 29, 2018
    Posts:
    20
    For such cases, why not use a C# managed (not ECS component) singleton object?
     
  4. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    In my game

    - I don't think ISharedComponentData is the solution (or even its intended use). Imagine if you tag every entity with this "singleton" data and what if you want to update this data? You have to loop through all entities to change them. By definition ISharedComponentData is meant to be a "group by value". It groups all entities with the same IShared value to the same chunk. And by attaching the same ISharedComponentData to everything does not make much sense.. (It is equivalent to group everything in 1 same group, just so that everything collectively know its own group's name)

    And it should change rarely as changing it requires moving all entities to a different chunk.

    - I made a static Entity with required one-off components at the start. Then when I want it I use EntityManager.Get/SetComponentData<T>(MySingletons.SingletonEntity). Or alternatively [Inject] and use index [0].


    - The added benefit is that a data tied to an entity can show up in ECS systems more naturally. Mainly [Inject], but the most useful for me is that it is usable with IJobProcessComponentData. I can process and transform various singleton datas as I like as all of them are attached to a single entity it would be in the same loop of IJobProcessComponentData
    - If you [Inject] to ComponentDataArray (even though it will has only 1 member) and use it with JobComponentSystem, it can be automatically depends on via the inputDeps system if you put it in a job and schedule it. Also, normal C# static variables could not be accessed from inside the job in the future.
     
    arielsan and vanxining like this.
  5. wusticality

    wusticality

    Joined:
    Dec 15, 2016
    Posts:
    71
    Because real singletons are evil and the source of so much pain and grief. ;)
     
  6. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    Why not just use a system and inject that instead of a component?
     
  7. wusticality

    wusticality

    Joined:
    Dec 15, 2016
    Posts:
    71
    Is there a way to query for systems from the World / EntityManager? I'd have to actually query the system and populate it with data, using singletons / globals is just a non-option (because it's awful).
     
  8. zulfajuniadi

    zulfajuniadi

    Joined:
    Nov 18, 2017
    Posts:
    117
    IEnumerable World.Active.BehaviourManagers
     
  9. wusticality

    wusticality

    Joined:
    Dec 15, 2016
    Posts:
    71
    Thanks for this, I'll check it out. I'm sort of torn between using their ECS and just using their job system so I have total power.
     
  10. vanxining

    vanxining

    Joined:
    Mar 29, 2018
    Posts:
    20
    World.Active.GetExistingSystem<MySystem>() can help.
    EntityManager is just another normal system as your custom ones.
     
    ChiuanWei and zulfajuniadi like this.
  11. Matsuguma

    Matsuguma

    Joined:
    Aug 7, 2017
    Posts:
    11
    I think I may have come up with a good way to make a SingletonComponent :D

    The issue I have with @5argon's approach and other approaches I've seen is that they involve injecting the singleton component into every system we want to use it in. As a result, these systems would always run, even if the other injected groups were not matched. Of course, we could just do an early exit on OnUpdate, but it seemed to me like we could do better.

    So I found out that an injected system does not count as condition to run a system. So I injected the singleton component into a system A. Then I injected system A into every system that needed the singleton component, and they can access it through a public variable in system A. Like this:
    Code (CSharp):
    1. public struct SingletonComponent : IComponentData {}
    2.  
    3. public class MySystem : ComponentSystem
    4. {
    5.     struct Group
    6.     {
    7.         public ComponentDataArray<SingletonComponent> Components;
    8.         public EntityArray Entities;
    9.     }
    10.  
    11.     [Inject] Group group;
    12.     public SingletonComponent Singleton;
    13.    
    14.     protected override void OnUpdate()
    15.     {
    16.         Singleton = group.Components[0];
    17.     }
    18. }
    19.  
    20. public class MyOtherSystem : ComponentSystem
    21. {
    22.     [Inject] MySystem system;
    23.  
    24.     protected override void OnUpdate()
    25.     {
    26.         // do something with: system.Singleton
    27.     }
    28. }
    29.  
    This way only the necessary systems run and it is easy to access the component from any other system.
     
    ChiuanWei, vanxining, floboc and 2 others like this.
  12. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Great, now I can also move singleton initializing logic to the system.
     
  13. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,154
    Does the Singleton assignment need to be in onupdate? Or does oncreate also work
     
  14. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    OnCreate works but the problem is OnCreateManager's order depends on which system comes first from the assembly reflection. If you have dependency to this singleton in multiple OnCreateManager it might cause bugs. For me I put them in OnUpdate then set this.Enabled = false; at the last line so it run once. Then attach [UpdateBefore(typeof(Initialization))] to ensure this update comes first. (Alternatively use OnStartRunning depending on use case)
     
    Deadcow_ likes this.
  15. Deadcow_

    Deadcow_

    Joined:
    Mar 13, 2014
    Posts:
    135
    I've got a problem with assignment in OnCreateManager or with OnStartRunning.
    Seems like Injection is not happened yet at this stage and "MyComponent = _group.Components[0];" produce index out of range exception. It works fine in OnUpdate, but I want to use MyComponent in initialization in some other systems.

    You guys saying OnCreateManager works for you? It may be the best case to init such data in OnCreate and use it in OnStartRunning of other systems:confused: But yeah, seems like data is not injected at this point :(

    Also it may be just luck for me, but it seems like UpdateBefore affects OnCreateManager and OnStartRunning execution order. I shouldn't rely on that?
     
  16. Deadcow_

    Deadcow_

    Joined:
    Mar 13, 2014
    Posts:
    135
    wait what, according to documentation - "... automatically injected into the injected variables before OnCreateManager, OnDestroyManager and OnUpdate". Looks like a bug :eek:
     
  17. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    I remembered I have to call UpdateInjectedComponentGroups() to force the injection. But sometimes that does not work in OnCreateManager only the earliest on OnStartRunning? Not sure. Maybe it injects but the entity itself is not ready yet.
     
    Deadcow_ likes this.
  18. Deadcow_

    Deadcow_

    Joined:
    Mar 13, 2014
    Posts:
    135
    Thanks! UpdateInjectedComponentGroups did the trick.
     
  19. Skolwind

    Skolwind

    Joined:
    Aug 28, 2014
    Posts:
    8
    If you need convenient access to component, you can directly reference Entity which component is attached to.

    Code (CSharp):
    1. public struct SingletonData : IComponentData { int value; }
    2.  
    3. public class MySystem : ComponentSystem
    4. {
    5.     struct Group    {   /* ...*/  }
    6.     [Inject] Group group;
    7.  
    8.     public Entity singletonEntity;
    9.  
    10.     protected override void OnUpdate()
    11.     {
    12.         var singleton = EntityManager.GetComponentData<SingletonData>(singletonEntity);
    13.         //...
    14.     }
    15. }
    Somewhere else you create singletonEntity and assign it
     
  20. Deadcow_

    Deadcow_

    Joined:
    Mar 13, 2014
    Posts:
    135
    I mostly use singletons for settings sharing, mostly through inspector.
    So I don't need SetUpSystem for those. I ended up with this wrapper for now

    Code (CSharp):
    1. public struct UniqueComponent<T> where T : UnityEngine.Component
    2. {
    3.     public ComponentArray<T> Components;
    4.  
    5.     public T Instance => Components[0];
    6. }
    7.  
    8.  
    9. public struct UniqueComponentData<T> where T : struct, IComponentData
    10. {
    11.     public ComponentDataArray<T> Components;
    12.  
    13.     public T Instance => Components[0];
    14. }
    and usage

    Code (CSharp):
    1. [Inject] private UniqueComponent<LayersSettingsComponent> _layer;
    2.  
    3. ...
    4.  
    5. var gl = _layer.Instance.GroundLayer;
     
    LukePammant, swejk and vanxining like this.
  21. capyvara

    capyvara

    Joined:
    Mar 11, 2010
    Posts:
    80
    Maybe add some implicit operator so you don't even need _layer.Instance, just _layer ?

    Code (CSharp):
    1. public static implicit operator T(UniqueComponent<T> unique)
    2. {
    3.     return unique.Instance;
    4. }
    5.  
     
    Seb-1814 likes this.
  22. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    With [Inject] going away, what will your approach to singletons be now?

    It seems like Skolwin’s approach to just store the data directly in Systems (or to just have systems reference singleton data from outside of ECS) might be the simplest solution. Thoughts?
     
  23. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Code (CSharp):
    1.     public class SingletonTests : ECSTestsFixture
    2.     {
    3.         [Test]
    4.         public void GetSetSingleton()
    5.         {
    6.             var entity = m_Manager.CreateEntity(typeof(EcsTestData));
    7.  
    8.             EmptySystem.SetSingleton(new EcsTestData(10));
    9.             Assert.AreEqual(10, EmptySystem.GetSingleton<EcsTestData>().value);
    10.         }
    11.      
    12.         [Test]
    13.         public void GetSetSingletonZeroThrows()
    14.         {
    15.             Assert.Throws<InvalidOperationException>(() => EmptySystem.SetSingleton(new EcsTestData()));
    16.             Assert.Throws<InvalidOperationException>(() => EmptySystem.GetSingleton<EcsTestData>());
    17.         }
    18.      
    19.         [Test]
    20.         public void GetSetSingletonMultipleThrows()
    21.         {
    22.             m_Manager.CreateEntity(typeof(EcsTestData));
    23.             m_Manager.CreateEntity(typeof(EcsTestData));
    24.  
    25.             Assert.Throws<InvalidOperationException>(() => EmptySystem.SetSingleton(new EcsTestData()));
    26.             Assert.Throws<InvalidOperationException>(() => EmptySystem.GetSingleton<EcsTestData>());
    27.         }
    28.      
    29.         [Test]
    30.         public void RequireSingletonWorks()
    31.         {
    32.             // RequireSingletonForUpdate will normally be called from OnCreateManager
    33.             EmptySystem.RequireSingletonForUpdate<EcsTestData>();
    34.  
    35.             EmptySystem.GetComponentGroup(typeof(EcsTestData2));
    36.          
    37.             m_Manager.CreateEntity(typeof(EcsTestData2));
    38.             Assert.IsFalse(EmptySystem.ShouldRunSystem());
    39.             m_Manager.CreateEntity(typeof(EcsTestData));
    40.             Assert.IsTrue(EmptySystem.ShouldRunSystem());
    41.         }
    42.     }
    Will be in next release.
     
    Last edited: Dec 22, 2018
    ChiuanWei, Kirsche, eizenhorn and 5 others like this.
  24. ChiuanWei

    ChiuanWei

    Joined:
    Jan 29, 2012
    Posts:
    131
    Inject is good to use for a big project ?
     
  25. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    [Inject] is going to be removed. We don't recommend using it.
     
    ChiuanWei, FROS7 and hippocoder like this.
  26. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    Thanks Joachim!

    Question: In your example, you create a new EcsData Component when you set the singleton:

    Code (CSharp):
    1. EmptySystem.SetSingleton(new EcsTestData(10));
    1. Is the intention here that the singleton is unique to that System?
    2. Will there be a way to use the same Component as a singleton across multiple systems?
     
    Last edited: Dec 23, 2018
  27. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    No there is no reason for a singleton that is unique per system. That would be called a member variable.
     
    orionburcham likes this.
  28. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    Well, that makes sense!
    I think my confusion came from this line:

    Code (CSharp):
    1. EmptySystem.SetSingleton(new EcsTestData(10));
    That looks like you're creating a new EcsTestData component, and assigning it as a singleton to EmptySystem. Likewise, I would expect this code to create two different Components, one unique to each System:

    Code (CSharp):
    1. System01.SetSingleton(new EcsTestData(10));
    2. System02.SetSingleton(new EcsTestData(10));
    If the singleton Component wasn't unique to the System, I would expect the API to be something like this:

    Code (CSharp):
    1. var entity = m_Manager.CreateEntity(typeof(EcsTestData));
    2.  
    3. EmptySystem.SetSingleton<EcsTestData>(entity);
    No doubt, I'm just misunderstanding your example. Would you (or anyone) mind telling me what I'm missing?

    Thanks, I'm very happy a solution is on the way.
     
    Last edited: Dec 24, 2018
  29. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I suspect it's because of safety checks. For example using the JobComponentSystem.GetComponentDataFromEntity instead of the EntityManager version.
     
    orionburcham likes this.
  30. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    exactly
     
    orionburcham likes this.
  31. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    OK, that makes sense, and thank you. ...I hate to say it, but I'm unfortunately still confused. Let's say we called this:

    Code (CSharp):
    1. EmptySystem.SetSingleton(new EcsTestData(10));
    How would another system go about reading or writing to that same singleton EcsTestData?

    I'm assuming that EcsTestData is an IComponentData struct. In that case, wouldn't this code...

    Code (CSharp):
    1. EcsTestData myEcsTestData = new EcsTestData(10);
    2.  
    3. System01.SetSingleton(myEcsTestData);
    4. System02.SetSingleton(myEcsTestData);
    result in System01 and System02 each having a unique EcsTestData, both of which are also unique to "myEcsTestData"?

    I know this is the most basic of points, so I'm sure I missing something very obvious. :| I hope I'm not wasting anyone's time with something boneheaded.
     
    Last edited: Dec 24, 2018
  32. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I don't have access to this so this is speculation but,

    No, system 02 would be overriding the value System01 set the singleton.

    The singleton value isn't per system, it's probably per world. (Could be global instead of per world, but per world makes more sense to me.)

    System01.SetSingleton and System02.SetSingleton both 'reference' the same entity.
     
  33. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    But EcsTestData is a struct. Structs can’t be passed by reference in C#. The EcsTestData passed into System01 and System02 would be two different EcsTestDatas.
     
  34. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    And then they're both passed to the same entity manager

    If you call setcomponentdata on an entity in system01 and setcomponentdata on the same entity in system02, what happens?

    Compare to what we'd previewed above.
    Look at the example, the entity still needs to be created at the start.

    var entity = m_Manager.CreateEntity(typeof(EcsTestData));

    So doing

    m_Manager.setcomponentdata(entity, default(ecstestdata)) ;

    And

    System.setsingleton(default(EcsTestData));

    Is probably going to do very similar things except setsingleton is going to have checks to ensure that only a single version exists of this component. This is why you don't need to have the entity, there can only be one anyway so there is no confusion of the entity you are setting the component data of.

    (sorry about formatting, it's christmas and sent in phone )
     
  35. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    Ok, I think I get it. Thank you for going to the trouble of explaining it.

    There were some requirements here I didn’t understand. For example, I hadn’t guessed that EcsTestData was a component type written specifically to be this Singleton.

    I figured the Singleton solution would support Component types that could also exist on other entities. And that the Singleton would just be one ‘instance’ of the Component which we designated should be used as a singleton. I guessed that since that’s the way the Singleton pattern usually works: you can have more than one ‘MyType’ instance, for example, with only one MyType assigned as a Singleton somewhere.

    But if I understand you correctly, There could only ever be one entity with an EcsTestData in this World. Correct?
     
  36. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I think it'll work with any component, don't think you'll need specific components but yes I believe there will only ever be 1 entity with EcsTestData.

    Again just my speculation.
     
  37. Fido789

    Fido789

    Joined:
    Feb 26, 2013
    Posts:
    343
    Can we get something similar for DynamicBuffer?
     
  38. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    There's nothing special about these singletons, it's just a regular entity with a length check on the group.

    Code (CSharp):
    1.         public T GetSingleton<T>()
    2.             where T : struct, IComponentData
    3.         {
    4.             var type = ComponentType.Create<T>();
    5.             var group = GetComponentGroupInternal(&type, 1);
    6.             var array = group.GetComponentDataArray<T>();
    7.             #if ENABLE_UNITY_COLLECTIONS_CHECKS
    8.             if (array.Length != 1)
    9.                 throw new System.InvalidOperationException($"GetSingleton<{typeof(T)}>() requires that exactly one {typeof(T)} exists but there are {array.Length}.");
    10.             #endif
    11.  
    12.             group.CompleteDependency();
    13.             return array[0];
    14.         }
    You can implement a buffer version yourself really easy.

    Code (CSharp):
    1.         protected override void OnUpdate()
    2.         {
    3.             var group = this.GetComponentGroup(typeof(MyBuffer));
    4.             var array = group.GetBufferArray<MyBuffer>();
    5.             #if ENABLE_UNITY_COLLECTIONS_CHECKS
    6.             if (array.Length != 1)
    7.                 throw new System.InvalidOperationException($"Singleton<{typeof(MyBuffer)}>() requires that exactly one {typeof(MyBuffer)} exists but there are {array.Length}.");
    8.             #endif
    9.  
    10.             group.CompleteDependency();
    11.             var singletonBuffer = array[0];
    Which you could wrap on your own method if you really use it a lot.
     
    timmehhhhhhh likes this.
  39. Fido789

    Fido789

    Joined:
    Feb 26, 2013
    Posts:
    343
    Yes @tertle , that is possible. But I would like to have it implemented in the framework.

    I actually stopped working on my ecs game a few months ago because it required me to write a lot of code for a very simple things. Now I am evaluating it from time to time if it is mature enough to allow me to write the game and not the framework.

    I will probably get back when Boolean will be invented.
     
  40. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    340
    Code (CSharp):
    1. SingletonHolderEntity1
    2. + SingletonComponentA
    3. + SingletonComponentB
    4. + SingletonComponentC
    5.  
    6. SingletonHolderEntity2
    7. + SingletonComponentFoo
    8. + SingletonComponentBar
    Then simply query for the required component.

    Downsides:
    1. It is not lazily instantiated.
    2. Ensuring the singleton instance is tricky, but you can spam errors to the console if you find more than 1 instance.
     
  41. Ryetoast

    Ryetoast

    Joined:
    Mar 8, 2015
    Posts:
    48
    Why is this line required? It seems really arbitrary that I have to create an entity when you are going to be using it as a singleton component, without ever interacting with that entity again. I feel like one of the two following makes more sense to me:

    Add a singleton component directly, never treating it as related to an entity.
    Code (CSharp):
    1. EmptySystem.AddSingleton(new EcsTestData(10));
    or

    A specific component type for singleton components that can be get/set from the singleton APIs without having to manually create it.
    Code (CSharp):
    1. struct EcsTestData : ISingletonComponentData {
    2.     public int Value;
    3. }
     
  42. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Check out SingletonTests.cs. The current use is just system/cg.SetSingleton<T>. Basically how it works is that it just get an entity with that type from the world for you. Doing it on the system you are adding reader dependency because it creates a CG of single type, then get the entity. If you do it on the CG with multiple types, you get error. If you have more than 1 or 0 entity with that type you also get an error.
     
    Last edited: Mar 7, 2019
  43. Deleted User

    Deleted User

    Guest

    Does it work with SharedComponentData?
     
  44. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    No, the generic says IComponentData. Also it does not make sense since ISharedComponentData is not really belonging to anyone (it is associated with a chunk, but the data itself belongs to EntityManager). The singleton API's "singleton" means a single entity.

    *I may as well post this hidden page from my unfinished website : http://gametorrahod.com/ecs-singleton-data/ I am migrating everything along with discarding old ECS posts from https://gametorrahod.com that doesn't make sense anymore, then finally I will migrate the domain name.
     
    Last edited: Apr 9, 2019
    rsodre, Zoey_O, NotaNaN and 1 other person like this.
  45. timmehhhhhhh

    timmehhhhhhh

    Joined:
    Sep 10, 2013
    Posts:
    157
    any smart approaches for using this with dynamic buffers?
     
  46. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    I'm also interested in a native solution. A workaround seems not that easy at the moment. Because they removed GetBufferArray and some other stuff...
     
  47. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    It's not too bad right now to acquire a singleton Dynamic buffer:

    Code (CSharp):
    1. // OnCreate
    2. query = GetEntityQuery(typeof(MyElement));
    3.  
    4. // OnUpdate
    5. var entity = query.GetSingletonEntity();
    6. var elements = EntityManager.GetBuffer<MyElement>(entity);
    It is important to distinguish the differences between using EntityQuery's Singleton methods and using ComponentSystem's because the latter will append an extra query to the system's internal EntityQuery list which will keep the system running for as long as the singleton component is there. In most of my use cases at least, processing singleton components are an one-time thing. So having the extra query appended is more of a side-effect than a benefit.
     
  48. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    440
    ComponentSystem.GetEntityQuery will also add the query to the system's internal list, causing the same result as using ComponentSystem.GetSingletonEntity:

    https://forum.unity.com/threads/upg...ystem-no-longer-executes.672349/#post-4502326

    I'm still curious if there's a better way to go about this while avoiding this side effect, or a more "expected" design for sharing standalone arrays of data between systems.
     
  49. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    The difference is that in EntityQuery you can specify multiple ComponentTypes (with excludes) which effectively give you a way to selectively work on the Singleton once.

    Code (CSharp):
    1. public class ClientMapInfoInitSystem : ComponentSystem {
    2.  
    3.     EndECBSystem endSimBufferSystem;
    4.     EntityQuery query;
    5.  
    6.     protected override void OnCreate() {
    7.         query = GetEntityQuery(ComponentType.ReadOnly<MapInfo>(), ComponentType.Exclude<MapMeshBlocksCreated>());
    8.         endSimBufferSystem = World.GetOrCreateSystem<EndECBSystem>();
    9.     }
    10.  
    11.     protected override void OnUpdate() {
    12.         var cmdBuffer = endSimBufferSystem.CreateCommandBuffer();
    13.         var entity = query.GetSingletonEntity();
    14.         var mapInfo = EntityManager.GetComponentData<MapInfo>(entity);
    15.  
    16.         var width = 32;
    17.         for (int y = 0; y < mapInfo.Size / width; y++) {
    18.             for (int x = 0; x < mapInfo.Size / width; x++) {
    19.                 var blockEntity = cmdBuffer.CreateEntity(EntityManager.CreateArchetype(typeof(MapMeshBlock), typeof(VertexElement), typeof(NormalElement), typeof(TriangleElement)));
    20.                 cmdBuffer.SetComponent<MapMeshBlock>(blockEntity, new MapMeshBlock() { Coord = new Unity.Mathematics.int2() { x = x, y = y }, Width = width });
    21.             }
    22.         }
    23.  
    24.         cmdBuffer.AddComponent<MapMeshBlocksCreated>(entity, new MapMeshBlocksCreated());
    25.     }
    26.  
    27. }
    This system will only update once. But if ComponentSystem.GetSingletonEntity is used, then it will update forever because the single-component query will always be valid.

    The Singleton methods are really just convenience methods with some length checks and an append query side-effect.
     
    Last edited: May 8, 2019
  50. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    440
    Hah, seems obvious now that you've spelt it out for me. I still need to rework my brain to how problems are solved in the ECS world. Still seems odd to me that you can't create queries without implicitly affecting how a system updates but maybe I'm missing something there.

    Thank you though, that makes perfect sense.
     
    Singtaa likes this.