我一直想做款 RPG,而对于开发 RPG 来说,科学地管理游戏剧情是不可或缺的,所以我对这方面做了一些探索。
初期探索阶段
计算机领域有句话,叫“不要重复造轮子”,意思就是与其自己从零开始探索制作,不如借助已有的东西。作为一个懒惰的人,我也深谙此道,所以一开始就探索了各式各类的剧情管理软件,最后发现以下几个比较能够用于实战:
yarn:脚本语言和卡片的结合管理,算是在灵活和方便管理间取个折中。
ink:完全由脚本语言管理剧情,优点是极致的灵活,用写代码的思路来管理剧情。缺点也很明显,基本不支持多语言。
articy draft:基于卡片式剧情管理,更像是一个游戏的数据库,能够定义各类数据嵌入,但灵活程度有限(至少不方便在剧情中嵌入代码,或是有方法但我不知道)。
Excel:没错!大道至简、大巧不工,也有不少人用 Excel 管理整个游戏剧情,毕竟 Excel 都能用来玩三国杀,管理游戏剧情自然不在话下,是吧?
一圈试用下来,我感觉每个软件都有一些不爽的点。例如,大部分卡片式剧情管理软件都让你拖线连接创建卡片,而我觉得手动拖线基本属于伪需求,因为大部分情况下,剧情都是线性执行,只有在一些关键节点才会做跳转,为了小部分场景,把大量时间浪费在拖线连接上有些得不偿失。其次,我打算在剧情管理软件中嵌入一些游戏逻辑,所以需要软件能够提供特殊的节点触发事情,或能够直接嵌入代码。
虽然上述软件都有实战案例,同时我也很清楚——它们不是那么完美,但做一些骚操作应该能够满足我的需求。可是,开发一款 RPG 的过程中,大部分时间都要放在制作剧情上,一款好的剧情管理软件能够极大地改善心情,所以最后,我还是打算自己撸一个简易的剧情管理软件。
第一次尝试
经过上述尝试,我大致得出以下需求:
绝对不要手动拖线,不要玩连连看
能够看情况触发事件
当时看到一位开发者实现的剧情管理系统,感觉挺符合我的想法,于是就按照视频上的软件形式做了一个。我自己是用 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 一份,改成想要的样子。
源码:https://github.com/mnikn/shovel-db
windows 版:https://github.com/mnikn/shovel-db/releases/tag/0.1.0
参考资料:
分支剧情创作中的挑战和工具
消失的箭头:一种创作(无)分支剧情的新思路
* 本文为用户投稿,不代表 indienova 观点。
领取专属 10元无门槛券
私享最新 技术干货