任务系统
任务系统提供完整的任务生命周期:创建、跟踪、推进、完成、失败、奖励、前置条件、时间限制和可重复任务。所有任务数据通过ScriptableObject定义,并与存档系统和CommandGraph集成。
架构概览
| 类名 | 用途 |
|---|---|
Quest | 定义任务目标、奖励、前置条件和元数据的ScriptableObject |
Objective | 表示任务中单个可跟踪任务的序列化类 |
QuestReward | 表示完成时授予的奖励的序列化类 |
QuestStatus | 任务的运行时状态:进度计数器、时间戳、完成/失败标志 |
QuestManager | 跟踪所有任务状态、处理生命周期并触发事件的SavableObject |
Quest
通过资源菜单中的 MCE > Quests > Quest 创建任务。每个 Quest ScriptableObject 包含以下部分:
分类
| 字段 | 类型 | 描述 |
|---|---|---|
IsMainQuest | bool | 主线任务对故事推进至关重要 |
Category | QuestCategory | 在任务日志中组织任务(见下文) |
Difficulty | QuestDifficulty | Easy、Normal、Hard、Expert — 在UI中显示 |
任务类别:
MainStory— 主线故事推进SideQuest— 可选的NPC任务DailyTask— 每24小时重置WeeklyChallenge— 每7天重置Event— 限时活动任务Collection— 填满图鉴、找到所有物品等
任务发布者
| 字段 | 类型 | 描述 |
|---|---|---|
QuestGiver | CharacterData | 分配此任务的NPC |
要求
| 字段 | 类型 | 描述 |
|---|---|---|
PrerequisiteQuests | List<Quest> | 必须先完成的任务 |
MinBadges | int | 接受任务所需的最低徽章数(0 = 无要求) |
RecommendedLevel | int | 在UI中显示,非强制要求 |
时间和重复
| 字段 | 类型 | 描述 |
|---|---|---|
TimeLimitSeconds | int | 以秒为单位的时间限制(0 = 无限制)。任务到期自动失败 |
IsRepeatable | bool | 任务完成后是否可以重新开始 |
RepeatCooldownSeconds | int | 重新接受前的冷却时间(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种)
| 类型 | 字段 | 示例 |
|---|---|---|
Item | ItemReward、ItemQuantity | 5x 好伤药 |
Money | MoneyAmount | $5,000 |
Experience | ExpAmount | 2,000 EXP分配给队伍 |
Monster | MonsterReward、MonsterLevel | Lv.20 雷鬃兽 |
GameVariable | GameVariableKey、GameVariableValue | 设置 HAS_BIKE = 1 |
Unlock | CustomUnlockId | "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: "继续找吧!"]