Would using entities and components as data model work?

I´m quite new to ECS/DOTS but I´ve used a package with entities and components as data model earlier.

Would anyone have tips on how to achieve that? How I think it could work is:

  • add also data only components as IComponentData, add them to “model” by using some kind of Spawner to access EntityManager? Or maybe I would have a System that would create the entities?

Data only components might not need Baker?

Example of “data only component” would be score. So it could be a singleton component, if something in game adds to score it needs to access EntityManager to modify the score value?

I´m also thinking about using Entities package as data model and possibly visualizing with classic gameObjects.

Theses are all on top of my head so, these are not untested:

  • You can generate the data-only components at runtime if needed. If there are no other components, I don’t see the need for a Baker. Unless you have pre-defined variables in the Editor…still, you can reference those and still generate the component at runtime.
  • Sure, you can use a singleton component for what looks like GameStates or GlobalVariables (scores, time, etc.). You can reset these every new game. Be sure you don’t make this singleton component insanely large
  • A SpawnerSystem is not a bad idea. You’d need the Archetype of the Entity you want to create, or have an Entity prefab ready. Pass those to the spawner, and you’ll have your instances.
  • You could have a helper class/system that can access the singleton component using the DefaultWorld’s EntityManager.
  • Have you tried looking at BlobAssets? Might be useful in your case.
1 Like

Thanks Neil-Corre, I’ll try these tomorrow.

Another thing I’m trying to figure out is making these component values reactive. I’d like to an UI component to be able to register to world.Score.OnChanged, world.Enemy.OnCreated and world.Enemy.OnDestroyed
(in score case value could be reactive. But OnCreated would need general listener and callback to get entity to be used as pointer)

If I have a manager layer I could fire change events from there I guess, just wondering if there’s a better practice

Outcome of this was making a reactive style model by using ECS with generics API:s. Seems to work fine so far:

This is what I did

        public EntityManager EntityManager => World.DefaultGameObjectInjectionWorld.EntityManager;
       
        private void Update()
        {
            // All these are just examples of how entities API:s could be called from outside
            var entity = CreateEmptyEntity();
            var positionComponent = new Position() { Value = new float3(2.3f, 4.3f, 3.3f) };
            AddComponent(entity, positionComponent);
            var query = EntityManager.CreateEntityQuery(typeof(Position));
            print($"Num of position components {query.ToEntityArray(Allocator.TempJob).Length}");
        }

        public Entity CreateEmptyEntity()
        {
            var components = new ComponentType[] { };
            return EntityManager.CreateEntity(components);
        }

        public void AddComponent<T>(Entity entity, T component) where T : unmanaged, IComponentData
        {
            EntityManager.AddComponentData(entity, component);
            ComponentAdded?.Invoke(entity, component);
        }

and listener side has this

        private Dictionary<Type, Delegate> _componentAddedListeners = new();
       
        public EntityComponentListener()
        {
            EntityComponentManager.Instance.ComponentAdded += OnComponentAdded;
            TestAPI();
        }
       
        private void OnComponentAdded(Entity entity, IComponentData componentData)
        {
            foreach (var kvp in _componentAddedListeners)
            {
                if (kvp.Key == componentData.GetType())
                {
                    kvp.Value.DynamicInvoke(entity, componentData);
                }
            }
        }
       
        public void AddComponentAddedListener<T>(Action<Entity, T> callback)
        {
            _componentAddedListeners.Add(typeof(T), callback);
        }
       
        public void OnPositionChanged<T>(Entity entity, T component)
        {
            Debug.Log("position component added");
        }
       
        private void TestAPI()
        {
            AddComponentAddedListener<Position>(OnPositionChanged);
        }