在制作游戏时,我们常遇见一种四个字就能概括的问题:分支剧情。它构想起来并不困难,无论是脑海中、纸面上还是电子屏幕上,我们都能顺其自然地画出剧情流程图。然而待具体实现时,这简单的几个字下却隐藏着诸多挑战——要以何种方式创作分支剧情,才能把它转变成游戏引擎,或者说机器能够理解的媒介?
如果把刚才这个问题中的“分支”两个字去掉,它仿佛就变得不那么棘手了。最显而易见的办法,无非是把所有的“在某件事发生之后触发哪个新的事件”、“谁在何时说了一句什么话”等等直接写到游戏本身的逻辑里去。虽然这个方法到头来会让剧情变得较难维护、对本地化不友好,但起码它直白、可行。
那么现在我们把“分支”两个字加回来。最直接的办法其实仍然存在——条件语句嘛。在现有的代码里,加上一个“取决于主角是否已完成某个任务,走向 A 剧情或者 B 剧情”不难吧?毕竟“完成过某个任务”是应该很容易拿到的一个、本身就会因为其它理由而存在的数据——等一下,如果是没那么唾手可得的信息呢?
如果剧情的分支需要取决于“主角是否与某位 NPC 有过交流”呢?这个信息听起来并不像是本来就会被记录的那种。办法还是有——记录下来嘛。每次主角与任何 NPC 交流,都在 Gameplay 逻辑中,额外地在游戏的数据库里标记一下。现在,可想而知,由于剧情走向难免会取决于各种信息,我们经常需要在 Gameplay 逻辑中记录和管理杂七杂八的、可能只服务于剧情的状态。
然而,当蜿蜒曲折的剧情发展到后期,即便我们做到了管理各种状态,条件语句也会变得越来越臃肿。这么做同时带来了更严重、也是刚才一直被逃避的问题:Gameplay 逻辑和叙事逻辑被混在一起,使得调试、排查和维护任何一边都变得愈发困难。某一段剧情没有按照预想的方式被触发,可能是因为此刻鼠标的点击没有被监听到,也可能是因为剧情上的触发条件没有写完备——到了这一步,开发只会越来越低效。
所以我们的问题变成了: 如何既在叙事中安插分支,又将 Gameplay 逻辑与叙事逻辑尽量解耦,保证两边的思路各自清晰。
况且,刚才的分析都建立在了一个隐形的条件上——对于作家和程序分开的团队来说——为游戏创作剧本的作家也得花时间和精力学习游戏引擎和代码,不然就得让程序员特地将剧情逻辑“复制”进游戏代码当中去。怎么选都不是什么优雅的方案,所以我们更加需要设法解耦,让写故事的人和写程序的人互不干扰,各自最大效率地产出内容。
其实有些读者读到这儿,在脑海中应该能预想出一个解决方案了。说白了,我们需要的是一个独立于游戏引擎,但又比日常的文本编辑器强大的剧情编辑工具。它起码得 对作家友好、能设计分支剧情、跟踪状态,以 Gameplay 逻辑为辅而不是为主,并生成能被游戏引擎识别的内容 。
幸运的是,这个问题早已被许多游戏开发者意识到了,也因此涌现出不少插件、工具和叙事引擎。我在这里介绍几款较为主流的,它们的使用场景有重叠,但也有不同。
需要额外指出的是,针对 Galgame 以及类型相似的文字冒险游戏,已有较为成熟的游戏引擎,本文探讨的诸如分支流程等问题已经被那些引擎自然而然地解决了。所以,接下来要介绍的这些工具,是应用于非文字冒险、但又涉及到分支剧情/对话的那些游戏的。《极乐迪斯科》(Disco Elysium)?《霓虹出租》(Neo Cab)?《林中之夜》(Night in the Woods)?是,就是那些风格。事实上,这几个游戏全都使用了这些工具中的某一个。
限于篇幅,我不能对这些工具深入展开,希望简短的介绍起码能讲清楚它们各自是什么。至于具体怎么用,我会在小标题链接到官网,那里通常配有技术文档帮助上手。
Twine 是一个开源的基于网页的分支剧情创作器。
从未写过代码的人可以迅速上手,而对于有编程经验的人来说,Twine 也提供了 语法 来实现赋值变量、条件控制(某些选项只有满足特定条件才会可选)等。 Twine 可以在线使用也可以下载客户端。撰写剧本的过程中可以随时从指定节点起“试玩”接下来的剧情,游玩的过程即用鼠标在网页上点击选项,剧情会按照设计好的路线来推进。这听起来很简陋,但 这可能是迅速创建一个可玩的互动电子剧本的最快方法 了,所以适合迅速搭建原型。
在 itch.io 上有 近 6000 个使用 Twine 制作的游戏 ,这庞大的数量也得益于 itch.io 对网页游戏的支持——Twine 可以直接生成网页游戏,但不能生成被其他游戏引擎读取的数据文件。有一个叫做 Cradle 的 插件 可将 Twine 生成的故事导入 Unity,但该插件已不再维护,随着Twine自身的更新迭代,我并不推荐去研究 Cradle 。如果对 Twine 的操作模式和功能感到满意,但又不满足于只在网页上游玩、想要和游戏引擎深度集成,那么接下来的 Yarn 是更好的选择。 Yarn 开源,基于 Twine,正如它们两个词的意思本身也很像。
Yarn 的设计者之一 Jon Manning 曾在一个讲座(Youtube视频“NZGDC17 - Yarn Spinner: A Tool For Writing Interactive Dialogue”)上提到过他们的理念: 让作家不需要学写代码,但允许作家通过 Yarn 来实现代码能够实现的事。
所以,和Twine类似地, Yarn 定义了一套非常简洁的 脚本语言 ,来实现条件判断、变量追踪等。不同的是 Yarn 可以配合 YarnSpinner 使用,将整个剧情逻辑导入 Unity(所以在 Unity 的 Gameplay 逻辑里可以一键“进行到下一段剧情”),甚至可以在 Unity 中定义函数,在使用 Yarn 编写剧情的过程中调用它们。 但 Yarn 自身不支持预览剧情,我的理解是,这意味着只有在导入 Unity 并完成相关的读取逻辑以后,才能在 Unity 中试玩到使用 Yarn 创作的剧情。这是 Yarn 目前比较大的一个短板。
如果说 Yarn 兼顾了视觉上流程图的直观性和逻辑上脚本语言的灵活性,那么 ink 可以说是在灵活性上走向了极致——这个工具不提供流程图,因为剧本片段之间的关系太过千变万化。
A flowchart says this blob exists within a structure, where this comes before and this comes after. In Heaven’s Vault, when Six and Aliya walk around, there’s a bucket of about 3,000 to 4,000 things they can say.And Ink just runs through all of them and says, ‘give me all the conversations that would make sense at this moment’. And then it says, ‘which one is the most relevant to what you were talking about last’, and it puts that in front of the player. And that’s all it does, there is no structure, no flowchart.
通过 Jon 的这段描述,我们不难理解 inkle 从不依赖流程图。如果某个角色接下来要说的话有三千种可能性,而这三千个备选项也能存在于不同流程里更早或更晚的对话中,那么我们需要的显然不是流程图,而是一个筛选器,挑选出最符合当下语境的那一句,然后推进剧情。而被选出来的那一句话,在下一次相似语境中就不会再被选中。
不仅对话,在 inkle 的设计理念中,剧情片段也是一样的道理。 接下来要发生哪段剧情,与上一段发生的是哪段剧情并非强相关,更多地,系统会考量自游戏开始后的各种状态、玩家的历史选择——ink 默认会记住玩家作过的每一个选择,包括很久以前的,而不需要我们手动使用变量去跟踪——从整个剧情池中挑出一个来推进剧情。
当然,ink 仍然支持之前 Twine 和 Yarn 支持的那种传统分支结构,只是在这里一切都需要靠脚本语言去编写,不再有流程图了。
摆脱了流程图的束缚,ink 可以实现相当细腻的“响应式(responsive)文字”。
在这个例子里,短短的一段文字,经过随机短语间的排列组合,取决于玩家是否穿着潮湿的衣服、当前地形是否为树林,便能有好几十种最终的呈现方式,底下的两段只是其中的两种。若这样精细的内容调整贯穿整个剧本,那流程图当然难以体现密密麻麻的走向可能性。
也正是因为没有流程图,一旦学习了 ink 的语法,写剧本的效率要高于其他工具——毕竟不必每次想添加一段剧情,都先用鼠标在流程图上新建一个节点了。 在这里,就像传统写作那样,手指不必离开键盘,一直打字就行。
ink 官方提供了 Unity 插件,民间则有虚幻、Godot、GameMaker Studio 2 的插件。可惜 ink 对本地化的支持不好,用一种语言写完的剧本,若想要翻译,那么剧本里的可变部分(也就是那些响应式的部分)越多,翻译过程就越艰难,如果跨语系就更加如此。
方才的 ink 在写作过程中可以说是面向过程的,而接下来的 articy:draft 则能满足面向对象的设计——等一下,写互动剧本也能面向对象吗?这不是计算机领域的词汇吗?
游戏开发的本质仍然是程序开发,当然可以面向对象。就拿主角的一句台词来说,一句台词包含什么?
倘若我们能想到的只有人物和台词这两个属性,那还是没有跳出传统写作的框架。试想,在各种需求下,显示这句话的文字颜色、人物表情的不同立绘、语音播放这句台词所需的时间长度、这句台词在选项菜单里显示的缩略文字(因为可能显示不下完整的台词)等等等等,都是游戏里可以被绑定在“台词”身上的属性。
而人物的属性就不言自明了,等级、职业、血量……从这个角度出发,我们的确可以面向对象地先定义每句台词、每个人物的属性,设置默认值,进而在创作剧本时系统性地照顾到它作为“游戏剧本”的方方面面。
而 articy:draft 天然地支持往对话和人物上添加自定义属性、设置类型和默认值。一旦属性设置完毕,就能在流程图的各个地方去引用和修改。用户也可以自己定义新的 Entity,例如“武器”,设置武器的各个属性,并在 articy:draft 而不是游戏引擎内 新建多把武器。接着就可以往人物里新增一个“武器”栏位,代表人物目前持有的武器,而这一切信息都将在游戏引擎内被读取——这进一步说明 articy:draft 适合面向对象的创作思路。
在 articy:draft 的流程图中,每个节点可以是一段剧情或是一句台词,通过条件、指令等串联。articy:draft 支持节点的无限嵌套,这对管理具有从属关系的剧情片段非常有帮助;合理安排层级关系,那么剧情流程图从外到内就能井井有条。articy:draft 自带一个数据库管理的面板,可定义全局变量,在流程图中读取和赋值;在预览故事流程时,还能实时查看每个变量的值。
配合刚才提到过的“在流程图内引用和修改人物属性”、“在 articy:draft 里管理人物配备的武器”,不难发现 articy:draft 其实已经可以在剧情逻辑里实现相当一部分 Gameplay 逻辑了。 如此一来,一个作家不需要掌握游戏引擎也能在撰写剧情的同时包揽一些 Gameplay 逻辑。当然,这是否可取就见仁见智了。
articy:draft 支持本地化、配音、版本管理和多人协作,官方还提供了 Unity 和虚幻的插件。其实 articy:draft 能做的不仅仅是分支剧情的管理,它还能管理游戏设计文档、剧本大纲、游戏地图等,但这些方面我了解得不多,也不是本文讨论的重点,故不再展开。
不同于之前的几个工具,articy:draft 是收费的,好在 Steam 上打完折也不是很贵。一些大厂如 CDProjekt RED、Techland 也是 articy:draft 的客户,这么想,或许能让这钱花得更心安理得些。 Dialogue System :Unity 素材商店的强大素材,自身可用于编写节点式分支对话(但要注意,这等于还是要在 Unity 里进行剧本创作),也支持 ink、articy:draft、Chat Mapper 等的导入,将来也会支持 Yarn。 自己实现工具:我相信对于资源丰富的大厂来说,都有成熟的内部工具来辅佐各种类型剧本的创作了。黑曜石就曾 分享过 他们的分支对话工具使用心得,同样是基于流程图的。而对于有闲功夫的独立游戏人,想自己造一个这样的工具出来玩,也值得鼓励——何况上边介绍到的许多开源产品都可以用作参考。 让我们回顾一遍开头的问题:如何既在叙事中安插分支,又将 Gameplay 逻辑与叙事逻辑尽量解耦,保证两边的思路各自清晰?
回想介绍过的那些工具,除了不支持和游戏引擎结合、但适合快速在网页上搭建原型的 Twine 以外,其余都能满足这个要求。当然,刚开始都需要花时间学习工具本身的使用,以及如何和游戏引擎结合。另外,可以看到这些工具或多或少都支持从叙事流程中调用游戏引擎里 Gameplay 相关的函数——如何划清叙事逻辑与 Gameplay 逻辑的界线也是我没能展开讨论的话题,但我认为这个问题没有标准答案,很大程度上取决于游戏本身的性质、甚至创作人员对两边工具的熟悉程度。 总之,工欲善其事必先利其器,一旦上手,之后的剧本创作和游戏开发将齐头并进、不再混沌。
每个工具的设计理念不尽相同,这些简短的介绍只是一个引子。究竟哪个适合自己,还得留给读者自行考察与斟酌。即便没有迫切需求,也希望对这些工具的探讨能触发大家对分支叙事实现方式和应用场景的思考。
Youtube视频“AdvX 2018 - Jon Ingold - Sparkling Dialogue: A Masterclass”(使用 ink 将银翼杀手中的经典对白改编成互动对话。不仅介绍 ink 的语法,更多的是讲互动对话设计时的要点,故强烈推荐)
Youtube视频“Ink: Behind the Narrative Scripting Language of '80 Days' and 'Sorcery!'”
评论区
共 14 条评论热门最新