我做了很多关于Roguelike的尝试,我找了很多关于肉鸽的GDC,论坛,教程,也游玩了很多肉鸽游戏,文章从挺进地牢的设计上来讲一下肉鸽地图的地牢生成到底有哪些设计理念,希望它能给众多想做生成式肉鸽地图的开发者一个思路作为参考。
前言
我正在开发自己的独立游戏,Roguelike类型的游戏可以很好的保持玩家的活跃度,并且对于能力侧重于代码的独立开发者来说,肉鸽可以说是用代码和美术资源做了完美的权衡,减少了很多对美术内容量的要求 (你不会想知道线性叙事内容对于独立游戏开发者来说意味着什么的…)。我做了很多关于Roguelike的尝试,我找了很多关于肉鸽的GDC,论坛,教程,也游玩了很多肉鸽游戏(挺进地牢,节奏地牢,以撒,死亡细胞,Hades(我是Supergiant的忠实粉丝),甚至还有手游-元气骑士,战魂铭人等)…那么我这次就从挺进地牢的设计上来讲一下肉鸽地图的地牢生成到底有哪些设计理念,希望它能给众多想做生成式肉鸽地图的开发者一个思路作为参考。
程序化地牢类型
总结一下主要我玩的这些游戏中地牢的特点吧。其实我想到的程序化地牢的制作方式主要有两大类,一类是 Room-Based ,另一类是 Tile-Based
Tile-Based
Tile 表示的是地牢每个房间中每一块最小元素(比如:每一块地板,每一块墙壁,每一个Prop等)。然后根据Tile-Based有两种生成地牢的策略:
- 这些最小元素通过一定的流程组合成一个肉鸽的房间(Room),肉鸽的房间再使用一定的组合方式进行整个大地图的组合,主要是房间(Room)和走廊(Corridor)的组合。
- 另一种组合方式是Tile直接生成大地图,这里的Tile根据代码或者是某一制作流程会直接生成整个地图,房间的概念还是存在, 但是其实会被弱化。当进行修改时,最小的编辑单元是Tile或者是控制Tile生成的流程逻辑。
生成的逻辑虽然弱化了房间(Room)的概念,但是在编写程序时,还是需要首先定义房间之间的连接和划分策略(如房间的大小范围在多少Tile的范围,走廊的长度在多少范围,噪声区间控制在多少,房间之间的连接,Tile的坍缩参数等等)
这个方案对于代码流程有很高的要求,虽然入门要求很低,但是上限高的吓人。
如果你希望生成简单的地牢,地牢的房间中没有很多复杂的元素,房间的生成随机性很强,那么这是一种比较合适的方案。不过上述的大部分游戏都是使用的下面提到的Room-Based方案,用来确保每个房间是精心设计的,更有趣的。
代表性的游戏有《暗黑破坏神2》《节奏地牢》等
Room-Based
- Room 是生成的最小单元,基本流程是我们预制一些房间的模板,然后把这些房间连接起来。
- 开发者会手动或者是半自动地设计每一个房间,然后在房间内使用一些可随机替换的物品,然后根据规则进行组合。
- 代表性的游戏有《挺进地牢》《死亡细胞》《以撒》等
这篇文章主要会介绍类似于《挺进地牢》的程序化地牢生成规则和策略,不会涵盖太多其他的内容以免显得臃肿啰嗦。
挺进地牢
挺进地牢是肉鸽历史上一个里程碑式的作品,它把俯视角肉鸽射击游戏带到了前所未有的高度,其中最让人印象深刻的就是那数百种不同枪械带来的独特体验,区分与单纯数值上的差异,挺进地牢的每一把枪械都能给玩家带来独特的体验。但是除了武器系统的设计之外,地牢的生成也很值得参考,它的地牢设计上毫不逊色于其丰富多彩的枪械。
有很多程序生成器可以组合出布局合理的关卡,一个简化的例子是《元气骑士》,他的地牢设计相对于挺进地牢而言要简单得多,地牢Room的样式都是正方形的,每个房间内有不同组合的道具和障碍物,然后生成几个Room,把他们连接起来,貌似就可以了。不过你是否注意到了,就是每次玩家遇到的道具房总是离角色出生点较近,商店适中,最远的是Boss房间,这里会使得游戏的节奏更出色。而《元气骑士》的生成策略可以看作是《挺进地牢》的缩影,后面我会讲到大概的设计思路。
挺进地牢的地图设计
总体来看,挺进地牢的关卡 “并不复杂”,下图是一张比较典型的地图。
玩家从入口进入,途中会经过箱子房间,还有商店,最后是Boss。挺进地牢总是能把握好Room的生成次序,总是能做到一个合理的布局和一个恰当的节奏和奖励机制。在众多循环和可选项中,它能做到布局紧凑,节奏恰当。对于独立开发者而言,真的很难处理好它们的平衡。
挺进地牢用到了传送门这样的机制,用来权衡和弥补地牢生成的一些瑕疵,这使得游戏节奏更紧凑,更照顾到了玩家的体验,当你走进一个比较长的岔路分支并走到了死胡同,传送器能快速访问已经攻克的房间。
目前为止,你可能觉得这有啥,我随便写个生成器,把预制好的房间生成出来,然后连接起来不就行了,反正有传送器呢…
其实,你重复游玩足够多的时候会意识到,房间的组合和排序并非只是靠随机,Boss总是玩家的出身点比较远,有敌人的房间总是能合理的分布在商店、宝箱和其他特殊的房间之中。除此之外,还有一个很好的机制——就是很多箱子都隐藏在了被锁住门的后面,他们是单向的,想要直接开宝箱,你需要绕路,但是你一旦打开,这条路又会变成捷径,这真的是很有趣的想法。恐怖的是,挺进地牢有很多天才的想法并没有实装,否则它会变得更好玩。
//TODO : 如果有需要的话,我可以单独把每一个解决方案都写一下大致的集成教程,那可能涉及到很多零散的内容。
挺进地牢的地图布局
当你玩了足够多的游戏,你会发现一些规律存在。在这里我像所有速通的大神们致敬,他们不断重复的游玩,穷举了所有的地牢连接规律。下面是其中一些已经探明的布局图。
Normal表示普通带随机敌人的房间,Hub有很多连接点的大型房间,还有Reward,Shop,Boss等,在这里不一一讲解了。要注意的是,这个图中,并没有标识一些特殊的房间(预先指定,或者从特殊房间列表中选择的秘密房间)
挺进地牢中的每一个场景都是由这些图生成的,只不过是这些图可能有多个,每次运行一次游戏时都会在几个图中选中一个出来,比如在膛室1有4个可选的布局图,每次游戏开始生成时,先从这4种不同的布局图中选一个。不过,这些布局图并不是通用的,在膛室2,3,4等会有其他相似的但是不同的布局图,因为不同场景的特点不同,开发者设计的布局图也根据场景的不同而不同,这是为了保持玩家的新鲜感,只是场景变了事实上并不会给玩家带来新鲜感,很容易使玩家产生疲劳,如果你玩了很多的地牢,你会意识到这一点的。
不过你玩的时候,会发现真正的地牢的布局并不是和上述我提到的几种布局图相同,有一些长条状或者是L型的走廊没有在图上列出,还有一些隐藏房间等,有很巧妙的办法给地牢添加了更多的随机性,下面我再分析一下它是怎么把抽象的布局图做成游戏场景的。
开始生成时,算法随机选择一个用于生成地牢的布局图。这个图只保存了房间之间的位置关系,并不会影响到它们的具体位置。每个房间都有一些标签(它们包含房间类型,连接的走廊类型等),房间之间的连接都是基于一个有向图,从根节点到子节点,子节点又会生成一个子树,然后循环。然后房间的标签会决定他们的连接策略。
布局图中的房间,有一部分会被替换成一定长度范围的随机房间,这里其实就是把图中的Room节点替换成真实的房间Prefab。然后还有一些房间会根据条件决定是不是可以额外多出一些连接点,这些连接点被用于房间注入(Injection),相当于在原有的布局图中额外嵌入一些特殊房间,这里我会叫房间注入。
房间注入比较灵活,开发者可以控制注入的房间类型,注入的位置,生成的概率和需要满足的先决条件等(比如玩家必须完成某些成就,或者身上有某些装备等),一些常见的注入类型包括秘密房间,监狱房间,电梯等特殊房间。(举个例子来说:秘密房间通常被注入到死胡同,有20%的几率连接到任何类型的房间,没有先决条件,有90%的出现几率)下图是一些房间注入的例子:
挺进地牢就是使用这样的机制给玩家带来随机性的趣味,生成器可以在每一个房间的位置计算注入的概率,不过这还是却决于玩家所处地牢的场景和地牢的深度。挺进地牢的第一阶段有300个左右的房间列表,第2,3,4阶段房间更多,这就是为什么它百玩不厌的原因之一。而上千个房间的空间占用确低得惊人,因为每一阶段对应的2D地牢资源保持在个位数以内,也就是说,挺进地牢可能只是使用了几十M的大小为你提供了数千万计的不同体验,这就是程序化生成的力量。
然后是Room之间的连接,**走廊(Corridor)**会在适当的位置连接Room的连接点,因为它们很长,所以他们决定了地牢的整体形状是方形的还是一条窄长的走廊,因此选择合适的连接方向会比较重要。
地牢的房间和走廊会基于连接图连接成一大个Chunk(如下图左侧有3个Chunk),每个Chunk要么是一个互相连接的房间环,要么是一串房间树。然后将这些Chunk或者说是子树连接成完整的树。在注入和连接后组合成一个完整的树形图。这里其实还有一部分额外的操作,我在开发中遇到的是组合和连接之后,由于走廊和房间可以互相连接,有时候会导致走廊出现在死胡同,这是不可接受的,这里的额外操作是剪枝(Prune),在完成树型结构之后,遍历这个树,如果房间的标签是走廊(Corridor)就移除它。经过组合、剪枝、注入之后的地牢则会变成下图右侧所示。
此外,为了建立更加复杂的地牢结构,我还使用了一个别的处理方法,那就是当房间之间有覆盖或者是距离满足连接条件时,以一定几率连接这两个房间。不过这会导致一个问题,那就是有时候我们不希望一些特定的房间被连接,这时候我就会通过房间的标签来判定是否满足连接条件。这个条件是凌驾于上面相对布局的条件的。所以原本布局图中没有连接的房间也会有概率连接起来,这使得地牢的随机性又变大了一些。
最后是将这些东西变成场景中的Room:随机放置一个Normal或者是Spawn Room,然后选择一对连接点将房间一个接一个地添加到布局中,这些连接点是提前放置好的标识(可以手动选择出入口,或者是随机位置),然后连接起来。这里的顺序是采用的多叉树的深度优先遍历,通过标签选择合适的Prefab,如果遇到了无法满足条件的情况,那就回溯。(一般数百次的尝试机会可以解决大部分情况,前提是你需要有合适的,数量足够的房间和走廊)。当然这个过程实际上是比这个稍微复杂一些的。那么…
Boom!我们的原始地牢就这么完成了!
剩下的事情要稍微简单一些,就是在房间内生成随机的物品以及装饰品等。如果有时间的话,我可能会再写一篇文章。大概的流程就是随机生成,局部随机和全局随机等。
Outro
其实这篇文章写的比较仓促,并没有准备很多素材,其中的内容也不是很详尽,但是我希望能给其他像我一样的独立开发者一点启示。
因为我是程序化生成的粉丝,程序化不仅在生成地牢中,除了场景,在动画也是一个很有趣的主题。再次说明一下,程序化地牢用了几十M的空间,可以为玩家生成具有千万种可能的地牢房间,这是程序化的功劳。我喜欢数据驱动的世界,开发者制定规则,在其中各式各样新奇的内容会涌现出来。就像《王国之泪》一样,一些物理规则带给玩家的远远超越物理本身。这就是程序化的魅力。
设计一个合理紧凑的地牢是很困难的。你不会知道他们究竟经历了多少次迭代和修改才得到了看上去还不错的结果,在没有前车之鉴的基础上,我很难想象。这也就是为什么独立游戏开发是很困难的事情,他们在现实和希望之间的权衡不像是游戏中预算和质量的权衡那么轻巧,那是生存的代价。
写这样一篇文章,也是借鉴学习了网上的各种资源,在开发独立游戏的这段时间,我感觉我学到的东西实在太多了,我看了非常多的GDC的天才idea,我学到的越多我越感觉自己很浅薄,我有无数新奇的想法想要分享,真希望自己有足够的时间认真地思考和学习。我把写文章当作独立开发的小憩,当我进度缓慢的时候,我更应该投入更多的时间和思考。
Ref:
https://enterthegungeon.com/
http://www.boristhebrave.com/2019/07/28/dungeon-generation-in-enter-the-gungeon/
https://zh.wikipedia.org/zh-cn/%E6%8C%BA%E9%80%B2%E5%9C%B0%E7%89%A2
https://enterthegungeon.fandom.com/wiki/Enter_the_Gungeon_Wiki