Розробка системи ежедневних завдань мобільної гри
Ежедневні завдання — головний механізм щоденного повернення в гру. Якщо досягнення дають довгострокові цілі, то daily quests вирішують питання «навіщо відкрити гру сьогодні». Для casual-ігр з DAU менше 100k правильно реалізовані ежедневки збільшують Day-7 retention в полтора-два рази — при умові, що завдання реально цікаві, а не «входи у гру 3 рази».
Генерація завдань: статичні vs динамічні
Статичні завдання — фіксований пул завдань, з якого випадково вибираються 3-5 на день. Просто у реалізації, передбачувано для гравця, але швидко приєднується.
Динамічні завдання — генеруються на основі прогресу гравця й активності. Новачку дають «пройди 3 рівні», ветерану — «собери 500 ресурсів типу X». Складніше у реалізації, краще retention.
Схема даних для динамічної системи:
[Serializable]
public class QuestTemplate
{
public string templateId;
public string type; // "complete_levels", "collect_items", "defeat_enemies"
public int[] targetValues; // варіанти складності: [3, 5, 10]
public string[] targetParams; // для collect: ["wood", "stone", "gold"]
public QuestRewardTier[] rewardTiers; // награда по складності
public int minPlayerLevel;
public int maxPlayerLevel; // -1 = без обмеження
}
[Serializable]
public class ActiveQuest
{
public string questId;
public string templateId;
public string type;
public string targetParam;
public int targetValue;
public int currentValue;
public bool isCompleted;
public bool rewardClaimed;
public DateTime expiresAt;
}
DailyQuestManager: генерація та трекінг
public class DailyQuestManager : MonoBehaviour
{
private const int QUEST_SLOTS = 3;
private List<ActiveQuest> _activeQuests = new();
private QuestDatabase _database;
public void GenerateDailyQuests()
{
_activeQuests.Clear();
var playerLevel = PlayerData.Level;
var random = new System.Random(GetDailySeed()); // seed = дата, однакові для всіх
var eligibleTemplates = _database.Templates
.Where(t => playerLevel >= t.minPlayerLevel &&
(t.maxPlayerLevel == -1 || playerLevel <= t.maxPlayerLevel))
.ToList();
var selectedTemplates = eligibleTemplates
.OrderBy(_ => random.Next())
.Take(QUEST_SLOTS)
.ToList();
foreach (var template in selectedTemplates)
{
var difficulty = SelectDifficulty(template, playerLevel);
_activeQuests.Add(CreateQuestFromTemplate(template, difficulty));
}
SaveQuests();
}
private int GetDailySeed()
{
// Всі гравці отримують однаковий набір завдань в один день
// (або не однаковий - залежить від дизайн-рішення)
return DateTime.UtcNow.Date.GetHashCode() ^ PlayerData.UserId.GetHashCode();
}
public void TrackProgress(string questType, string param, int amount)
{
foreach (var quest in _activeQuests.Where(q => !q.isCompleted && q.type == questType))
{
if (!string.IsNullOrEmpty(quest.targetParam) && quest.targetParam != param) continue;
quest.currentValue += amount;
if (quest.currentValue >= quest.targetValue)
{
quest.isCompleted = true;
OnQuestCompleted(quest);
}
}
SaveQuests();
}
}
Інтеграція в геймплей
Трекінг завдань повинен бути бесшовним — гравець не думає «надо натиснути кнопку учета», він просто грає:
// У системі боївки:
void OnEnemyDefeated(Enemy enemy)
{
DailyQuestManager.Instance.TrackProgress("defeat_enemies", enemy.type, 1);
AchievementsManager.Instance.TrackEvent("enemy_defeated", 1);
// ...
}
// У системі збору ресурсів:
void OnResourceCollected(string resourceId, int amount)
{
DailyQuestManager.Instance.TrackProgress("collect_items", resourceId, amount);
// ...
}
Один виклик з геймплейної системи — всі завдання з потрібним типом оновлюються автоматично.
Система наград і streak
Ежедневні завдання працюють краще в парі з серією виконання (quest streak):
// При завершенні всіх завдань дня
void OnAllQuestsCompleted()
{
var streak = PlayerPrefs.GetInt("daily_quest_streak", 0);
var lastCompletedDate = PlayerPrefs.GetString("quest_last_completed", "");
var yesterday = DateTime.UtcNow.Date.AddDays(-1).ToString("yyyy-MM-dd");
var today = DateTime.UtcNow.Date.ToString("yyyy-MM-dd");
streak = (lastCompletedDate == yesterday) ? streak + 1 : 1;
PlayerPrefs.SetInt("daily_quest_streak", streak);
PlayerPrefs.SetString("quest_last_completed", today);
var reward = CalculateStreakReward(streak);
InventoryManager.Instance.AddReward(reward);
GameAnalytics.NewDesignEvent("DailyQuests:StreakCompleted", streak);
}
На 7-й день серії видаємо особливу награду. На 30-й — виключну. Це створює довгострокову ціль поверх ежедневних завдань.
Серверна генерація vs клієнтська
Для ігор без читерської загрози — клієнтська генерація простіше. Для ігор з реальними нагородами (IAP-еквівалент) — генерація й видача наград тільки через сервер (PlayFab Cloud Script). Клієнт запитує завдання, сервер повертає згенерований список й верифікує прогрес перед виданням награди.
Що входить у роботу
- Проектування шаблонів завдань та системи складності
- Реалізація
DailyQuestManagerз генерацією й трекінгом прогресу - Інтеграція трекінгу в геймплейні системи
- Система streak з нарастаючими нагородами
- Скидання завдань по розписанню (UTC midnight)
- UI: список завдань, прогрес-бари, екран отримання награди
- Серверна генерація й валідація (PlayFab, при необхідності)
Терміни
Клієнтська система з базовим набором завдань: 4–7 днів. Повна система з серверною генерацією, streak й складним UI: 2–3 тижні. Вартість розраховується індивідуально.







