Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Feedback Singleton Entities not ideal

Discussion in 'Entity Component System' started by LazyGameDevZA, Oct 29, 2020.

  1. LazyGameDevZA

    LazyGameDevZA

    Joined:
    Nov 10, 2016
    Posts:
    143
    I was recently reading through a Rust based ECS framework's documentation called SPECS and they had a very interesting idea for "singleton entity" data. They have made some very interesting design choices and there is also a focus on providing something that is a little more aware how to handle certain situations from a multi-threading perspective by leveraging some of the Rust language features.

    My feedback is based on the Unity Physics package and how it chooses to do certain things and I suspect it's because the Entities package singleton components work. I have previously experimented with ways to store system data in a singleton component in the same way a reference to a BlobAsset would be stored. This required quite a bit of gymnastics and I still ran into issues with the job scheduler that made things quite difficult. It also means that if I have some single item that might need to be updated I have to go through all of the gymnastics of doing the update on the main thread or going through the process of getting the entity so that an update can be made using an EntityCommandBuffer.

    SPECS takes a step back in this regard. Even though a "component" type is still created it's API names the storage mechanism a "Resource". There's heavy use of Rust's language features to prevent concurrency issues, but I feel that the API better hides systems from one another way better.

    This is especially prevalent in the Unity Physics package. If we want to do anything with the physics state that isn't stored in the EntityManager we have to inject other systems and access the physics state data as a property of those other systems. I don't quite feel this is a good way to go about it and I think creating a way to attach resources (or whatever Unity would name them) to the world would be a better approach. BlobAssets get very close with providing something that could work, but the immutability of a BlobAsset doesn't make it conducive to solving this problem. The main aim in my view is to have it intrinsically tied to the lifecycle of a World in the Entities package which should make maintaining and sharing system state easier to decouple from one another. The SPECS framework does make a different assumption that the programmer will have to take care in defining the system update order as well as initialization code for setting up this resource data, but I think offloading that responsibility to the system should be a fair compromise and it will maintain the usability for those that prefer an auto-bootstrap approach.
     
  2. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    Not sure if I understand everything, but are you basically asking for a way for things like PhysicsWorld to be just a component on a singleton Entity? Because then it becomes "officially" queryable through the same mecanisms as all other data in the ECS, and it prevents coupling between systems?

    If so then yes I agree that it would be a huge improvement, if such a thing is possible.

    But, if what you're suggesting is the possibility of creating another sort of singleton "World Data" which are different from components, I feel like there would be some redundancy here. Because that would be almost exactly the same as creating a system with no update that contains this data. Data in systems is data that's unique to a world, because there's only one of each system per world. Maybe it's just the word "System" that makes us think they aren't supposed to be used like that?

    Like, imagine if instead of PhysicsWorld living inside BuildPhysicsWorldSystem, it lived inside a system named "PhysicsData", which has no update. Wouldn't that be the same as having singleton data associated to your World?
     
    Last edited: Oct 29, 2020
    NotaNaN likes this.
  3. LazyGameDevZA

    LazyGameDevZA

    Joined:
    Nov 10, 2016
    Posts:
    143
    I'm suggesting something that's a little in between of what you're describing. Yes, I want to be able to query for some types of system state like the PhysicsWorld component, but I think tying it to the EntityManager is only hurting usability in some cases. If it's a singleton component, why should I care about the entity it's associated with?

    I don't think the mentality of "everything is an entity" in terms of data is a good idea. Clearly, it's not the case with the Physics package and they opted to do the workaround. The PhysicsWorld is an acceleration structure and I doubt it would work if it's data was stored and managed using the EntityManager. In SPECS there is the notion of an entity manager, but it's abstracted away behind the World. In Unity's Entities package we are much more focused on the EntityManager which leads to an API focused around entities and the components attached to them.

    I mentioned the fact that some parts of the game require an acceleration structure and there's usually a reason it's made, either for query performance or for writing performance. Untiy's Collections package serves as a very valuable resource to create these acceleration structures with, but sometimes my acceleration structure could be as simple as an array of data. If we follow the singleton component mentality that collection now has to be wrapped up into a component or the collection's items have to become components which brings with it the baggage of chunk storage.

    Going further with this if we take the example of a simple piece of singleton data like DeltaTime (not the way Unity implemented it, but for the sake of simplicity just a single struct that has the DeltaTime written to it on every frame). If I go and make it a component, it allows a programmer to add it to another entity, which starts breaking the singleton queries underneath when you use the easy-to-use
    GetSingleton<T> 
    methods available on a system.

    The SPECS book has a section dedicated to Resources here.

    The basis of what I want is as follows:
    • Easy and thread-safe read/write of single struct item struct based data (something with primitive fields only)
    • Ability to give a native collection to the world and tell it to use that as a piece of singleton data
    • Creation of a complex acceleration structure that could possibly contain other native collections along with their job handles for dependency management.
    The 3rd point is maybe a little hectic, but I feel the first 2 will already make things much simpler. If we stick with the "Resource" naming it could be as simple as a method on
    SystemBase
    that provides a
    GetResource<T>
    method with which to find it and ideally wrap it up in some structure that also provides the job handles if you do need to write changes to a resource.

    In a previous attempt at doing something like this I would create a component that would have a pointer to some native collection that served as my acceleration structure, but it added a LOT of boilerplate to get that pointer into a state where I could work with the collection and I had no idea how to handle job dependencies so that idea fell flat.
     
    davenirline and unity-freestyle like this.
  4. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    Oh I see, this clears things up

    It kind-of looks like attaching components to the world itself instead of to an entity. Like some kind of "World.GetComponent<MySingletonThing>()" or "World.SetComponent<PhysicsWorld>(myPhysicsWorld)". These components could be structs that implement "IWorldComponent" or something like that... and would be able to hold native collections. We could imagine that instead of a built-in "World.Time", it could become "World.GetComponent<Time>()" instead, etc...

    I do think there is something very finnicky about singleton Entities currently, because as you said, nothing stops you from creating more than one of them. So I'd agree that something like "world components" or "resources" would be much better

    _________

    EDIT:
    but at the same time, this also brings me back to my point on the similarity that these "world components"/"resources" would have with systems. Is there really much of a difference between:
    Code (CSharp):
    1. World.GetComponent<PhysicsWorld>().Bodies;
    and
    Code (CSharp):
    1. World.GetExistingSystem<PhysicsWorldSystem>().PhysicsWorld.Bodies;
    ?

    It does make things easier to use but still very similar in function. A system is basically a "singleton component" of the ECS World, which has the option of doing some logic as well. I think the QoL benefits of "world components" would still be very much worth it though
     
    Last edited: Oct 29, 2020
    NotaNaN and unity-freestyle like this.
  5. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    677
    This is what I do currently in my projects btw, I have my SingletonTag and created extension methods to the World class to have GetSingletonEntity (that gets my entity with SingletonTag), Get/SetSingletonData and Get/SetSingletonObject (to manipulate that entity's components
     
    unity-freestyle likes this.
  6. unity-freestyle

    unity-freestyle

    Joined:
    Aug 26, 2015
    Posts:
    45
    Seems like a nice approach indeed, to have this "World Data", which could be native containers as well, and with better support for handling the systems dependencies that access it.
     
    charleshendry likes this.
  7. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    2,029
    One thing I still dun understand why not make IComponentData able to put native container? Does it not possible for performance reason? Or introduce another new thing like INativeContainer to allow u to put native container and able to globally access just like IComponentData.
     
  8. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    677
    You can put NativeContainers on IComponentData if you make it a class
     
  9. LazyGameDevZA

    LazyGameDevZA

    Joined:
    Nov 10, 2016
    Posts:
    143
    I think the main thing I'm envisioning is adding that layer of job safety over it as well. Right now the Dependency property of a system is marked as protected (although I don't think it should be a problem if you access it from within another system that inherits from SystemBase). It boils down to an element of discipline then. I can't use just the data in what I'm building because the data is intrinsically coupled with the system, or rather it can be. In the Physics package, the data structure is associated with a system that does the processing of the structure.

    In the Unity Physics example the big plus that's touted is that it shares it's data types with the Havok package. This is where things get quite loopy, because I would've envisioned that data as a separate shared package that both physics packages could depend on and we could then easily have other people build physics packages on top of this shared data type package (like somebody integrating ODE and being able to switch out physics engines without needing to jump through hoops).

    It also means that if we store singleton data in a system we bring over whatever processing that system might bring with it. Usually one would want that, but you then also run the risk of exposing methods on that system that does data processing and can be called from anywhere. It again becomes a discipline thing and I feel an API that can handle the dependencies aspect a little more subtly might be very valuable.

    Overall I'll just be happy if the singleton API can get some quality of life improvements that don't require me to do too much boilerplate.

    While this is an option, I don't think it's a good option. We need better ways of handling sharing special container types between systems in a way that makes things simple enough to use without causing issues like jobs freaking out about reference types or the scheduler or passing around job handles without having to have a degree in multi-threading.
     
    unity-freestyle, NotaNaN and PhilSA like this.
  10. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,983
    I find that it is generally not a good idea to have public singleton state on a system unless it is both transient and self-consuming. Additionally, the state should be exposed publicly at the top level system of the module. If that module is a single system, it is exposed by the system. If that module uses a group of systems, then the API should be on a ComponentSystemGroup or some non-updating system. EntityCommandBufferSystem meets these rules, which is why it kinda just works without much issue. Physics does not follow these rules, which is why there is significantly more confusion here on the forums (fortunately, they have kept the fluctuation of the number of systems and where things are stored down to a minimum or else it would be a much bigger problem).

    As for singletons and other containers, I'll just share the relevant documentation for the solution I came up with that I find works and feels a lot better:
    https://github.com/Dreaming381/Latios-Framework/blob/v0.2.0/Documentation~/Core/Global Entities.md
    https://github.com/Dreaming381/Lati...e/Collection and Managed Struct Components.md
     
    LazyGameDevZA likes this.