ECS working with scriptable objects for crafting

I am currently trying to create a factory game, but can’t figure out how to do recipes/crafting using ECS.

I would like machines that are entities, to have an inventory of sorts and check if it has the required items from the recipe. currently, all I need is the recipe data, the rest I should be able to do.

I have tried to get scriptable objects(my preferred way) but couldn’t figure it out.
is there any way to get this to work?

PROBLEM: ScriptableObjects, being a managed type, are not accessible from ecs components

SOLUTION: Create BlobAssetReference<T> (read-only memory block) based on given ScriptableObject and read that.

scriptable object:
190373-screenshot-2021-12-26-030517

gameObject:

system running:
transmuter system running

TransmutationRecipeAsset.cs

using System.Collections.Generic;
using Unity.Entities;
using Unity.Collections;
using UnityEngine;

[CreateAssetMenu( menuName="Game/Recipe Asset" , fileName="recipe for 0" , order=1 )]
public class TransmutationRecipeAsset : ScriptableObject
{
    static Dictionary<string,BlobAssetReference<RecipeBlobAsset>> Allocations = new Dictionary<string,BlobAssetReference<RecipeBlobAsset>>();
    [SerializeField] Substance _product;
    [SerializeField][Min(1)] int _batchProducts = 1;
    [SerializeField][Min(0.1f)] float _batchDuration = 1.5f;
    [SerializeField] InventoryItem[] _ingredients;
    public BlobAssetReference<RecipeBlobAsset> ToBlobAssetReference ()
    {
        if( Allocations.ContainsKey(this.name) )
        {
            return Allocations[this.name];
        }
        else
        {
            BlobAssetReference<RecipeBlobAsset> recipe;
            {
                var builder = new BlobBuilder( Allocator.Temp );
                ref var root = ref builder.ConstructRoot<RecipeBlobAsset>();
                {
                    root.Product = _product;
                }
                {
                    root.BatchProducts = _batchProducts;
                }
                {
                    root.BatchDuration = _batchDuration;
                }
                {
                    int numIngredients = _ingredients.Length;
                    var arr = builder.Allocate( ref root.Ingredients , numIngredients );
                    for( int i=0 ; i<numIngredients ; i++ ) arr = _ingredients;
                }
                recipe = builder.CreateBlobAssetReference<RecipeBlobAsset>( Allocator.Persistent );
                builder.Dispose();
            }

            Allocations.Add( this.name , recipe );

            return recipe;
        }
    }
}

public struct RecipeBlobAsset
{
    public Substance Product;
    public int BatchProducts;
    public float BatchDuration;
    public BlobArray<InventoryItem> Ingredients;
}

public enum Substance : byte { Lead , Mercury , Gold }

TransmuterComponent.cs

using Unity.Entities;
using UnityEngine;

[DisallowMultipleComponent]
[RequireComponent( typeof(InventoryComponent) )]
public class TransmuterComponent : MonoBehaviour, IConvertGameObjectToEntity
{
    [SerializeField] TransmutationRecipeAsset _recipeAsset;
    public void Convert ( Entity entity , EntityManager dstManager , GameObjectConversionSystem conversionSystem )
    {
        dstManager.AddComponentData( entity , new Transmuter{
                RecipeData		= _recipeAsset.ToBlobAssetReference()
        } );
    }
}

public struct Transmuter : IComponentData
{
    public BlobAssetReference<RecipeBlobAsset> RecipeData;
    public bool BatchStarted;
    public float BatchTime;
}

InventoryComponent.cs

using Unity.Entities;
using UnityEngine;

[DisallowMultipleComponent]
public class InventoryComponent : MonoBehaviour, IConvertGameObjectToEntity
{
	[SerializeField] InventoryItem[] _items;*
    public void Convert ( Entity entity , EntityManager dstManager , GameObjectConversionSystem conversionSystem )
    {
        var inventory = dstManager.AddBuffer<InventoryItem>( entity );
            for( int i=0 ; i<_items.Length ; i++ )
            inventory.Add( _items *);*
    }
}

[InternalBufferCapacity( 3 )]
[System.Serializable]
public struct InventoryItem : IBufferElementData
{
    public Substance Type;
    public int Quantity;
}

TransmutationSystem.cs

using Unity.Entities;
using Unity.Jobs;

[UpdateInGroup( typeof(SimulationSystemGroup) )]
public class TransmutationSystem : SystemBase
{
    protected override void OnUpdate ()
    {
        float deltaTime = Time.DeltaTime;

        Entities
            .WithName("transmutation_job")
            .ForEach( ( ref Transmuter transmuter , ref DynamicBuffer<InventoryItem> inventory ) =>
            {
                ref var recipe = ref transmuter.RecipeData.Value;

                if( transmuter.BatchStarted )
                {
                    transmuter.BatchTime += deltaTime;
                    if( transmuter.BatchTime>recipe.BatchDuration )
                    {
                        transmuter.BatchTime -= recipe.BatchDuration;
                        transmuter.BatchStarted = false;

                        bool itemCreated = false;
                        for( int i=0 ; i<inventory.Length ; i++ )
                        {
                            var item = inventory;
                            if( item.Type==recipe.Product )
                            {
                                item.Quantity += recipe.BatchProducts;
                                inventory *= item;
                                itemCreated = true;
                                break;
                            }
                        }
                        if( !itemCreated )
                        {
                            inventory.Add( new InventoryItem{
                                Type		= recipe.Product ,
                                Quantity	= recipe.BatchProducts
                            } );
                        }
                    }
                }
                else
                {
                    int numPassed = 0;
                    for( int i=0 ; i<recipe.Ingredients.Length ; i++ )
                    {
                        var ingredient = recipe.Ingredients;
                        bool passed = false;
                        foreach( var item in inventory )
                        {
                            if( item.Type==ingredient.Type && item.Quantity>=ingredient.Quantity )
                            {
                                passed = true;
                                numPassed++;
                                break;
                            }
                        }
                        if( !passed ) break;
                    }
                    if( numPassed==recipe.Ingredients.Length )
                    {
                        for( int i=0 ; i<recipe.Ingredients.Length ; i++ )
                        {
                            var ingredient = recipe.Ingredients;
                            for( int k=0 ; k<inventory.Length ; k++ )
                            {
                                var item = inventory[k];
                                if( item.Type==ingredient.Type )
                                {
                                    item.Quantity -= ingredient.Quantity;
                                    inventory[k] = item;
                                }
                            }
                        }

                        transmuter.BatchStarted = true;
                    }
                }
            } )
            .WithBurst().ScheduleParallel();
    }
}