Skip to content

任务系统 ​

Category ​

system / 玩法系统。节点定位:任务把「策划写的一段玩法叙事」变成「服务端可推进、可发奖、可对账的进度对象」——定义、进度、链式前置、奖励发放四件事。数据表管线在 配置表与数据驱动管线、奖励凭证在 结算、产出与回收的数值口径在 numerical 树,本页只管进度机制本身。

Definition ​

任务是「带条件的进度对象」:一条任务定义给出目标(做什么、做几次)、前置(能不能接)、奖励(做完了给什么),玩家侧持有一条进度记录。它与成就同构——都是事件驱动的计数器与状态判定——区别在任务有接取与交付两个显式节点、可被放弃与重接,成就的进度只增不减、无交付动作。任务与成就共用同一套进度模型,分开写只是因为运营侧对两者的心智不同。

Problem ​

任务的坑按出现顺序:

  • 进度与业务耦合:把「击杀 3 只狼」的判断写死在战斗代码里,加一个任务就要改一次战斗服。任务量一大,战斗代码里全是 if quest_id == 1001。
  • 链式前置断链:任务 A 完成要解锁任务 B,但玩家的进度记录与前置记录分两处存,前置判定用旧快照,出现「做了 A 却接不了 B」。
  • 重复发奖:交付动作重试、重连重放、崩溃恢复都可能把同一笔奖励发两次或零次——与结算同款的幂等问题。
  • 进度回滚:事件重复投递(网络重发)让「击杀数」多计,或服务端重启丢了内存中的进度。

先定「谁推进进度」(事件源),再定「何时发奖」(交付提交点),最后定「前置怎么判」——顺序反了,链式任务的事后补丁最难写。

Algorithm ​

进度模型:定义与运行态分离 ​

任务定义是静态数据,运行态只有一条进度记录:

text
quest_def:                       # 静态表,策划维护,热更可换
  id:            q-1001
  objectives:    [ {type: kill, key: wolf, need: 3},
                   {type: collect, key: pelt, need: 2} ]
  prereq:        [ q-1000 ]
  rewards:       [ {item: gem, n: 10}, {exp: 500} ]
  rule_version:  v3

quest_progress:                  # 运行态,每玩家每条任务一行
  player:        u1001
  quest:         q-1001
  state:         in_progress     # locked / available / in_progress / completed / rewarded
  counters:      { kill:wolf: 1, collect:pelt: 0 }
  updated_at:    2026-10-09T10:00:00Z

运行态只存「计数器 + 状态」,不存文案与奖励——那些每次从定义表读。这样改文案、调奖励只动表,不动玩家数据。

进度推进:事件驱动,而非轮询 ​

服务端把玩家行为发成统一事件(击杀、拾取、到达、对话),任务层订阅事件并匹配 (type, key) 自增计数器:

  • 事件源要收敛:战斗、拾取、移动各出各的事件,但格式统一成 {player, type, key, n},任务层只认这一个格式
  • 自增要幂等:事件带唯一的 event_id,进度记录里存最近处理过的 event_id 或按 (quest, objective, event_id) 去重,网络重发不会多计
  • 计数只增不减是简化前提:需要「减少」的任务(如「保持不被发现」)用状态判定而非计数器,单独一类

链式前置:状态机,不是快照 ​

任务状态机与前置的关系:

状态含义进入条件
locked前置未满足初始态,前置任务未 rewarded
available可接取全部前置 rewarded(按运行态实时判定,非快照)
in_progress进行中玩家接取
completed目标达成待交付所有 objective 计数达标
rewarded已发奖终结交付提交成功

前置判定必须读运行态而非缓存快照——这是「做了 A 却接不了 B」的根因。解锁在「前置任务 rewarded」的那一刻写一条「待解锁」记录,玩家上线时按记录刷 available,避免全表扫描。

奖励发放:交付即提交点 ​

交付(交付动作那一刻)是提交点,与结算共用凭证口径:

  • 交付时先写奖励凭证 (player, quest_id, rewards, rule_version),再落库——凭证先行,崩溃可重放
  • 落库按 (player, quest_id) 去重——重复交付直接跳过
  • 一次性任务与日常任务的差别只在重置规则:日常按服务器日重置进度(重置口径见内文体力页同类问题),一次性任务 rewarded 即终态

自动交付 vs 手动交付 ​

形态触发优点代价
自动交付目标达成即发奖少一次点击,反馈即时玩家看不到「完成」仪式感;连线中断时发奖时机难对齐
手动交付玩家点「交付」明确的完成仪式;交付前可放弃需处理「完成了但没交」的悬挂态
混合日常自动、主线手动各取所长两套路径都要测

Used By ​

  • TrinityCore(开源 MMORPG 服务端套件,公开仓库与 wiki):任务定义、达成条件、奖励各自成数据表,服务端按事件推进进度,加任务不改引擎代码——这是本页「定义与运行态分离」的直接参照。具体表名以项目公开文档为准。
  • WoW(商业 MMO):任务链、日常/周常任务、任务日志上限的公开设计事实,作为链式前置与重置规则的商业参照。
  • FF14(商业 MMO):主线任务门槛与任务同步机制(主线进度锁副本解锁)作为「任务作为玩法闸门」的参照。
  • 原神(商业手游):每日委托的每日重置与纪行任务联动,作为日常任务与运营任务两种形态并存的参照。

可参考分析:TrinityCore 任务数据表化=可参考(定义与运行态分离直接落本页进度模型);WoW 任务链与日常=可借鉴(链式前置与重置规则落状态机与一次性/日常一节);FF14 主线锁副本=可借鉴(任务作玩法闸门,落点 design 树进度设计);原神每日委托=可参考(日常重置与运营任务并存,落次数门控窗口表);事件驱动幂等=可借鉴(落进度推进一节,与结算凭证口径对齐)。

关系链:进度模型 → 事件推进 → 链式前置 → 奖励发放 → 交付形态。逐段回答「为什么需要下一个」:

  • 定义了进度对象,才谈得上推进——没有运行态,事件无处可落。
  • 能推进了才要前置——链式任务的前置判定必须读运行态,否则断链。
  • 前置顺畅了才发奖——交付是提交点,凭证先行保证不重发不漏发。
  • 发奖路径定了才选交付形态——自动与手动决定「完成」到「发奖」之间有没有人。

上游:配置表与数据驱动管线(定义表怎么热更)、技能系统(任务目标的常见事件源);下游:成就与图鉴(同构的事件订阅)、结算(奖励凭证的落库口径)、掉落与概率设计(任务奖励的数值侧)。

Reference ​

  • 本页为机制归纳;TrinityCore 的表结构以其公开仓库与 wiki 为准,不臆造表名与列名。
  • 奖励凭证与幂等口径:结算 与 交易、支付与经济系统。
  • 术语对照:Quest(任务)、Objective(任务目标)、Prerequisite(前置)、Turn-in(交付)、Daily Reset(每日重置)。

游戏知识体系