因為我一直想做個 rpg 遊戲,而對於做 rpg 來說,科學地管理遊戲劇情是不可或缺的,所以我對於這方面做了一些探索。
初期探索階段
計算機領域有句話叫不要重複造輪子,意思就是與其自己從零開始探索製作,不如藉助已有的東西。我作為一個懶惰的人也深諳此道,所以一開始我就探索了各式各類的劇情管理軟件,最後發現主要是這幾個比較能夠用於實戰:
- yarn:腳本語言和卡片的結合管理,算是在靈活和方便管理中取個折中。
- ink:完全由腳本語言管理劇情,優點是極致的靈活,用寫代碼的思路來管理劇情。缺點也很明顯,基本上不支持多語言。
- articy draft:基於卡片式管理劇情,更像是一個遊戲的數據庫,能夠定義各類數據嵌入,但是靈活程度有限(至少是不方便在劇情中嵌入代碼,或者是有方法但是我不知道?)
- excel:沒錯!大道至簡大巧不工,也有不少人用 excel 管理一整個遊戲劇情,畢竟 excel 都能用來玩三國殺,管理遊戲劇情也自然不在話下是吧
上面一圈試用下來,每個軟件都有一些我的不爽點。例如大部分卡片式的劇情管理軟件都是讓你拖線連接創建卡片,我是覺得手動拖線基本上屬於偽需求,因為大部分情況下劇情都是線性執行,只有在一些關鍵節點才會做跳轉,為了小部分的場景就要大部分時間都要浪費在拖線連接上屬於得不償失了。然後我是打算在劇情管理軟件中嵌入一些遊戲邏輯,所以需要軟件能夠給個特殊的節點觸發事情或者能夠直接嵌入代碼。
雖然上述軟件都有實戰案例,我也很清楚它們雖然可能不是那麼完美,但是做一些騷操作應該能夠滿足我的需求,但是一個 rpg 大部分時間都要放在搞劇情上,一個好的劇情管理軟件能夠極大提升開發心情,所以最後我還是打算自己擼一個簡易的劇情管理軟件。
第一次嘗試
經過上述的嘗試我大致得出以下需求:
- 絕對不要手動拖線,不要玩連連看
- 能夠看情況觸發事件
當時看到一個 開發者實現的劇情管理系統 感覺挺符合我的想法的,於是就按照視頻上的軟件的形式做了一個。
我是用 godot 引擎來做遊戲,由於 godot 做插件拓展能力強,所以當時也很自然直接在 godot 上實現了這樣一個劇情管理的插件:
可以看出,我在這個軟件根據情況設置了一些的數據類型,基本上整體是按照樹形的結構來往下堆數據,在當時暫時滿足了我的需求,而且感覺用得還挺愉快,直到...
不爽
樹形管理的方式有個比較致命的缺點,那就是很難自由地從一個對話點隨意跳到另外一個對話點上,而隨著我的遊戲開發進展,這種靈活性是必要的。
雖然我嘗試在這基礎上修修補補來實現這一點,但是即使功能能夠實現,從管理來說也不太直觀,我感覺到沒辦法再繼續這樣妥協下去了。
新的構思
由於中途過了一段時間,我也有了各種奇奇怪怪的積累和想法,例如為了管理遊戲中各種數據搞了一個 通用遊戲數據管理軟件,中途參加了各類的 gamejam 和一些平時的想法做了各形各類的遊戲,發現只做一個遊戲可能只用市面上現成的軟件湊合下能勉強做出來,但是如果想做一系列遊戲就必須整合自己的工具鏈才能多快好省地實現,而我傾向是與其花大精力做一個遊戲,不如花不大不小的精力做多個遊戲。
基於這個理念,我對於自己做出來的軟件要求是足夠靈活,能夠適應自己做不同遊戲的需求,同時我看了一篇關於劇情管理的文章 深受啟發,而且我想做的遊戲劇情點也較為分散,符合文章中 storylet 的想法,於是打算結合這篇文章的想法再做個軟件。
重構!
既要追求刺激,那就要貫徹到底了,這時候我打算做得與其說是一個單獨管理劇情的軟件,不如說是一個定位相當於 articy draft 的遊戲數據庫,當然功能肯定不會有它那麼全,反正我也不太需要太多功能,重要的是簡潔並高效,這時我的需求如下:
- 還是不要玩連連看,這個是底線
- 靈活與方便管理間取得平衡,可以各節點間動態自由跳轉,可以嵌入代碼
- 支持多語言
確定好方向後,後面的開發就水到渠成了,為了美觀還有開發效率,就使用我最熟悉的 web 技術棧實現了。
成品
經過了一段時間的開發和調整,最後做出來這樣的東西:
這個軟件管理劇情的方式是分散式的,側邊欄展示每個故事片段或者數據,因為這個軟件定位是一個遊戲數據庫,所以除了管理故事還要管理一些靜態數據例如物品、敵人這些。
對於故事的管理是採用思維導圖式的操作方式和一個橫向排列的樹,每個節點分為三大類:sentence、branch 和 action,前兩個很好理解就是普通的對話語句和分支,action 節點則負責做一些特殊邏輯和演出。
對於靜態數據的管理,會先定義一個 schema,然後能根據 schema 動態生成表單,之後在表單上填數據就好了。這個動態生成表單的能力在整個軟件中都有體現,可以說這個軟件所有的表單都是動態生成的。
故事例子1
我們以一個簡單的例子看下這個軟件是怎麼處理故事的:
綠色的是 sentence 節點,藍色的是 branch 節點,紅色的是 action 節點。整個邏輯基本上就是從左邊走到右邊,遇到 branch 節點的話,那些連接到 branch 節點的相當於是一個選項了,所以你會看到這些節點左邊有個白色的選項名。選中選項就會跳到對應的節點繼續執行。
故事例子2
剛才的例子比較線性,但是如果遇到一些節點需要判斷狀態才能執行的怎麼辦呢?下面這個是例子:
這個例子中一開始就有個分叉,但是注意到的是這個分叉前可不是 branch 節點,所以是不會有選項的,那麼怎麼判斷要走哪個節點呢?其實每個節點都會有個 Enable check 的字段,這個相當於代碼判斷,由外部調用判斷返回值,如果匹配相當於這個節點可用,否則就是不可用,通過這個判斷機制就能靈活選擇走哪個節點。首先整個處理邏輯是從左向右,先嚐試執行右邊第一個節點,如果第一個節點不可用就嘗試第二個以此類推。
複雜但實際的故事例子
上面的例子都是在我遊戲中比較簡單的實例,而有沒有更復雜的例子呢,答案是有的,且看下面:
上面是一個故事片段的大體樣子,看上去挺複雜是吧,我講下這個故事片段的大體過程。隊伍發現了一個上鎖的寶箱,然後玩家有四種選項:砸開,撬開、用鑰匙打開和離開,用鑰匙打開這個節點可用的前提是需要物品欄中有鑰匙,而砸開或者撬開的話又需要選擇隊伍中的人,選擇後要進行能力判定,判斷成功就能打開寶箱,判斷失敗就不行,而且為了讓劇情更靈活一點,選擇不同的人時這個人在成功和失敗時還要說不同的話,而且如果選擇砸開或者撬開的話即使打開了寶箱也要進行陷阱的傷害判定。
上面一大串需求中比起之前還需要以下能力:能夠從一個節點任意跳到另一任意節點、節點內容複用,那麼這個軟件是怎麼處理的呢?
首先是節點複用,我先定義這兩個需要複用的節點,分別是獲得物品的和觸發陷阱的,這兩個節點都是 action 節點, 這些邏輯我都是通過在節點裡面編輯代碼實現了。
然後 action 節點中會定義個 copy 的類型,能夠把目標節點上的內容抄到當前節點,這就實現複用了。
跳轉也是相同的道理,定義個 jump 類型,然後選要跳轉的節點。
記得我們之前說的觸發陷阱要做傷害判定的邏輯嘛,其實判定傷害後也是需要展示傷害值的,如果按照之前的處理是給每個隊伍的人創作一個 sentence 節點寫受到了多少點傷害,不過由於節點的處理是動態的,這就意味著我能夠在執行時插入新的節點,所以這些 sentence 節點我就都由代碼創建了。
動態修改節點表單
上面的例子中我們看到每個節點都有不同的編輯表單,不過像我之前說的,其實所有表單都是動態生成的。在設置中會有每類節點的 schema 設置,理論上這些節點的數據甚至都可以改成和劇情管理完全沒關係,改下 schema 變成個事件管理軟件也完全沒問題。
靜態數據管理
其實有了動態創建表單的能力,管理一些靜態數據是很簡單的事情,而且這些數據還能和故事中的數據聯動。
示例視頻
下面就展示我實際場景中是怎麼編輯故事的:
小結
目前這個軟件我用了一段時間感覺還不錯,基本上符合我的需求,雖然理論上是可以做成公用,不過我可沒精力去維護和管理別人的各式各類需求,所以我是會開放源碼和一些簡易的使用文檔,不過具體的教程和要求我就懶得搞了,有興趣的人更傾向於自己 fork 一份自己改成想要的樣子。
源碼
windows 版
參考資料:
- 分支劇情創作中的挑戰和工具
- 消失的箭頭:一種創作(無)分支劇情的新思路