Архитектура системы крафтинга для мобильной игры
Проектируем систему крафтинга с нуля. Типичные ошибки: хранить рецепты в коде, доверять клиентской валидации, игнорировать офлайн-прогрессию. За долгие годы мы реализовали крафтинг для игр с совокупной аудиторией более 10 млн установок. Делимся архитектурными решениями, которые экономят до 40% времени при интеграции.
Система крафтинга — механика комбинирования ресурсов для получения предметов. В мобильных играх она варьируется от простой (5 камней → 1 алмаз) до сложной (цепочка из 10 рецептов с разными станциями и вероятностями). От правильной схемы данных зависит производительность и защита от взлома. Например, в RPG с 50 рецептами и 4 станциями без серверной валидации читерство достигает 90%.
Вместо хранения рецептов в коде выносим их в конфиги — Remote Config или Addressables. Это меняет баланс без обновления приложения: увеличить время крафта редкого меча с 10 до 15 секунд за пару кликов. А серверная валидация снижает риск читерства на 90% для игр с монетизацией. Стоимость разработки системы крафтинга рассчитывается индивидуально, но экономия бюджета за счёт грамотной архитектуры составляет до 30%. Ниже разберём ключевые компоненты на примерах с Unity и PlayFab.
Почему рецепты стоит хранить как данные?
Рецепты удобно хранить как данные, а не как логику. Это позволяет добавлять рецепты без обновления приложения — через 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 = инструмент, не расходуется
}
| Параметр | Описание |
|---|---|
| recipeId | Уникальный идентификатор рецепта |
| resultItemId | ID предмета-результата |
| ingredients | Список ингредиентов с количеством и флагом расхода |
| craftingTime | Время крафта в секундах (0 — мгновенный) |
| successRate | Вероятность успеха от 0 до 1 |
Список рецептов загружается при старте из ScriptableObject или через Addressables.LoadAssetAsync<CraftingDatabase>. Для балансировки без обновления — Remote Config хранит JSON с overrides. Пример: увеличили время крафта меча с 10 до 15 секунд, если баланс сместился.
Реализация проверки ингредиентов и серверной валидации
Проверка двусторонняя: на клиенте для UX (мгновенная ошибка), на сервере для защиты от взлома. Клиентская логика в 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;
}
}
Для игр с монетизацией обязательна серверная валидация через 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 };
};
Серверная валидация предотвращает читерство: клиент может взломать, сервер — нет.
Как организовать крафтинг с таймером и офлайн-прогрессию?
Для механики «запустил и вернись через час» используем объект слота:
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: если прошло — крафт завершён. Так таймер работает в офлайне.
Типы крафта
| Тип крафта | Описание | Сложность | Пример |
|---|---|---|---|
| Мгновенный | Ингредиенты сразу конвертируются в результат | Низкая | 5 камней → 1 алмаз |
| С таймером | Запуск крафта, ожидание завершения | Средняя | Создание зелья 30 мин |
| Вероятностный | Результат зависит от шанса | Средняя | Превращение руды с шансом 70% |
| Многоуровневый | Цепочка крафтов из промежуточных предметов | Высокая | Сталь из руды и угля → меч |
Категория крафта выбирается под жанр. Для RPG подходят многоуровневые и таймерные, для казуальных — мгновенные и вероятностные.
Этапы разработки системы крафтинга
- Анализ — определяем механики, строим прототип.
- Проектирование данных — рецепты, инвентарь, слоты, серверные правила.
- Реализация — пишем CraftingManager, валидацию, таймеры, UI.
- Интеграция сервера — PlayFab Cloud Script для валидации.
- Тестирование — нагрузка, читерство, офлайн-сценарии.
- Деплой — публикация с соблюдением Store Review Guidelines.
Пример реального кейса
Для RPG с 50 рецептами и 4 станциями реализовали многоуровневую цепочку крафтов. Использовали PlayFab для серверной валидации. Время крафта — от 10 секунд до 24 часов. Серверная валидация снизила читерство до нуля. Проект занял 2 недели.Содержание разработки системы крафтинга
- Проектирование схемы данных рецептов и инвентаря.
- Реализация CraftingManager с проверками и вероятностями.
- Система крафтинга с таймерами и офлайн-прогрессией.
- Локальные пуш-уведомления о завершении крафта.
- Серверная валидация через PlayFab / GameSparks.
- UI: рецептбук, слоты крафтинга, прогресс-бар.
- Аналитические события для балансировки популярности рецептов.
Сроки разработки
Базовый крафтинг (мгновенный, без таймеров): 3–5 дней. Полная система с таймерами, слотами, серверной валидацией и UI: 1,5–3 недели. Стоимость рассчитывается индивидуально после оценки объёма инвентаря и количества рецептов. Гарантируем стабильную работу и соблюдение Store Review Guidelines.
Наши специалисты имеют 10+ лет опыта в мобильной разработке — учтём нюансы код-сайнинга и сертификатов для публикации. Оценим проект и предложим оптимальное решение. Закажите разработку системы крафтинга — получите надёжную архитектуру. Свяжитесь для консультации.







