목차
Why Crafting Systems Are Essential
Crafting systems are an integral part of many games, providing players with control, creativity, and progression. Whether it’s creating weapons in an RPG or building shelters in a survival game, a crafting system enhances gameplay depth and replayability.
This guide takes a deep dive into implementing a highly flexible data-driven crafting system for Unity, focusing on scalability, modularity, and real-world use cases. By the end of this article, you’ll have a fully-featured crafting system ready for production.
Overview of the Advanced Crafting System
The crafting system will include:
- Dynamic Recipe Management: Add, remove, or modify recipes without touching the core logic.
- Inventory Integration: Automatically check and update player inventory.
- Partial Crafting Support: Display missing ingredients and suggest actions.
- Recipe Categorization: Group recipes into categories for better organization.
- Event System: Notify UI and other systems of crafting events.
The crafting system will leverage ScriptableObjects, Unity Events, and modular programming to ensure reusability and maintainability.
Use Cases for This System
- RPGs: Crafting weapons, potions, and armor.
- Survival Games: Creating tools, shelters, and consumables.
- Sandbox Games: Building advanced structures or upgrading equipment.
- Simulation Games: Managing complex recipes for industrial production chains.
Full-Featured Crafting System Code
1. Data Structure for Items and Recipes
We start by defining the basic components using ScriptableObjects.
using UnityEngine;
using System.Collections.Generic;
[CreateAssetMenu(fileName = "NewItem", menuName = "Crafting/Item")]
public class ItemData : ScriptableObject
{
public string itemName;
public Sprite icon;
public string description;
public bool isStackable;
}
[CreateAssetMenu(fileName = "NewRecipe", menuName = "Crafting/Recipe")]
public class CraftingRecipe : ScriptableObject
{
public string recipeName;
public ItemData outputItem;
public int outputQuantity = 1;
public List<Ingredient> ingredients;
public string category; // e.g., "Weapons", "Potions", etc.
[System.Serializable]
public class Ingredient
{
public ItemData item;
public int quantity;
}
}
2. Crafting Manager
The CraftingManager handles recipe validation, crafting logic, and event broadcasting.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class CraftingManager : MonoBehaviour
{
public List<CraftingRecipe> recipes;
public UnityEvent<ItemData, int> onCraftSuccess; // Triggered when crafting succeeds
public UnityEvent<CraftingRecipe> onCraftFail; // Triggered when crafting fails
public bool CanCraft(CraftingRecipe recipe, Dictionary<ItemData, int> inventory, out List<ItemData> missingItems)
{
missingItems = new List<ItemData>();
foreach (var ingredient in recipe.ingredients)
{
if (!inventory.ContainsKey(ingredient.item) || inventory[ingredient.item] < ingredient.quantity)
{
missingItems.Add(ingredient.item);
}
}
return missingItems.Count == 0;
}
public bool Craft(CraftingRecipe recipe, Dictionary<ItemData, int> inventory)
{
if (!CanCraft(recipe, inventory, out var missingItems))
{
onCraftFail?.Invoke(recipe);
return false;
}
// Consume ingredients
foreach (var ingredient in recipe.ingredients)
{
inventory[ingredient.item] -= ingredient.quantity;
if (inventory[ingredient.item] <= 0)
{
inventory.Remove(ingredient.item);
}
}
// Add crafted item
if (inventory.ContainsKey(recipe.outputItem))
{
inventory[recipe.outputItem] += recipe.outputQuantity;
}
else
{
inventory[recipe.outputItem] = recipe.outputQuantity;
}
onCraftSuccess?.Invoke(recipe.outputItem, recipe.outputQuantity);
return true;
}
}
3. Inventory System
The InventoryManager integrates with the crafting system to manage item quantities.
using System.Collections.Generic;
using UnityEngine;
public class InventoryManager : MonoBehaviour
{
public Dictionary<ItemData, int> inventory = new Dictionary<ItemData, int>();
public void AddItem(ItemData item, int quantity)
{
if (inventory.ContainsKey(item))
{
inventory[item] += quantity;
}
else
{
inventory[item] = quantity;
}
}
public void RemoveItem(ItemData item, int quantity)
{
if (inventory.ContainsKey(item))
{
inventory[item] -= quantity;
if (inventory[item] <= 0)
{
inventory.Remove(item);
}
}
}
public int GetItemQuantity(ItemData item)
{
return inventory.ContainsKey(item) ? inventory[item] : 0;
}
}
4. User Interface for Crafting
A dynamic UI updates based on available recipes and inventory.
using UnityEngine;
using UnityEngine.UI;
using System.Linq;
public class CraftingUI : MonoBehaviour
{
public CraftingManager craftingManager;
public InventoryManager inventoryManager;
public Transform recipeListParent;
public GameObject recipeButtonPrefab;
void Start()
{
GenerateRecipeList();
}
void GenerateRecipeList()
{
foreach (var recipe in craftingManager.recipes)
{
var button = Instantiate(recipeButtonPrefab, recipeListParent);
button.GetComponentInChildren<Text>().text = recipe.recipeName;
button.GetComponent<Button>().onClick.AddListener(() =>
{
if (craftingManager.Craft(recipe, inventoryManager.inventory))
{
Debug.Log($"Successfully crafted {recipe.outputItem.itemName}");
}
else
{
Debug.Log("Failed to craft. Missing items.");
}
});
}
}
}
Key Features
- Dynamic Inventory Validation: The crafting manager dynamically validates recipes based on inventory.
- Event System: Enables seamless integration with UI and sound effects.
- Recipe Categorization: Recipes can be grouped into categories for organized display.
- Extensible Design: Easily expand the system to include crafting timers, resource rarity, or quality tiers.
Advantages and Disadvantages
Advantages
- Flexibility: Modify recipes without altering the codebase.
- Reusability: A modular design that fits any Unity project.
- Scalability: Manage hundreds of recipes efficiently.
Disadvantages
- Setup Overhead: Requires time to configure data structures and assets.
- Complex Debugging: Issues in data files can be harder to trace than hardcoded logic.
Conclusion
This advanced crafting system is designed to be flexible, scalable, and ready for production. By leveraging data-driven principles, you can manage recipes dynamically, streamline inventory integration, and enhance the player experience.
With the provided codebase, you have a powerful foundation to implement and customize a crafting system that meets your game’s unique needs.