This guide will show you how to implement a complete crafting system using Ultimate Grid Inventory (UGI). You'll learn how to create recipes, validate ingredients, and handle the crafting process.
Here's a complete example of a crafting system that integrates with UGI:
using System;using System.Collections.Generic;using System.Linq;using Inventory.Scripts.Core.Controllers;using Inventory.Scripts.Core.Grids;using Inventory.Scripts.Core.Enums;using Inventory.Scripts.Core.Items;using Inventory.Scripts.Core.Items.Metadata;using Inventory.Scripts.Core.ScriptableObjects;using Inventory.Scripts.Core.ScriptableObjects.Items;using UnityEngine;namespace Inventory.Scripts.Utilities{ public class CraftingSystem : MonoBehaviour { [SerializeField] private InventorySo playerInventorySo; [SerializeField] private InventorySupplierSo inventorySupplierSo; [Tooltip("Optional list of recipes available in this crafting station")] [SerializeField] private List<Recipe> availableRecipes = new List<Recipe>(); /// <summary> /// Attempts to craft an item based on the provided recipe /// </summary> /// <param name="recipe">The recipe to craft</param> /// <returns>True if crafting was successful, false otherwise</returns> public bool CraftItem(Recipe recipe) { // Step 1: Get the recipe requirements var recipeRequirements = recipe.RecipeRequirements; // Step 2: Check if player has all required ingredients var (canCraftItem, itemsToRemove) = ContainsAllItemsInPlayerInventory(recipeRequirements); // Step 3: If missing ingredients, abort crafting if (!canCraftItem) { Debug.Log("Cannot craft - missing required ingredients"); return false; } // Step 4: Try to find space for the crafted item in inventory var (item, gridResponse) = inventorySupplierSo.FindPlaceForItemInGrids(recipe.ItemToCraft, playerInventorySo.GetGrids()); // Step 5: If no space available, abort crafting if (gridResponse != GridResponse.Inserted) { Debug.Log("Cannot craft - inventory is full"); return false; } // Step 6: Remove the ingredients that were used foreach (var itemTable in itemsToRemove) { inventorySupplierSo.RemoveItem(itemTable); } // Step 7: Crafting successful! Debug.Log($"Successfully crafted: {item.ItemDataSo.DisplayName}"); return true; } /// <summary> /// Checks if the player's inventory contains all required items for a recipe /// </summary> private (bool, List<ItemTable>) ContainsAllItemsInPlayerInventory(List<ItemDataSo> itemsRecipe) { // Get all items from all inventory grids var allItemsFromGrids = playerInventorySo.GetGrids().SelectMany(grid => grid.GetAllItemsFromGrid()).ToList(); return CheckRecipe(allItemsFromGrids, itemsRecipe); } /// <summary> /// Validates if all recipe requirements are met and returns the items to be consumed /// </summary> private static (bool, List<ItemTable>) CheckRecipe(List<ItemTable> playerItems, List<ItemDataSo> recipeItems) { var recipeCounts = new Dictionary<ItemDataSo, double>(); foreach (var item in recipeItems) { recipeCounts.TryAdd(item, 0); recipeCounts[item] += 1; } var itemsToRemove = new List<ItemTable>(); var remainingNeeds = new Dictionary<ItemDataSo, double>(recipeCounts); foreach (var kvp in remainingNeeds.ToList()) { var neededItem = kvp.Key; var remainingAmount = kvp.Value; var matchingItems = playerItems .Where(i => i.ItemDataSo == neededItem) .ToList(); foreach (var item in matchingItems) { if (remainingAmount <= 0) break; var metadata = item.GetMetadata<CountableMetadata>(); if (metadata != null) { var available = metadata.Stack; var toTake = Math.Min(available, remainingAmount); var stackableData = item.GetItemData<ItemStackableDataSo>(); var minStack = stackableData != null ? stackableData.MinStack : 0; // Check if we can split without breaking rules var canSplit = toTake >= minStack && available - toTake >= minStack; if (canSplit) { var splitItem = metadata.Split(toTake); if (splitItem != null) { itemsToRemove.Add(splitItem); remainingAmount -= toTake; } } else if (available <= remainingAmount && available >= minStack) { // Take the full item if possible (no need to split) itemsToRemove.Add(item); remainingAmount -= available; // Avoid double-using this item playerItems.Remove(item); } } else { itemsToRemove.Add(item); remainingAmount -= 1.0; } } remainingNeeds[neededItem] = remainingAmount; } var canCraft = remainingNeeds.Values.All(v => v <= 0.0001); return (canCraft, canCraft ? itemsToRemove : new List<ItemTable>()); } /// <summary> /// Recipe definition class - can be extracted to a separate file /// </summary> [Serializable] public class Recipe { [Tooltip("The item that will be created when crafting")] [SerializeField] private ItemDataSo itemToCraft; [Tooltip("The items required to craft this recipe")] [SerializeField] private List<ItemDataSo> recipeRequirements; public ItemDataSo ItemToCraft => itemToCraft; public List<ItemDataSo> RecipeRequirements => recipeRequirements; } }}
After implementing your crafting system, consider these enhancements:
Add a crafting UI that displays available recipes and required ingredients
Implement recipe discovery mechanics where players can learn new recipes
Create crafting stations with different available recipes
Add crafting animations and sound effects for a more immersive experience
Implement crafting requirements beyond just ingredients (e.g., player level, skills)
By following this guide, you'll have a robust crafting system that integrates seamlessly with the Ultimate Grid Inventory system, enhancing your game's depth and player engagement.