Розробка системи крафтингу мобільної гри
Система крафтингу — механіка, в якій гравець комбінує ресурси або предмети для отримання нового. Від простого «3 дерева + 2 залізо = меч» до багаторівневих дерев рецептів з вірогідними результатами й часовими механіками. Складність реалізації визначається не інтерфейсом, а архітектурою даних: рецепти, інвентар, стан крафту, серверна валідація.
Архітектура даних рецептів
Рецепти краще зберігати як дані, а не як логіку в коді. Це дозволяє додавати нові рецепти без оновлення додатку через Remote Config або Addressables:
[Serializable]
public class CraftingRecipe
{
public string recipeId;
public string resultItemId;
public int resultQuantity;
public List<Ingredient> ingredients;
public float craftingTime; // секунди, 0 = миттєво
public float successRate; // 0.0-1.0, 1.0 = завжди
public string requiredStation; // "forge", "alchemy_table", null = скрізь
public int requiredLevel;
}
[Serializable]
public class Ingredient
{
public string itemId;
public int quantity;
public bool consumed; // false = інструмент, не витрачається
}
Список рецептів завантажується при старті з ScriptableObject або через Addressables.LoadAssetAsync<CraftingDatabase>. Для балансировки без оновлення — Remote Config зберігає JSON з overrides, який патчить дефолтні значення.
CraftingManager: основна логіка
public class CraftingManager : MonoBehaviour
{
public static CraftingManager Instance { get; private set; }
private CraftingDatabase _database;
private InventoryManager _inventory;
public CraftingResult TryCraft(string recipeId, string stationId = null)
{
var recipe = _database.GetRecipe(recipeId);
if (recipe == null) return CraftingResult.InvalidRecipe;
// Перевірка станції
if (!string.IsNullOrEmpty(recipe.requiredStation) && recipe.requiredStation != stationId)
return CraftingResult.WrongStation;
// Перевірка рівня
if (PlayerData.Level < recipe.requiredLevel)
return CraftingResult.LevelTooLow;
// Перевірка наявності інгредієнтів
foreach (var ingredient in recipe.ingredients)
{
if (_inventory.GetCount(ingredient.itemId) < ingredient.quantity)
return CraftingResult.MissingIngredients;
}
// Вірогідність успіху
bool success = Random.value <= recipe.successRate;
// Списуємо тільки витрачаємі інгредієнти
foreach (var ingredient in recipe.ingredients.Where(i => i.consumed))
_inventory.Remove(ingredient.itemId, ingredient.quantity);
if (success)
_inventory.Add(recipe.resultItemId, recipe.resultQuantity);
// Аналітика
GameAnalytics.NewDesignEvent($"Crafting:Result:{recipeId}", success ? 1 : 0);
return success ? CraftingResult.Success : CraftingResult.Failed;
}
}
Крафтинг з таймером
Для механіки «запустив крафт, вернись через годину»:
public class CraftingSlot
{
public string recipeId;
public DateTime completionTime;
public bool isComplete => DateTime.UtcNow >= completionTime;
}
public void StartCrafting(string recipeId, int slotIndex)
{
var recipe = _database.GetRecipe(recipeId);
_craftingSlots[slotIndex] = new CraftingSlot
{
recipeId = recipeId,
completionTime = DateTime.UtcNow.AddSeconds(recipe.craftingTime)
};
// Зберігаємо в PlayerPrefs або Cloud Save
SaveCraftingState();
// Планіємо локальний пуш
NotificationManager.ScheduleLocal(
$"Крафтинг завершений: {recipe.resultItemId}",
recipe.craftingTime
);
}
Стан слотів необхідно персистити — якщо гравець закрив додаток, таймер повинен продовжуватися. Для офлайн-прогресії використовуємо DateTime.UtcNow при наступному запуску: якщо completionTime < DateTime.UtcNow — крафт завершений.
Серверна валідація
Для ігор з реальною монетизацією крафтинг не можна повністю довіряти клієнту. Класична схема через PlayFab Cloud Script:
// PlayFab Cloud Script
handlers.CraftItem = function(args) {
var recipeId = args.recipeId;
var recipe = getRecipeFromCatalog(recipeId);
// Перевіряємо інвентар на сервері
var inventory = server.GetUserInventory({ PlayFabId: currentPlayerId });
for (var ingredient of recipe.ingredients) {
var count = countItem(inventory, ingredient.itemId);
if (count < ingredient.quantity) {
return { success: false, error: "insufficient_items" };
}
}
// Списуємо й видаємо на сервері
for (var ingredient of recipe.ingredients.filter(i => i.consumed)) {
server.RevokeInventoryItems({ ... });
}
server.GrantItemsToUser({ itemIds: [recipe.resultItemId], ... });
return { success: true, resultItemId: recipe.resultItemId };
};
Що входить у роботу
- Проектування схеми даних рецептів й інвентаря
- Реалізація
CraftingManagerз логікою перевірок та вірогідностей - Система крафтингу з таймерами й офлайн-прогресією
- Локальні пуш-уведомлення про завершення крафту
- Серверна валідація через PlayFab / GameSparks (при необхідності)
- UI: рецептбук, слоти крафтингу, прогрес-бар
- Аналітичні eventi для балансировки популярності рецептів
Терміни
Базовий крафтинг (миттєвий, без таймерів): 3–5 днів. Повна система з таймерами, слотами, серверною валідацією й UI: 1,5–3 тижні. Вартість розраховується індивідуально.







