Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Two clarifications about GetSingleton

Discussion in 'Entity Component System' started by Rs, Sep 7, 2019.

  1. Rs

    Rs

    Joined:
    Aug 14, 2012
    Posts:
    74
    1. Does it also create an entity/singleton? I observed that if I call it AND also 'manually' create an entity, I get an error saying there are more than 1 entities of that type.
      Code (CSharp):
      1.  
      2. EntityArchetype MySingleEntityArchetype = entityManager.CreateArchetype(typeof(MySingleEntity))        
      3. Entity mySingleEntity = entityManager.CreateEntity(MySingleEntityArchetype);
      4. entityManager.SetComponentData(mySingleEntity, new MySingleEntity());
      5.  
      And somewhere else
      Code (CSharp):
      1. mySingleton = resultGroup.GetSingleton<MySingleEntity>();
      I get this:
      Code (CSharp):
      1. InvalidOperationException: GetSingleton<MySingleEntity>() requires that exactly one MySingleEntity exists but there are 2.
      I deduce that GetSingleton also creates one entity of the requested type, am I right?

    2. I'm also guessing that GetSingleton allocates a NativeArray under the hood because if I invoke it in the OnCreate method I get a memory leak warning. Is this true?
      Here's the OnCreate method
      Code (CSharp):
      1.  resultGroup = GetEntityQuery(typeof(MySingleEntity));
      2. mySingleEntity = resultGroup.GetSingleton<MySingleEntity>();

      EDIT: So the second point kinda resolved itself by restarting Unity. Weird. Then I had an error saying that the I have 0 Entities of that type when calling it. I moved GetSingleton to the OnUpdate method and it works fine. I still would like to know for sure whether it allocates a NativeArray or not. But most importantly: what if I need this in the OnCreate context? (As I do)
     
    Last edited: Sep 7, 2019
  2. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    No to both questions. See the below unit tests. The Singleton APIs are fairly straight-forward convenience methods with some count checks on them. There are some tricky parts such as knowing when to use the
    EntityQuery
    set of singleton methods vs the
    ComponentSystem
    ones, but they will become apparent once you just dig into the source code.

    Also in some cases, it's easier and more flexible to just use
    GetSingletonEntity()
    and then work on the entity.

    Code (CSharp):
    1. namespace Tests {
    2.     public class SingletonComponentTests : ECSTestsFixture {
    3.         [Test]
    4.         public void GetSetSingleton() {
    5.             var entity = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2));
    6.  
    7.             EmptySystem.SetSingleton(new EcsTestData(10));
    8.             Assert.AreEqual(10, EmptySystem.GetSingleton<EcsTestData>().value);
    9.         }
    10.  
    11.  
    12.         [Test]
    13.         public void GetSetSingleton1() {
    14.             m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2));
    15.             var query = EmptySystem.GetEntityQuery(typeof(EcsTestData), typeof(EcsTestData2));
    16.             query.SetSingleton(new EcsTestData(10));
    17.             Assert.AreEqual(10, query.GetSingleton<EcsTestData>().value);
    18.         }
    19.  
    20.  
    21.         [Test]
    22.         public void GetSetSingleton2() {
    23.             m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2));
    24.             var query = EmptySystem.GetEntityQuery(typeof(EcsTestData), typeof(EcsTestData2));
    25.             var entity = query.GetSingletonEntity();
    26.             m_Manager.SetComponentData(entity, new EcsTestData2(10));
    27.             Assert.AreEqual(10, m_Manager.GetComponentData<EcsTestData2>(entity).value1);
    28.         }
    29.  
    30.         [InternalBufferCapacity(8)]
    31.         public struct MyElement : IBufferElementData {
    32.             public static implicit operator int(MyElement e) { return e.Value; }
    33.             public static implicit operator MyElement(int e) { return new MyElement { Value = e }; }
    34.             public int Value;
    35.         }
    36.  
    37.         [Test]
    38.         public void SingletonBuffer() {
    39.             var entity = m_Manager.CreateEntity(typeof(MyElement));
    40.             var buffer = m_Manager.GetBuffer<MyElement>(entity);
    41.             buffer.Add(123);
    42.  
    43.             var query = EmptySystem.GetEntityQuery(typeof(MyElement));
    44.             var singletonEntity = query.GetSingletonEntity();
    45.             var singletonEntityBuffer = m_Manager.GetBuffer<MyElement>(singletonEntity);
    46.  
    47.             Assert.AreEqual(1, singletonEntityBuffer.Length);
    48.             Assert.AreEqual(123, singletonEntityBuffer[0].Value);
    49.         }
    50.  
    51.         [Test]
    52.         public void GetSetSingletonZeroThrows() {
    53.             Assert.Throws<InvalidOperationException>(() => EmptySystem.SetSingleton(new EcsTestData()));
    54.             Assert.Throws<InvalidOperationException>(() => EmptySystem.GetSingleton<EcsTestData>());
    55.         }
    56.  
    57.         [Test]
    58.         public void GetSetSingletonMultipleThrows() {
    59.             m_Manager.CreateEntity(typeof(EcsTestData));
    60.             m_Manager.CreateEntity(typeof(EcsTestData));
    61.  
    62.             Assert.Throws<InvalidOperationException>(() => EmptySystem.SetSingleton(new EcsTestData()));
    63.             Assert.Throws<InvalidOperationException>(() => EmptySystem.GetSingleton<EcsTestData>());
    64.         }
    65.  
    66.         [Test]
    67.         public void RequireSingletonWorks() {
    68.             // RequireSingletonForUpdate will normally be called from OnCreateManager
    69.             EmptySystem.RequireSingletonForUpdate<EcsTestData>();
    70.  
    71.             EmptySystem.GetEntityQuery(typeof(EcsTestData2));
    72.  
    73.             m_Manager.CreateEntity(typeof(EcsTestData2));
    74.             Assert.IsFalse(EmptySystem.ShouldRunSystem());
    75.             m_Manager.CreateEntity(typeof(EcsTestData));
    76.             Assert.IsTrue(EmptySystem.ShouldRunSystem());
    77.         }
    78.     }
    79. }
     
    Rs likes this.