返回首页

客户端程序员视角下的任务系统设计

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

一、什么是任务

广义理解

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

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

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