← Назад к вопросам

Реализовать систему крафта предметов

2.0 Middle🔥 191 комментариев
#C# и ООП#UI#Коллекции и структуры данных#Ресурсы и ассеты

Условие

Реализуйте систему крафта предметов.

Требования

  1. Рецепты как ScriptableObject
  2. Проверка наличия ингредиентов
  3. Время крафта
  4. Очередь крафта
  5. UI крафт меню
  6. Разблокировка рецептов

Рецепт содержит

  • Список ингредиентов с количеством
  • Результат крафта
  • Время изготовления
  • Требуемый уровень навыка

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Система крафта предметов в Unity

Создам модульную архитектуру на основе ScriptableObject и Manager паттернов. Система должна быть гибкой, расширяемой и удобной в использовании.

Структура рецепта (ScriptableObject)

[System.Serializable]
public struct RecipeIngredient
{
    public Item item;
    public int quantity;
}

public class Recipe : ScriptableObject
{
    [SerializeField] private string recipeName;
    [SerializeField] private List<RecipeIngredient> ingredients;
    [SerializeField] private Item result;
    [SerializeField] private int resultQuantity = 1;
    [SerializeField] private float craftingTime = 5f;
    [SerializeField] private int requiredSkillLevel = 1;
    [SerializeField] private bool isUnlocked;

    public string RecipeName => recipeName;
    public List<RecipeIngredient> Ingredients => ingredients;
    public Item Result => result;
    public int ResultQuantity => resultQuantity;
    public float CraftingTime => craftingTime;
    public int RequiredSkillLevel => requiredSkillLevel;
    public bool IsUnlocked => isUnlocked;

    public void Unlock() => isUnlocked = true;
}

Manager крафта с очередью

public class CraftingManager : MonoBehaviour
{
    [SerializeField] private Queue<CraftingTask> craftingQueue = new();
    [SerializeField] private CraftingTask currentTask;
    [SerializeField] private Inventory inventory;
    [SerializeField] private PlayerSkills playerSkills;

    [System.Serializable]
    private class CraftingTask
    {
        public Recipe recipe;
        public float timeRemaining;
    }

    public bool TryStartCrafting(Recipe recipe)
    {
        if (!recipe.IsUnlocked)
        {
            Debug.Log("Рецепт не разблокирован");
            return false;
        }

        if (playerSkills.GetSkillLevel("Crafting") < recipe.RequiredSkillLevel)
        {
            Debug.Log($"Требуется уровень {recipe.RequiredSkillLevel}");
            return false;
        }

        foreach (var ingredient in recipe.Ingredients)
        {
            if (inventory.GetItemCount(ingredient.item) < ingredient.quantity)
            {
                Debug.Log($"Недостаточно {ingredient.item.name}");
                return false;
            }
        }

        foreach (var ingredient in recipe.Ingredients)
        {
            inventory.RemoveItem(ingredient.item, ingredient.quantity);
        }

        var task = new CraftingTask
        {
            recipe = recipe,
            timeRemaining = recipe.CraftingTime
        };

        if (currentTask == null)
        {
            currentTask = task;
            OnCraftingStarted?.Invoke(recipe);
        }
        else
        {
            craftingQueue.Enqueue(task);
        }

        return true;
    }

    private void Update()
    {
        if (currentTask == null && craftingQueue.Count == 0)
            return;

        if (currentTask == null)
        {
            currentTask = craftingQueue.Dequeue();
            OnCraftingStarted?.Invoke(currentTask.recipe);
        }

        currentTask.timeRemaining -= Time.deltaTime;
        OnCraftingProgress?.Invoke(1f - (currentTask.timeRemaining / currentTask.recipe.CraftingTime));

        if (currentTask.timeRemaining <= 0)
        {
            CompleteCrafting();
        }
    }

    private void CompleteCrafting()
    {
        var recipe = currentTask.recipe;
        inventory.AddItem(recipe.Result, recipe.ResultQuantity);
        playerSkills.AddExperience("Crafting", (int)recipe.CraftingTime);
        
        OnCraftingCompleted?.Invoke(recipe);
        currentTask = null;
    }

    public event System.Action<Recipe> OnCraftingStarted;
    public event System.Action<float> OnCraftingProgress;
    public event System.Action<Recipe> OnCraftingCompleted;
}

UI меню крафта

public class CraftingUI : MonoBehaviour
{
    [SerializeField] private Transform recipesContainer;
    [SerializeField] private CraftingRecipeButton recipeButtonPrefab;
    [SerializeField] private Text progressText;
    [SerializeField] private Image progressBar;
    [SerializeField] private CraftingManager craftingManager;
    [SerializeField] private List<Recipe> allRecipes;

    private void Start()
    {
        DisplayRecipes();
        craftingManager.OnCraftingStarted += UpdateUI;
        craftingManager.OnCraftingProgress += UpdateProgressBar;
    }

    private void DisplayRecipes()
    {
        foreach (var recipe in allRecipes)
        {
            var button = Instantiate(recipeButtonPrefab, recipesContainer);
            button.Setup(recipe, craftingManager);
        }
    }

    private void UpdateProgressBar(float progress)
    {
        progressBar.fillAmount = progress;
        progressText.text = $"{progress:P0}";
    }

    private void OnDestroy()
    {
        craftingManager.OnCraftingStarted -= UpdateUI;
        craftingManager.OnCraftingProgress -= UpdateProgressBar;
    }
}

Преимущества решения

  • Модульность: рецепты — независимые ScriptableObject'ы
  • Очередь: несколько крафтов подряд
  • Валидация: проверка ингредиентов перед стартом
  • Event система: слабая связанность между компонентами
  • Расширяемость: легко добавить баффы, скидки, критические успехи
Реализовать систему крафта предметов | PrepBro