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: ScriptableObject
s, 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:

gameObject:
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();
}
}