現在互聯網是個業務都有任務系統,似乎任務系統是所謂“遊戲化”改造最主要的形式。因此有好多程序員就想做個“通用”的任務系統。但是,作為一個典型的產品概念,真的可以對應的設計一個可複用的技術模塊嗎?我們大概的列舉了一下碰到過的“任務”的需求,來試圖描述一下“任務系統”作為一個技術“產品”可能要滿足的需求。
任務列表
“任務列表”是“任務”這個設計最典型的表象,沒有一個 UI 面板列出要做的任務,幾乎就不能算任務系統了。但是這個面板裡面的東西,是不是都可以用某種比較固定的邏輯來實現呢?
接任務
對於任務列表裡面可以列出什麼來,常見的就有兩種不同的需求:
- 手動接任務。用戶需要進行某些操作,具體的選擇了一個想要去做的任務,這個任務的條目才會進入到任務列表裡。
- 自動接任務。只要用戶滿足某種條件,任務條目就自動進入任務列表裡。

手動接任務

自動接任務
對於以上兩種需求,顯然“手動接任務”的設計需要更復雜一些,因為需要為每個用戶都存一個數據,來記錄他接了的任務。而“自動接任務”則無需關心這個,只要是系統配置了的任務,所有人統統都有。——雖然按照“手動接任務”的程序設計,也可以實現“自動接任務”的功能,但就會多出一大堆不必要的代碼,而且更重要的是:自動接任務的系統,往往會在產品運營期間,要求統一修改某些任務,譬如統一屏蔽某個任務——這對於記錄了任務列表的存檔數據來說,就可能需要修改大量的用戶存檔,這對於服務器系統來說是一個繁重而容易出錯的操作。
可接任務的條件
對於哪些任務可以被玩家去做,不同的產品既有一些共性的需求,也有很多非常個性的需求。共性的需求包括:
- 是否可以重複的。重複的條件是哪種時間週期,還是有次數限制?
- 限時完成的任務
- 需要完成的前置任務

可重複的“日常”任務

WOW複雜的前置任務鏈條
至於個性需求,那就真的很難描述,基本上每個產品都不一樣。基於“完成”的情況,譬如時間、次數的統計,作為任務可接條件來說,需要記錄每個玩家的任務完成記錄(時間、次數等)的,但對於要求前置任務的條件,只需要記錄每個完成任務的ID就可以了,兩者直接存儲的數據可以有很大差別。當然我們也可以全部都按“可重複”任務的方式去實現,也能兼容“前置任務”的需求,但是實現的複雜程度就變得更高了。
排序
任務列表作為一個面板,上面的任務以何種順序顯示,是否有分頁,這些都是重要的設計。不太複雜的系統完全靠策劃在配置表格裡面填順序也很常見。有的系統會需要按照用戶接任務的順序排序,或者按照未完成的任務等級排序。但如果任務面板是產品的一個重要入口,那麼這種順序肯定就不可能是所有人都固定的這種設計,甚至有時候需要通過 AB 測試來對不同的用戶,動態的展現不同的任務列表。
有的系統任務數量太多了,還需要分門別類的進行展示。比較常見的就是一個二級樹的形式進行展現。有的系統會設計成完成了的任務不再顯示,而有些則需要顯示已經完成的任務。

任務面板大量排列元素
由於“排序”本身的高效實現,是需要存儲結構(索引)預先支持的,所以在需求未知的情況下,不可能準確的設計出好的存儲結構。往往最後的實現,都是寫代碼在內存裡面,按照需求來排序。這種代碼在新的排序需求面前,幾乎沒有任何的可複用能力,只能看著大段的循環體代碼來改。——有人曾經試圖設計一個“權重”系統,試圖只寫一些代碼來生成每個任務的“權重”,讓任務列表排序,根據這些“權重”自主排序,就好像不同密度的液體自己分層一樣,但排序的需求變換真的變換很大,這套設計反而增加了後續維護開發者的理解難度,還不如就是設置一個數字作為排序數值。
任務條目跳轉
任務面板上的條目,在簡單的遊戲中,可以設計為無法點擊。在比較複雜的遊戲中,則會設計為點擊之後,彈出任務詳細介紹面板。但是有很多非遊戲類應用,任務條目則是一個“子功能”的入口,也有點擊了直接彈出某個活動面板的。因此任務的配置,必須要包含所有“點擊”條目之後行為所需的數據:有可能是某個內部系統的入口ID,或者是URL的一部分……根據點擊的效果不同,這一部分的數據顯然是不一樣的。對於存儲來說,可以簡單的用一個字符串來存放這些東西,但是對於客戶端代碼來說,就必須解析這個字符串並且識別出不同的功能來實現。如果你想實現一個“通用”的任務面板,就必須要壓制住利用這個任務面板來做各種入口“運營”的需求。

跳轉到不同界面的任務
完成任務
任務完成的實現,是任務系統的最重要部分,因為“任務”就是引導玩家去做那些“完成任務”所需的行為的。而這種“引導”本身沒有什麼天然的界定,在很多不同的需求層面,會有各自的要求。
行為記錄的統計
一般任務系統都會記錄用戶的某些行為,然後用來計算是否“完成了任務”。對於普通 RPG 遊戲來說,最常見的就是“殺死X只怪物”,或者“獲得某個道具”,甚至最簡單的就是找某個 NPC 互動一下。雖然遊戲中可能涉及很複雜的玩法,但是基本上可以利用遊戲的戰鬥系統、道具系統來作為任務的輔助設計,形成容易理解也容易實現的“完成任務”功能。

遊戲中常見的任務
但是對於非遊戲類業務來說,由於缺乏“戰鬥系統”或者“道具系統”,要引導玩家去做某些行為,就會顯得異常複雜。譬如你設計一個引導玩家網購的任務,假如只統計玩家的消費金額作為任務完成記錄,那麼玩家很可能去消費那些方便退貨的商品來刷任務條件。而對於另外一些玩家來說,簡單的幾行文字可能根本無法簡單的說明任務完成的全部條件,說明太長了玩家不會有耐心看,譬如限定的消費商品種類、商家等等。
有些任務希望進行所謂的產品之間“導流”,那問題就會更加複雜,因為任務系統必須要有辦法從其他產品那裡獲得“完成任務”所需的統計數據。這種數據的格式和接口往往都是其他系統提供的,數據獲得的時機、方式、格式往往都不一樣。當然任務系統也可以規定自己的數據接口格式,要求被引導的程序系統按照規定實現,但這樣任務系統就會變得異常複雜,為了接入這個任務系統,需要大量的跨團隊的溝通和調試。更別說對於安全、鑑權方面的需求,也因為跨系統調用而增加。

銀行APP的任務有各種操作
從上面這個角度來說,如果單純的實現一個任務系統,而不是作為某個業務系統的內置模塊,對於“完成任務”這種核心邏輯的實現來說,幾乎是沒有任何可複用的代碼的。因為“做任何事情”都可以作為“任務”的記錄。
最後,有些任務系統會顯示任務完成得“進度”。譬如要求你在線10分鐘,你現在已經在線了5分支,這個進度就要顯示 50%。但是這個“進度”在不同的行為下展示方法也是不同的,不是所有的行為都可以用一個進度條來描述,譬如有的任務需要你搜集三個道具,這種就應該是三個 checkbox 打勾的展示方式。
由於“任務”這個概念太過模糊,有些“每日打卡”的引導面板、“定時活動”的引導、“成就蒐集”的獎勵,都可以被認為是“任務系統”去承擔的需求。這就更加添加了任務系統的複雜性。——過於複雜的系統,想要設計出可複用的部分,就會變得更加困難,因為可複用的部分會很小很小。

混雜各種設置的任務
獎勵配置
所有的任務都需要獎勵,否則不會有玩家來玩。最常見的獎勵是一種固定的配置數據,但是對於可重複完成的任務來說,獎勵可能是一個價值遞減的數組。也有一些獎勵是具備“隨機性”的配置。這對於獎勵配置的開發,就變得難以“通用”了。
對於非遊戲類的系統來說,很多獎勵實際上並不是“系統內”的,而是需要其他系統進行發獎的。譬如任務獎勵是一個電商的優惠券,就需要接入電商的系統,並且在安全、合規方面有特別的要求,至少會要生成一個防止錯誤重試導致重複發獎的“冪等ID”。不同的業務系統在獎品發放方面,會有各種不同的需求,這樣每個“獎勵類型”都需要寫一段特別的代碼。——這樣在發獎環節,能“通用”的代碼就變得更少了。

某APP的任務系統接入可選接口
發獎流程
有些任務系統是需要手動領獎的,也就是需要用戶去操作一下領獎的界面。而另外的一些任務系統則是自動發獎的。
手動領獎的系統,對於那種有時間限制的任務來說,可能需要處理這種情況:玩家完成了任務一直沒有領獎,任務過期了要怎麼辦。系統可以一直保留可以領獎的狀態,但是如果發獎的是其他系統,任務過期可能預示著發獎系統已經不提供服務了,這樣發獎會一直不成功,那麼如何給玩家補償又成為一個新的問題。
自動發獎的設計沒有了上面的問題,但是又有自己的問題:如何通知玩家。這種通知需要的是一個“從服務器到客戶端”的主動通知,在大量使用 HTTP 協議的 APP 類應用來說,需要新建一個 websocket 或者 SSE 之類的技術系統;或者預埋一些功能到現有的某些系統裡面進行通知。就算實現了通知的技術,還要考慮這些通知是否會打斷玩家的使用流程,譬如一個任務獎勵了5張不同的優惠券,一張張的展示很費時間,不展示又怕玩家不知道……
總結
上面的這些問題,對於某一個特定的系統來說,都不是很難解決的問題。但對於一個“通用”系統,想要同時兼容這些解決方案,會讓系統變得特別複雜,對於任務設計者來說,需要學習上面的所有這些設定,是一個非常煩人的工作。既然代碼複用程度不高,使用又特別複雜,那麼我們還真的需要去做一個“通用”的任務系統嗎?

僅剩一個架子值得去做嗎?