跳到主要内容

任务系统

任务系统提供完整的任务生命周期:创建、跟踪、推进、完成、失败、奖励、前置条件、时间限制和可重复任务。所有任务数据通过ScriptableObject定义,并与存档系统和CommandGraph集成。

架构概览​

类名用途
Quest定义任务目标、奖励、前置条件和元数据的ScriptableObject
Objective表示任务中单个可跟踪任务的序列化类
QuestReward表示完成时授予的奖励的序列化类
QuestStatus任务的运行时状态:进度计数器、时间戳、完成/失败标志
QuestManager跟踪所有任务状态、处理生命周期并触发事件的SavableObject

Quest​

通过资源菜单中的 MCE > Quests > Quest 创建任务。每个 Quest ScriptableObject 包含以下部分:

分类​

字段类型描述
IsMainQuestbool主线任务对故事推进至关重要
CategoryQuestCategory在任务日志中组织任务(见下文)
DifficultyQuestDifficultyEasy、Normal、Hard、Expert — 在UI中显示

任务类别:

  • MainStory — 主线故事推进
  • SideQuest — 可选的NPC任务
  • DailyTask — 每24小时重置
  • WeeklyChallenge — 每7天重置
  • Event — 限时活动任务
  • Collection — 填满图鉴、找到所有物品等

任务发布者​

字段类型描述
QuestGiverCharacterData分配此任务的NPC

要求​

字段类型描述
PrerequisiteQuestsList<Quest>必须先完成的任务
MinBadgesint接受任务所需的最低徽章数(0 = 无要求)
RecommendedLevelint在UI中显示,非强制要求

时间和重复​

字段类型描述
TimeLimitSecondsint以秒为单位的时间限制(0 = 无限制)。任务到期自动失败
IsRepeatablebool任务完成后是否可以重新开始
RepeatCooldownSecondsint重新接受前的冷却时间(86400 = 每日,604800 = 每周)

Objective​

每个任务包含一个 List<Objective>,定义玩家必须完成的任务。目标是按顺序排列的,可以自动推进。

目标类型(11种)​

类型描述关键字段
Talk与特定NPC对话TargetNPC
Deliver向NPC交付物品TargetNPC、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 好伤药
MoneyMoneyAmount$5,000
ExperienceExpAmount2,000 EXP分配给队伍
MonsterMonsterReward、MonsterLevelLv.20 雷鬃兽
GameVariableGameVariableKey、GameVariableValue设置 HAS_BIKE = 1
UnlockCustomUnlockId"safari_zone_access"
// 每个奖励都有面向UI的人类可读描述
reward.GetDescription(); // "好伤药 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()(例如从MonoBehaviour的Update中),自动使已超时的任务失败:

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,通过 SavableQuestManager 将所有任务状态序列化为JSON。任务引用按名称存储,并在加载时根据 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. 分配任务发布者:将NPC的 CharacterData 拖到Quest Giver字段中
  4. 定义目标:向Objectives列表添加条目。为每个条目设置类型、目标和数量
  5. 定义奖励:向Rewards列表添加条目。选择奖励类型并填写相关字段
  6. 设置前置条件(可选):将其他Quest资源拖入PrerequisiteQuests
  7. 配置时间/重复(可选):设置 TimeLimitSeconds 和/或 IsRepeatable 及冷却时间
  8. 注册到WorldDatabase:将任务添加到 WorldDatabase.Quests,以便在可用性查询中出现
  9. 配置CommandGraph:在NPC的CommandGraph中使用任务节点来启动、跟踪和完成任务