← Назад к вопросам
Реализовать систему крафта предметов
2.0 Middle🔥 191 комментариев
#C# и ООП#UI#Коллекции и структуры данных#Ресурсы и ассеты
Условие
Реализуйте систему крафта предметов.
Требования
- Рецепты как ScriptableObject
- Проверка наличия ингредиентов
- Время крафта
- Очередь крафта
- UI крафт меню
- Разблокировка рецептов
Рецепт содержит
- Список ингредиентов с количеством
- Результат крафта
- Время изготовления
- Требуемый уровень навыка
Комментарии (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 система: слабая связанность между компонентами
- Расширяемость: легко добавить баффы, скидки, критические успехи