Перейти к основному содержимому

Система заданий

Система заданий предоставляет полный жизненный цикл заданий: создание, отслеживание, выполнение, завершение, провал, награды, предварительные требования, временные ограничения и повторяемые задания. Все данные заданий определяются через ScriptableObjects и интегрированы с системой сохранения и CommandGraph.

Обзор архитектуры​

КлассНазначение
QuestScriptableObject, определяющий цели, награды, предварительные требования и метаданные задания
ObjectiveСериализуемый класс, представляющий одну отслеживаемую задачу в рамках задания
QuestRewardСериализуемый класс, представляющий награду, выдаваемую при завершении
QuestStatusСостояние задания во время выполнения: счётчики прогресса, временные метки, флаги завершения/провала
QuestManagerSavableObject, отслеживающий все состояния заданий, обрабатывающий жизненный цикл и вызывающий события

Quest​

Создайте задание через MCE > Quests > Quest в меню активов. Каждый Quest ScriptableObject имеет следующие разделы:

Классификация​

ПолеТипОписание
IsMainQuestboolОсновные задания критичны для продвижения по сюжету
CategoryQuestCategoryОрганизует задание в журнале заданий (см. ниже)
DifficultyQuestDifficultyEasy, Normal, Hard, Expert — отображается в UI

Категории заданий:

  • MainStory — продвижение по основному сюжету
  • SideQuest — опциональные задания NPC
  • DailyTask — сбрасывается каждые 24 часа
  • WeeklyChallenge — сбрасывается каждые 7 дней
  • Event — временные event-задания
  • Collection — заполнить MonsterDex, найти все предметы и т.д.

Выдающий задание​

ПолеТипОписание
QuestGiverCharacterDataNPC, который выдаёт это задание

Требования​

ПолеТипОписание
PrerequisiteQuestsList<Quest>Задания, которые должны быть выполнены первыми
MinBadgesintМинимальное количество значков для принятия (0 = нет)
RecommendedLevelintОтображается в UI, не является обязательным

Время и повторение​

ПолеТипОписание
TimeLimitSecondsintЛимит времени в секундах (0 = без лимита). Задание автоматически проваливается по истечении
IsRepeatableboolМожно ли перезапустить задание после завершения
RepeatCooldownSecondsintОжидание перед повторным принятием (86400 = ежедневно, 604800 = еженедельно)

Objective​

Каждое задание содержит List<Objective>, определяющую задачи, которые игрок должен выполнить. Цели упорядочены и могут продвигаться автоматически.

Типы целей (11)​

ТипОписаниеКлючевые поля
TalkПоговорить с определённым NPCTargetNPC
DeliverДоставить предмет NPCTargetNPC, TargetItem
DefeatПобедить тренеров или диких монстровTargetMonster (null = любого), TargetCount
CatchПоймать определённый видTargetMonster, TargetCount
CollectСобрать количество предметовTargetItem, TargetCount
GoToОтправиться на определённую картуMapLocation
EscortСопроводить NPC к месту назначенияTargetNPC, MapLocation
EvolveЭволюционировать монстра определённого видаTargetMonster
HatchEggВысидеть яйцо монстраTargetMonster (null = любое)
WinAtFacilityВыиграть битву в определённом учрежденииMapLocation
CustomОтслеживается через игровую переменнуюCustomVariableKey, TargetCount

Поля цели​

[Serializable]
public class Objective
{
public string LocalizationKey; // Локализованный текст описания
public ObjectiveType Type; // Что игрок должен сделать
public SceneInfoAsset MapLocation; // Где (для GoTo, маркер на миникарте)
public CharacterData TargetNPC; // Кто (для Talk/Deliver/Escort)
public MonsterEntry TargetMonster; // Какой вид (null = любой)
public ScriptableObject TargetItem; // Какой предмет (Collect/Deliver)
public int TargetCount = 1; // Сколько нужно
public bool IsOptional; // Бонусная цель, не обязательная
public bool HiddenUntilPrevious; // Скрыта до завершения предыдущей цели
public string CustomVariableKey; // Для отслеживания типа Custom
}

Необязательные цели не блокируют завершение задания. Скрытые цели создают ощущение открытия по мере продвижения задания.

QuestReward​

Каждое задание имеет List<QuestReward>, выдаваемую автоматически при завершении.

Типы наград (6)​

ТипПоляПример
ItemItemReward, ItemQuantity5x Super Potion
MoneyMoneyAmount$5,000
ExperienceExpAmount2,000 EXP распределяется по команде
MonsterMonsterReward, MonsterLevelLv.20 Thundermane
GameVariableGameVariableKey, GameVariableValueУстановить HAS_BIKE = 1
UnlockCustomUnlockId"safari_zone_access"
// Каждая награда имеет человекочитаемое описание для UI
reward.GetDescription(); // "Super Potion x5", "$5,000", "2,000 EXP" и т.д.

QuestManager​

QuestManager — это SavableObject (ScriptableObject с поддержкой сохранения/загрузки), владеющий всем состоянием выполнения заданий. Создайте через MCE > Quests > Manager.

Жизненный цикл задания​

Не начато  -->  StartQuest()  -->  Активно (отслеживание целей)
|
+---------+-----+-----+---------+
| |
CompleteQuest() FailQuest()
| |
Завершено Провалено
|
(если IsRepeatable)
|
StartQuest() (после кулдауна)

Запуск задания​

questManager.StartQuest(quest, startingObjective: 0);

Это создаёт запись QuestStatus, записывает временную метку начала, показывает уведомление и устанавливает начальную цель. Если задание повторяемое и находится на кулдауне, вызов игнорируется с предупреждением.

Продвижение целей​

// Увеличить счётчик прогресса для цели #0 на 1
questManager.UpdateObjectiveProgress(quest, objectiveIndex: 0, delta: 1);

Когда прогресс достигает TargetCount цели, система показывает уведомление и автоматически переходит к следующей необязательной цели. Если все обязательные цели выполнены, задание завершается автоматически.

Вы также можете установить текущую цель напрямую:

questManager.SetQuestObjective(quest, objective: 2);

Завершение и провал​

questManager.CompleteQuest(quest);  // Отмечает как завершённое, выдаёт награды, вызывает событие
questManager.FailQuest(quest); // Отмечает как проваленное, вызывает событие

Задания с ограничением по времени​

Вызывайте questManager.CheckTimeExpiry() периодически (например, из Update MonoBehaviour), чтобы автоматически проваливать задания, время которых истекло:

long remaining = questManager.GetRemainingTime(quest); // секунды или -1

Запросы заданий​

// Все задания, с которыми игрок взаимодействовал
Dictionary<Quest, QuestStatus> all = questManager.GetAllQuests();

// Фильтрованные запросы
List<Quest> active = questManager.GetActiveQuests();
List<Quest> completed = questManager.GetCompletedQuests();
List<Quest> failed = questManager.GetFailedQuests();

// Фильтр по категории, статусу завершения и/или статусу провала
// Сигнатура: GetQuestsFiltered(QuestCategory? category = null, bool? completed = null, bool? failed = null)
List<Quest> dailies = questManager.GetQuestsFiltered(
category: QuestCategory.DailyTask,
completed: false,
failed: null
);

// Проверка предварительных требований
bool canStart = questManager.ArePrerequisitesMet(quest);

// Получение всех доступных для начала заданий (предварительные требования выполнены, ещё не начаты)
List<Quest> available = questManager.GetAvailableQuests();

// Проверка, есть ли у игрока конкретное задание
bool has = questManager.HasQuest(quest);

Процент завершения игры​

QuestManager отслеживает процент завершения игры на основе выполненных заданий. Он может быть рассчитан автоматически (выполненные / общее количество заданий в мировой базе данных) или по фиксированному знаменателю (QuestsForHundo). Процент становится видимым для игрока, когда VisibleGamePercentage установлен в true.

float pct = questManager.GameCompletionPercentage; // 0.0 - 1.0

События​

questManager.OnQuestStarted      += (Quest q) => { ... };
questManager.OnObjectiveProgressed += (Quest q, int objIdx, int newVal) => { ... };
questManager.OnObjectiveChanged += (Quest q, int newObjIdx) => { ... };
questManager.OnQuestCompleted += (Quest q) => { ... };
questManager.OnQuestFailed += (Quest q) => { ... };
questManager.OnRewardsGranted += (Quest q, List<QuestReward> rewards) => { ... };

Сохранение / Загрузка​

QuestManager расширяет SavableObject и сериализует все состояния заданий в JSON через SavableQuestManager. Ссылки на задания хранятся по имени и разрешаются через WorldDatabase при загрузке.

Узлы CommandGraph (10)​

Система заданий предоставляет 10 узлов CommandGraph для использования в скриптах NPC и триггерах событий:

Узлы действий​

УзелОписание
StartQuestЗапустить задание с опциональным индексом начальной цели
CompleteQuestОтметить задание как завершённое (выдаёт награды)
FailQuestОтметить задание как проваленное
UpdateQuestObjectiveУстановить текущую цель на определённый индекс
ProgressQuestObjectiveУвеличить счётчик прогресса цели на дельту
UnlockGameCompletionPercentageСделать процент завершения видимым для игрока

Узлы условий​

УзелОписание
IfPlayerHasQuestВетвление, если игрок начал (или завершил) задание
IfCompletedQuestВетвление, если задание было завершено
IfQuestFailedВетвление, если задание было провалено
IfCurrentQuestObjectiveВетвление, если текущая цель задания соответствует определённому индексу

Пример: Поток задания NPC​

[IfPlayerHasQuest] -- Нет --> [Dialog: "Мне нужна помощь!"] --> [StartQuest]
-- Да --> [IfCompletedQuest] -- Да --> [Dialog: "Спасибо!"]
-- Нет --> [IfCurrentQuestObjective(1)]
-- Да --> [Dialog: "Ты нашёл это?"]
-- Нет --> [Dialog: "Продолжай искать!"]

Создание задания: Пошагово​

  1. Создайте ассет Quest: Щёлкните правой кнопкой мыши в окне Проекта, выберите MCE > Quests > Quest
  2. Установите классификацию: Выберите категорию, сложность и является ли задание основным
  3. Назначьте выдающего: Перетащите CharacterData NPC в поле Quest Giver
  4. Определите цели: Добавьте записи в список Objectives. Установите тип, цель и количество для каждой
  5. Определите награды: Добавьте записи в список Rewards. Выберите тип награды и заполните соответствующие поля
  6. Установите предварительные требования (опционально): Перетащите другие ассеты Quest в PrerequisiteQuests
  7. Настройте время/повторение (опционально): Установите TimeLimitSeconds и/или IsRepeatable с кулдауном
  8. Зарегистрируйте в WorldDatabase: Добавьте задание в WorldDatabase.Quests, чтобы оно появлялось в запросах доступности
  9. Подключите CommandGraph: Используйте узлы заданий в CommandGraph вашего NPC для запуска, отслеживания и завершения задания