Proper way to store an array that is used between multiple entities

Hey, I am currently trying to make an inventory system using ECS. I posted a previous thread about the inventory, but this question is unrelated to the inventory so I have made a new thread.

At the start of the game, I load a bunch of Item ScriptableObjects using Addressables.

What’s the best way to store these ScriptableObjects using ECS? Normally, I would create a class called something like “DataManager” and just store a list of all the ScriptableObjects, then access them when I want to make new items.

How do I accomplish this using ECS?

If data immutable - blobs. But all depends on your data types inside SO.

The data is Immutable, things that the SO contains are strings, ints, doubles, and Meshes.

Could you elaborate on blobs?

I recommend you watch this talk:

2 Likes

Thanks, after watching the video I came up with this.

public class Items {
        private NativeHashMap<NativeString64, Item> _item_data;

        public async void Load() {
            var item_data_list = await ScriptableObjectLoader.Load<ItemData>(AddressableConstants.ITEM);

            _item_data = new NativeHashMap<NativeString64, Item>(item_data_list.Count, Allocator.Persistent);

            foreach (var item in item_data_list) {
                var item_data = ItemDataBlob.BuildItem(item);

                _item_data[item_data.Value.Id] = new Item {
                    Data = item_data
                };
            }
        }

        public Item GetItem(NativeString64 id) { return _item_data[id]; }

        public void Dispose() { _item_data.Dispose(); }
    }

    public class ItemSystem : ComponentSystem {
        private Items _items;

        protected override void OnCreateManager() {
            _items = new Items();
            _items.Load();

            CreateEntity("test");
        }

        protected override void OnUpdate() { }

        public void CreateEntity(string id) {
            var entity = EntityManager.CreateEntity();

            EntityManager.AddSharedComponentData(entity, _items.GetItem(new NativeString64(id)));
        }

        protected override void OnDestroy() { _items.Dispose(); }
    }

    public struct ItemDataBlob {
        public NativeString64 Id;
        public NativeString64 Name;
        public NativeString512 Description;
        public int MaxStackSize;

        public static BlobAssetReference<ItemDataBlob> BuildItem(ItemData item_data) {
            using (var builder = new BlobBuilder(Allocator.Temp)) {
                ref var root = ref builder.ConstructRoot<ItemDataBlob>();

                root.Id = new NativeString64(item_data.Id);
                root.Name = new NativeString64(item_data.Name);
                root.Description = new NativeString512(item_data.Description);
                root.MaxStackSize = item_data.MaxStackSize;

                return builder.CreateBlobAssetReference<ItemDataBlob>(Allocator.Persistent);
            }
        }
    }

    public struct Item : ISharedComponentData {
        public BlobAssetReference<ItemDataBlob> Data;
    }

Please let me know if this is the correct way to do this

1 Like

For a slightly cleaner use of blobs, I find this extension really useful:
https://github.com/periodyctom/Hydrogen.Entities

Thanks for the recommendation, but I would like to learn the underlying system before I start using a library for it.

At a quick glance this seems ok. Normally I would use a conversion system + a subscene, but since you are loading this via Addressables I’m not sure there is a dynamic path for that.

Also you could use the singleton entity pattern to more easily find your data again from an ECS system/job.

1 Like

I didn’t even know about Get/Set Singleton, I will definitely use that over my current implementation.

1 Like