现在互联网是个业务都有任务系统,似乎任务系统是所谓“游戏化”改造最主要的形式。因此有好多程序员就想做个“通用”的任务系统。但是,作为一个典型的产品概念,真的可以对应的设计一个可复用的技术模块吗?我们大概的列举了一下碰到过的“任务”的需求,来试图描述一下“任务系统”作为一个技术“产品”可能要满足的需求。
任务列表
“任务列表”是“任务”这个设计最典型的表象,没有一个 UI 面板列出要做的任务,几乎就不能算任务系统了。但是这个面板里面的东西,是不是都可以用某种比较固定的逻辑来实现呢?
接任务
对于任务列表里面可以列出什么来,常见的就有两种不同的需求:
- 手动接任务。用户需要进行某些操作,具体的选择了一个想要去做的任务,这个任务的条目才会进入到任务列表里。
- 自动接任务。只要用户满足某种条件,任务条目就自动进入任务列表里。

手动接任务

自动接任务
对于以上两种需求,显然“手动接任务”的设计需要更复杂一些,因为需要为每个用户都存一个数据,来记录他接了的任务。而“自动接任务”则无需关心这个,只要是系统配置了的任务,所有人统统都有。——虽然按照“手动接任务”的程序设计,也可以实现“自动接任务”的功能,但就会多出一大堆不必要的代码,而且更重要的是:自动接任务的系统,往往会在产品运营期间,要求统一修改某些任务,譬如统一屏蔽某个任务——这对于记录了任务列表的存档数据来说,就可能需要修改大量的用户存档,这对于服务器系统来说是一个繁重而容易出错的操作。
可接任务的条件
对于哪些任务可以被玩家去做,不同的产品既有一些共性的需求,也有很多非常个性的需求。共性的需求包括:
- 是否可以重复的。重复的条件是哪种时间周期,还是有次数限制?
- 限时完成的任务
- 需要完成的前置任务

可重复的“日常”任务

WOW复杂的前置任务链条
至于个性需求,那就真的很难描述,基本上每个产品都不一样。基于“完成”的情况,譬如时间、次数的统计,作为任务可接条件来说,需要记录每个玩家的任务完成记录(时间、次数等)的,但对于要求前置任务的条件,只需要记录每个完成任务的ID就可以了,两者直接存储的数据可以有很大差别。当然我们也可以全部都按“可重复”任务的方式去实现,也能兼容“前置任务”的需求,但是实现的复杂程度就变得更高了。
排序
任务列表作为一个面板,上面的任务以何种顺序显示,是否有分页,这些都是重要的设计。不太复杂的系统完全靠策划在配置表格里面填顺序也很常见。有的系统会需要按照用户接任务的顺序排序,或者按照未完成的任务等级排序。但如果任务面板是产品的一个重要入口,那么这种顺序肯定就不可能是所有人都固定的这种设计,甚至有时候需要通过 AB 测试来对不同的用户,动态的展现不同的任务列表。
有的系统任务数量太多了,还需要分门别类的进行展示。比较常见的就是一个二级树的形式进行展现。有的系统会设计成完成了的任务不再显示,而有些则需要显示已经完成的任务。

任务面板大量排列元素
由于“排序”本身的高效实现,是需要存储结构(索引)预先支持的,所以在需求未知的情况下,不可能准确的设计出好的存储结构。往往最后的实现,都是写代码在内存里面,按照需求来排序。这种代码在新的排序需求面前,几乎没有任何的可复用能力,只能看着大段的循环体代码来改。——有人曾经试图设计一个“权重”系统,试图只写一些代码来生成每个任务的“权重”,让任务列表排序,根据这些“权重”自主排序,就好像不同密度的液体自己分层一样,但排序的需求变换真的变换很大,这套设计反而增加了后续维护开发者的理解难度,还不如就是设置一个数字作为排序数值。
任务条目跳转
任务面板上的条目,在简单的游戏中,可以设计为无法点击。在比较复杂的游戏中,则会设计为点击之后,弹出任务详细介绍面板。但是有很多非游戏类应用,任务条目则是一个“子功能”的入口,也有点击了直接弹出某个活动面板的。因此任务的配置,必须要包含所有“点击”条目之后行为所需的数据:有可能是某个内部系统的入口ID,或者是URL的一部分……根据点击的效果不同,这一部分的数据显然是不一样的。对于存储来说,可以简单的用一个字符串来存放这些东西,但是对于客户端代码来说,就必须解析这个字符串并且识别出不同的功能来实现。如果你想实现一个“通用”的任务面板,就必须要压制住利用这个任务面板来做各种入口“运营”的需求。

跳转到不同界面的任务
完成任务
任务完成的实现,是任务系统的最重要部分,因为“任务”就是引导玩家去做那些“完成任务”所需的行为的。而这种“引导”本身没有什么天然的界定,在很多不同的需求层面,会有各自的要求。
行为记录的统计
一般任务系统都会记录用户的某些行为,然后用来计算是否“完成了任务”。对于普通 RPG 游戏来说,最常见的就是“杀死X只怪物”,或者“获得某个道具”,甚至最简单的就是找某个 NPC 互动一下。虽然游戏中可能涉及很复杂的玩法,但是基本上可以利用游戏的战斗系统、道具系统来作为任务的辅助设计,形成容易理解也容易实现的“完成任务”功能。

游戏中常见的任务
但是对于非游戏类业务来说,由于缺乏“战斗系统”或者“道具系统”,要引导玩家去做某些行为,就会显得异常复杂。譬如你设计一个引导玩家网购的任务,假如只统计玩家的消费金额作为任务完成记录,那么玩家很可能去消费那些方便退货的商品来刷任务条件。而对于另外一些玩家来说,简单的几行文字可能根本无法简单的说明任务完成的全部条件,说明太长了玩家不会有耐心看,譬如限定的消费商品种类、商家等等。
有些任务希望进行所谓的产品之间“导流”,那问题就会更加复杂,因为任务系统必须要有办法从其他产品那里获得“完成任务”所需的统计数据。这种数据的格式和接口往往都是其他系统提供的,数据获得的时机、方式、格式往往都不一样。当然任务系统也可以规定自己的数据接口格式,要求被引导的程序系统按照规定实现,但这样任务系统就会变得异常复杂,为了接入这个任务系统,需要大量的跨团队的沟通和调试。更别说对于安全、鉴权方面的需求,也因为跨系统调用而增加。

银行APP的任务有各种操作
从上面这个角度来说,如果单纯的实现一个任务系统,而不是作为某个业务系统的内置模块,对于“完成任务”这种核心逻辑的实现来说,几乎是没有任何可复用的代码的。因为“做任何事情”都可以作为“任务”的记录。
最后,有些任务系统会显示任务完成得“进度”。譬如要求你在线10分钟,你现在已经在线了5分支,这个进度就要显示 50%。但是这个“进度”在不同的行为下展示方法也是不同的,不是所有的行为都可以用一个进度条来描述,譬如有的任务需要你搜集三个道具,这种就应该是三个 checkbox 打勾的展示方式。
由于“任务”这个概念太过模糊,有些“每日打卡”的引导面板、“定时活动”的引导、“成就搜集”的奖励,都可以被认为是“任务系统”去承担的需求。这就更加添加了任务系统的复杂性。——过于复杂的系统,想要设计出可复用的部分,就会变得更加困难,因为可复用的部分会很小很小。

混杂各种设置的任务
奖励配置
所有的任务都需要奖励,否则不会有玩家来玩。最常见的奖励是一种固定的配置数据,但是对于可重复完成的任务来说,奖励可能是一个价值递减的数组。也有一些奖励是具备“随机性”的配置。这对于奖励配置的开发,就变得难以“通用”了。
对于非游戏类的系统来说,很多奖励实际上并不是“系统内”的,而是需要其他系统进行发奖的。譬如任务奖励是一个电商的优惠券,就需要接入电商的系统,并且在安全、合规方面有特别的要求,至少会要生成一个防止错误重试导致重复发奖的“幂等ID”。不同的业务系统在奖品发放方面,会有各种不同的需求,这样每个“奖励类型”都需要写一段特别的代码。——这样在发奖环节,能“通用”的代码就变得更少了。

某APP的任务系统接入可选接口
发奖流程
有些任务系统是需要手动领奖的,也就是需要用户去操作一下领奖的界面。而另外的一些任务系统则是自动发奖的。
手动领奖的系统,对于那种有时间限制的任务来说,可能需要处理这种情况:玩家完成了任务一直没有领奖,任务过期了要怎么办。系统可以一直保留可以领奖的状态,但是如果发奖的是其他系统,任务过期可能预示着发奖系统已经不提供服务了,这样发奖会一直不成功,那么如何给玩家补偿又成为一个新的问题。
自动发奖的设计没有了上面的问题,但是又有自己的问题:如何通知玩家。这种通知需要的是一个“从服务器到客户端”的主动通知,在大量使用 HTTP 协议的 APP 类应用来说,需要新建一个 websocket 或者 SSE 之类的技术系统;或者预埋一些功能到现有的某些系统里面进行通知。就算实现了通知的技术,还要考虑这些通知是否会打断玩家的使用流程,譬如一个任务奖励了5张不同的优惠券,一张张的展示很费时间,不展示又怕玩家不知道……
总结
上面的这些问题,对于某一个特定的系统来说,都不是很难解决的问题。但对于一个“通用”系统,想要同时兼容这些解决方案,会让系统变得特别复杂,对于任务设计者来说,需要学习上面的所有这些设定,是一个非常烦人的工作。既然代码复用程度不高,使用又特别复杂,那么我们还真的需要去做一个“通用”的任务系统吗?

仅剩一个架子值得去做吗?