核心目标:设计稳定、高效、可维护的任务系统,确保数据规则与逻辑意图能够准确传播给所有协作者

一、什么是任务

广义理解

游戏是对世界的抽象,任务就是这个抽象世界里玩家出生、成长、发挥影响力过程中一个又一个的普世目标和存在意义

  • 一方面,玩家不得不完成它
  • 另一方面,玩家完成它又会获得这个抽象世界的普世认可

狭义理解(RPG游戏)

RPG游戏是一个故事,任务就是故事中一条或多条故事线上的(时间、事件、状态)节点,用于:

  • 驱动故事发展
  • 记录玩家历程

任务系统的核心功能

基于从业经验归纳,任务系统本身及相关联系统对任务提出的核心要求:

序号 核心功能 说明
1 关键时间点记录 记录玩家行为数据,标记游戏进程
2 状态管理 未接取、进行中、已达成、已结束、已失败
3 上下文连续性 链式结构支持连续性,最好支持分支(树形结构)
4 多类型条件组合 支持逻辑关系(AND、OR)运算和优先级(括号)
5 多种条件检查方式 改变任务状态的不同触发机制
6 多种筛选规则 用于分类、显示、统计等
7 多种文本表现形式 适应不同场景的表现需求

二、任务的逻辑分类

基于当前游戏任务系统,从逻辑层面归纳以下分类:

mindmap root((任务分类)) 剧情任务 故事线的核心元素 驱动游戏主线发展 指引任务 剧情中的一部分 建立角色能力认知 游戏本身的目标引导 日常任务 短小重复 量变产生质变 易于达成的小目标 平台期目标 类似日常但跨度更长 需要几天完成 具有方向性指导 随机任务 枯燥世界中的小巧合 增加探索新鲜感 合作任务 群体参与改造世界 增加玩家间互动

分类说明

类型 特点 设计目的
剧情 核心元素 推动故事发展
指引 复杂,可内嵌剧情 建立玩家对游戏机制的认知
日常 短小重复 提供可达成的小目标
平台期目标 跨度数天 提供长期方向性指导
随机任务 偶然触发 增加探索新鲜感
合作任务 多人参与 增强玩家间互动与参与感

三、任务系统的关联系统

以下系统直接或间接与任务系统有关联:

flowchart TB subgraph 核心系统 Quest[任务系统] end subgraph 直接依赖 Guide[指引和解锁系统] Explore[探索/调查/对话] Achievement[成就系统] end subgraph 功能耦合 Monster[怪物召唤] NPC[NPC显隐] Animation[动画播放] end subgraph 表现层 Text[进度文本] Nav[目标导航] Highlight[NPC高亮] end subgraph 其他 Robot[主角助手] Activity[活动管理器] end Quest <-->|双向关联| Achievement Quest -->|时间点/进度| Guide Quest -->|记录进度| Explore Quest -->|任务中配置| Monster Quest -->|剧情状态| NPC Quest <-->|状态相互利用| Animation Quest -->|耦合配置| Text Quest -->|耦合配置| Nav Quest -->|耦合配置| Highlight Quest -->|时间点驱动| Robot Quest <-->|相互引用| Activity

关联系统详解

系统 关联方式 存在问题
指引和解锁 依赖任务的时间点和进度记录能力
探索/调查/对话 需要进度记录和奖励能力 对话系统与任务对话令人困惑
成就 双向关联,理论上不应独立 原任务配置过于复杂导致分离
怪物召唤 任务中直接配置 其他玩家看到"与空气战斗"的怪异行为
NPC显隐 为剧情状态服务
动画播放 相互利用状态与时间点 断线重连时状态基准冲突
进度文本/导航/高亮 耦合到任务配置
主角助手 以任务时间点驱动行为
活动管理器 相互引用 任务配置引用活动,活动触发任务

四、遇到的问题与困难

4.1 潜规则(最致命问题)

"来吧老哥,先这么搞,你加个默认规则不就行了么!"

"改一个字段不是还要全部刷数据么,单独加个判断特殊处理一下!"

"先跑起来!"

典型案例:任务目标显示规则

早期实现

目标类型 默认显示规则
杀怪 "杀怪某某 x/n"
物品 "收集物品某某 x/n"
... ...
任务完成 "找到某某某"

问题演化

  1. 早期只有 3-4 条目标类型
  2. 项目发展后膨胀到 8-9 条目标类型
  3. 需要自定义格式化字符串
  4. 默认规则 + 自定义逻辑并行运行

最终爆发

打包前夜报错 → 默认显示规则引用怪物包数据 → 怪物包没有名字定义 → 进不去游戏

根本原因

  • 怪物配置升级为怪物包配置时,使用自定义格式未暴露问题
  • 使用默认规则时触发空引用错误

💡 教训:如果开始就按照最原子的逻辑实现,返工与Bug会少很多。

4.2 空白逻辑持续发挥余热

现象

  • 2017年9月编写的逻辑,2019年4月仍未使用
  • 大量空白字段被所有任务携带,但只有极个别任务使用

具体例子

字段 状态
接取条件:玩家温度上下限 从未使用
接取时需求技能id数组 从未使用
大部分空白逻辑字段 极少数任务使用

带来的问题

  1. 占用屏幕空间 - 打开配置表需要不断按右方向键寻找关键列
  2. 代码冗余 - 仅接取条件判断就写了 200+
  3. 空引用错误 - 老数据缺少新条件项
  4. Debug困难 - 配置同学、程序同学信息不完整

4.3 完全手动的跨表引用

典型案例:提交任务配置

任务配置表
    ↓ 配置完成提交NPC
提交服务配置
    ↓ 加到NPC服务组件
NPC服务组件
    ↓ 填到NPC表
NPC表

以上步骤缺一不可,否则分分钟报错。

高耦合问题

场景 影响
地编重新种NPC并导出 其他任务表要同步修改位置
区域触发任务 区域信息中记录,任务配置表还要记录区域id

文本信息跨表混乱

文本类型 处理方式
任务目标 游戏程序逻辑分别处理
任务说明 配置表导出工具替换
对话文本 未处理

风险:某系统元素改名 → 显示问题或额外工作量


4.4 导出数据与数据源不一致

分类一:导出落后于Excel

现象:游戏内表现与期望不一致,Excel数据源没问题,但导出数据有差别

多发场景:多人合作中,某人修改内容未及时同步导出数据

直接影响:打包节点报错,需等下一个包才能继续工作

分类二:数据结构演化未全刷表

原因:最后修改数据结构的同学对全刷表正确性没信心

背景: - 几千条数据 - 数个同学在一两个月版本迭代中陆续增删改 - 部分数据因功能、测试环境等改变而无效/过期

风险: - 承担巨大核对工作量 - 或承担出包后数据错误的责任风险

集合对应不合理

数据源集合 导出数据集合
正式数据 正式数据(理想)
测试数据 测试数据(开发需要)
废弃数据 ❌ 废弃数据(版本演化残留)
开发中数据 ❌ 不在数据源中的数据

缺失:数据源中缺乏数据类别管理,导出数据没有一对一映射方式

4.5 链式依赖

典型案例

任务A完成条件 → 依赖成就B达成
成就B达成条件 → 依赖任务C/D/E/F...完成

难点

如何显示任务A的进度?

方案 问题
显示"需要达成某成就" 需另跨页面查看成就进度
直接显示成就进度 与原逻辑不合,通用性不强

4.6 任务的多个接取时机

被动条件(轮询)

条件类型 说明
玩家等级、声望、成就 基础属性检查
游戏内时间、昼夜 时间相关
前置、互斥条件 任务关系

触发方式:NPC携带任务,玩家交互时轮询

主动触发

触发方式 说明
进入区域 区域触发
活动开启 活动触发

组合问题

案例:进入区域 + 只有夜晚才能接取

问题场景: 1. 玩家白天进入区域 2. 挂机等到晚上 3. 原本被动的接取条件需要考虑主动触发

风险:放开任意组合,补丁逻辑将非常复杂

4.7 断线以后的连续性

问题场景

任务完成条件:观看指定动画

正常流程

进入区域 → 接取任务 → 播放动画 → 动画完成 → 完成任务

断线后问题: - 任务已在玩家身上 → 不会播放接取动画 - 没播放指定动画 → 无法完成需要观看动画的任务

死循环

解决方案

进入区域 → 接取任务
离开地图 → 放弃任务
任务接取 → 播放动画 → 动画完成 → 完成任务

原理: - 掉线 = 离开地图 → 放弃任务 - 上线 = 首次进入区域 → 重新接取 → 完整播放动画

遗留问题: - 其他控制动画的逻辑(区域) - 断线后的位置同步问题 - 逻辑冲突


五、优化设计方案

5.1 任务状态与转移条件

状态定义

stateDiagram-v2 [*] --> 未接取 未接取 --> 进行中: 接取条件满足 进行中 --> 已达成: 达成条件满足 进行中 --> 已失败: 失败条件满足 已达成 --> 已结束: 提交条件满足 已达成 --> 进行中: 回退达成条件 已失败 --> [*] 已结束 --> [*] 未接取 --> [*]: 放弃(隐藏状态)

状态说明

状态 说明
未接取 任务存在但玩家未接取
进行中 玩家已接取,正在完成
已达成 完成条件满足,等待提交
已结束 任务完成,流程结束
已失败 任务失败,流程结束
已放弃(隐藏) 等同于未接取

状态转移触发机制

转移 触发时机
未接取 → 进行中 "接取时机"触发
进行中 → 已达成 "达成条件"满足
进行中 → 已失败 "失败条件"满足
已达成 → 已结束 "提交条件"满足
已达成 → 进行中 "回退达成条件"(特殊)

:省略"任务更新"时机,改为监听各个"条件更新"


5.2 任务之间的组织关系

树形结构

graph TD A[主线任务: 启程] --> B[子任务: 收集物资] A --> C[子任务: 寻找向导] B --> D[子子任务: 购买工具] B --> E[子子任务: 采集草药] C --> F[分支: 选择向导A] C --> G[分支: 选择向导B] style A fill:#e3f2fd style F fill:#fff3e0 style G fill:#fff3e0

树形结构的优势

优势 说明
连续上下文 父子任务提供逻辑连续性
分支能力 兄弟任务提供额外分支
分组逻辑 无需额外的"任务包"配置表

注意事项

  • 需要完善的配表工具
  • 否则配置相对吃力

5.3 条件系统设计

当前系统的条件数量

条件类别 数量 说明
接取类型 11
接取条件 16+ 与接取类型组合
完成/提交条件 15 最多3条,AND/OR逻辑
失败条件 1 超时失败
重做条件 1 限次包
放弃条件 2 手动放弃、离开地图
成就首次达成 10 升级、创建公会等
成就积累型 48 收集物品、杀怪等

⚠️ 这些类型在可预见的将来还会不可控制地迅速膨胀

条件抽象方案对比

方案 优点 缺点
完整脚本(Lua) 非常灵活 配置难度大,依赖关系难整理
DSL/表达式 兼顾可读性与表达性 需要解析程序,触发类型条件难处理
类型枚举+参数 Excel配置方便 逻辑组织局限(默认AND)

推荐方案:条件配置表 + 逻辑表达式

设计思路

// 任务数据
{
    "2001": {
        "accept_condition": "(010001 and 010002) or 010003",
        "complete_condition": "xxxxxx"
    }
}

// 条件数据
{
    "010001": {"type": 1, "param_1": 10},
    "010002": {"type": 2, "param_1": 11, "param_2": 7},
    "010003": {"type": 0, "param_1": 5, "param_2": 1010101}
}

解析方式

class Condition {
    virtual bool Match(param);
};

class AND : public Condition {
    virtual bool Match(param) {
        return left.Match(param) && right.Match(param);
    }
};

// 解析表达式树
Condition* condition = Parse("(010001 and 010002) or 010003");
if (condition->Match(param)) {
    // 状态转移
}

客户端与服务器的不对称性

mask 定义
0x00 没有忽略
0x01 服务器忽略,返回 False
0x02 服务器忽略,返回 True
0x04 客户端忽略,返回 False
0x08 客户端忽略,返回 True

示例

"010004": {
    "type": 0,
    "param_1": 5,
    "param_2": 1010101,
    "ignore": "0x06"  // 服务器和客户端都忽略
}

5.4 条件的检查方式

flowchart LR A[条件检查] --> B{检查方式} B -->|定期执行| C[轮询] B -->|事件驱动| D[触发] C --> C1[逻辑简单] C --> C2[不适合大量检查] D --> D1[逻辑复杂] D --> D2[功能灵活] style C fill:#fff3e0 style D fill:#e3f2fd

检查方式对比

方式 优点 缺点 适用场景
轮询 逻辑简单 不适合大量检查 NPC交互任务
触发 功能灵活 逻辑复杂 区域、活动触发

当前系统的检查方式分布

任务类型 检查方式
NPC身上交接的任务 轮询
区域、活动触发的任务 触发
进行中的达成条件 触发

⚠️ 警告:混乱地不加定义地使用不同检查方式,会遇到解决不完的问题


5.5 条件计数(执行进度)

计数数据的存在位置

类型 处理方式
需要记录过去触发状态 服务器更新计数
客户端无对应数据 服务器更新计数
客户端可自己判断 本地更新计数

设计建议

  • 明确定义哪些条件需要服务器计数
  • 哪些条件客户端可以本地处理
  • 状态变化可与其他类型条件复用

5.6 状态切换时的行为

建议整合的行为

行为 当前实现 建议整合
任务完成后接取新任务 默认行为 ✅ 保留
任务完成后没收物品 明确定义 ✅ 保留
发奖 单独规定 🔧 整合到状态切换行为

整合发奖逻辑的优势

  1. 逻辑表达更顺畅
  2. 避免程序开天窗写特例
  3. 支持任务分支逻辑 - 完成后走分支A - 失败后走分支B

5.7 相似功能的整合

对话系统

当前问题

功能 问题
对话 单独记录进度,散乱逻辑
任务对话 接取前/完成前/完成后显示
合并问题 两个UI可能叠在一起

建议方案

flowchart TD A[统一对话系统] --> B[定义分支行为] B --> C{任务状态} C -->|接取前| D[显示接取对话] C -->|进行中| E[显示未完成对话] C -->|完成后| F[显示完成对话] D --> G[触发任务接取条件] F --> H[触发任务完成]

核心思想

  • 任务是玩家游戏进程的条件与状态转移链
  • 对话引用玩家任务的完成状态
  • 避免逻辑与表现混在一起

成就系统

当前问题

  • 成就与任务双向关联
  • "完成任务A,B,C...时完成成就D"
  • "完成成就D时完成任务E"
  • 车轱辘逻辑,程序开天窗打补丁

建议方案

成就是一系列特殊的任务

方案 实现
静态配置 角色创建后天生携带的任务id
动态配置 达到某阶段接取某系列任务

显示:只是状态的包装,不应与逻辑冲突

动画播放

建议:归纳到任务状态切换时的行为

  • 特定时间
  • 特定区域
  • 播放行为
  • 包括动画完成传送等功能

断线问题:通过粒度更小的任务逻辑节点,保存动画播放进度


5.8 任务的显示与包装

任务标签(Tag)

设计目的:解决"它是什么也是什么"的多维度分类问题

{
    "id": 2001,
    "tag": ["Secondary", "Story", "Visable", "Night"],
    "comment": "玩家看得见的夜间才能完成的支线剧情任务"
},
{
    "id": 2002,
    "tag": ["Trophy", "Stage1"],
    "comment": "玩家在阶段1的成就任务"
}

对比

方式 类比
任务类型 QQ朋友分组(单一维度)
任务标签 微信标签(多维度)

剧情信息

建议:任务不应包含显示文本信息

原因 说明
1 大量功能任务不需要文本,避免浪费
2 单独设计文本树,任务状态控制分支
3 解耦表现与底层逻辑

进度文本与导航

当前配置问题

{
    "id": 2001,
    "commit_npc": 10000,
    "commit_info": {"pos": "x,y,z", "map_id": 11},
    "commit_comment": "balabala",
    "complete_condition": [
        {
            "target_comment": "balabala",
            "target_param_xxx": 1,
            "target_info": {"pos": "x,y,z", "map_id": 11, ...}
        }
    ]
}

理想配置

{
    "id": 2001,
    "comment": "balabala",
    "map_id": 11,
    "pos": "x,y,z",
    "npc_ids": [10001]
}

优势

  • 导航配置可给其他系统使用(资源收集导航)
  • 通过任务树包装表现任务

六、总结

6.1 核心优化方向

mindmap root((优化方向)) 明确状态定义 5个关键状态 7种转移条件 清晰的触发机制 树形组织结构 父子提供连续性 兄弟提供分支 减少额外配置表 独立条件配置 条件配置表 逻辑表达式 支持AND/OR 统一检查方式 明确定义轮询/触发 避免混合使用 整合相似功能 对话统一 成就即任务 动画纳入状态行为 标签化显示 多维度分类 解耦逻辑与表现

6.2 关键原则

原则 说明
明确定义 状态、条件、检查方式都要有清晰定义
缩减整合 合并重叠功能,减少配置复杂度
解耦分离 逻辑与表现分离,降低耦合度
工具支持 持续推进有效的配置程序插件

6.3 关于数据与视图

真实存储的数据与看到的数据不一定一致

  • 数据库:3范式下的无冗余关系表
  • 终端用户(策划):关联过的视图表
  • 进一步拓展:针对不同策划角色的专用视图
  • 剧情策划:条件、文本、导航、奖励、父子节点
  • 成就策划:简单条件与初始化接取
  • 关卡策划:动画播放与进度记录时机