在基于网格的流氓世界中占据多个空间的生物/实体仍然不是那么常见,尽管在开发社区中这是一个越来越受欢迎的话题。其中许多讨论都围绕着如何解决与这类实体相关的各种技术和设计问题,虽然多年来我已经在许多这样的对话中提供了意见,但现在确实是时候将其中一些经验、观察和建议收集到一篇文章中,以便更好地参考:)。
2013年,当我第一次将CogMind作为一个商业项目进行开发时,我粗略地介绍了“多瓦片机器人”,但我真正做的只是给出了一个基本的描述,而不是研究支撑这一功能的细节。
自从2011年我第一次将多瓦片生物添加到X@com后,我就一直在处理它们。就像我在那里写的那样,这是相当令人头疼的,因为我从一开始就没有把它们包括在内,但考虑到主要的来源材料(X-Com),这既是必要的,也肯定会给这些生物增加很多性格,所以非常值得。
一个著名的例子是,一条巨大的“D”龙只占据了一个空间,就像一个小冒险家一样的战术单位,以它自己的方式很棒(优雅!),但对于一个实际更大的生物的心理影响和机械影响,还是有一些可以说的。
我猜还有现实主义的论据--虽然在游戏中完全的现实主义通常不是一个好的目标,但当你既能拥抱它又能得到一些回报时,总是很好的。
“神圣的…。看那东西!我们现在可能不能带上它,但如果我们沿着这条狭窄的走廊逃走,它可能就不能跟着我们了…。我们会找到另一条路绕过去的。“。
从技术上讲,“多瓦片”生物的概念可以细分为许多不同的类别,我不会在这里详细介绍所有这些类别。
例如,一些盗贼拥有固定的多瓦片生物,比如Ragnarok的常春藤爬行者。
虽然是静止的,但有时这类生物也可以向外生长,就像HyperRogue中的各种常春藤一样。
总体而言,这些对于开发人员来说都相当容易使用,这只是游戏中您是否想要/需要这样的东西的一个例子。
也有蛇形的多瓦片生物可以移动,但身体基本上遵循头部所走的任何路径,所以也不是很难管理。
这一类型还包括无定形的区域生物,如DCSS Krakens,它由单一的身体瓷砖组成,并发出触角,本质上是单个生物的行为(尽管它们与身体的距离受到限制,而且伤害它们也会伤害Kraken)。
出于本文的目的,为了控制范围(更重要的是为了涵盖通常会出现问题的特别具有挑战性的领域),我实际上只对最普通的多瓦片生物感兴趣,在这种类型中,整个生物是一个“连接的实心斑点”,它出现在地图上,像其他单瓦片生物一样作为一个单元移动。
这一类别最极端的例子可能是Dumuzid,这是一款明确围绕大小概念设计的7DRL,所以你最终会拥有一个庞大的四肢伸展的身体,作为一个整体移动也就不足为奇了。
一些更典型的例子,都巧合地反映了这样一个主题,即多层生物经常被用来对付特别具有挑战性或令人敬畏的敌人,比如老板:
对于X@COM,我全力以赴地使用多瓦片单元,从X-Com最初的需求扩展而来。开着坦克穿过墙壁,看着大楼倒塌,这是一件很有趣的事情:)。看着巨大的敌人在建筑物中横冲直撞,推倒墙壁,压碎家具,这也是一件有趣的事情,但更可怕的是!X-Com本身只有2×2的单元,但是当把这样的机械装置放在适当的位置时,可能会让大小变得任意!当时在录制声音测试演示视频时,末尾有一个5x5的巨像,从3点50分开始,你可以看到下面的画面:
从X@com继承了它的架构,甚至继承了一些机制,CogMind还包含了许多多瓦片实体,以满足它们的震撼和敬畏因素。虽然从更开放的地面环境和更典型的地下环境相比,需要克服的障碍稍微多一些,但对我来说,这是值得的。
据报道,伊凡也有2×2的怪物,它们通常可以摧毁墙壁以便四处走动(或者只是静止不动),但我找不到任何好的资源或图像。
我将在下面分享更多适用于我们将要讨论的主题的CogMind和其他无赖的示例。
请注意,正如这里讨论的那样,多瓦片生物指的是那些实际更大的生物,涉及到机械和它们占据的网格空间的数量,而不仅仅是表面上的大小,这是许多游戏为了绕过实现问题而做的简单方式,但结果也没有充分发挥它们的潜力。
在地图上描绘多瓦片生物有许多不同的方法,如果仅限于ASCII和/或具有统一单元大小的传统网格,其中一些可能需要更有创意。
对于CogMind的ASCII模式,我选择在实体当前占用的区域中重复单个字符。通常反对这一概念的人声称,无法区分它是一组实体还是单个大型实体,但在实践中,这并不是什么大问题,因为根据游戏内容,其他情况下,个人既以统一的形状相邻,又以完美的队形移动,这是极其罕见的。更不用说当检查这样的实体时,所有的空间一起突出显示,并且标记(或者例如在列出当前可见的内容的其他流氓中)只对整个事物执行一次,而不是针对每个组成单元格执行一次。因此,这并不是真正需要担心的问题,除非有其他特定于给定游戏的内容或机制的东西可能会导致混淆。
入侵为基于ASCII的流氓提供了另一种可能的方法,通过单独的代表角色将生物的角色链接到其周围占用的单元,在本例中使用‘+’:
在ASCII游戏中经常使用的基于网格的非常严格的系统下,从技术上讲,仍然可以使用延伸到多个单元的精灵来绘制多瓦片生物。尤其是CogMind的引擎甚至不支持绘制任何与网格大小不匹配的内容,因此绘制多瓦片对象的唯一方法是分段绘制,一次绘制一个单元格。例如,2x2庞然大物由四个单独的单元格组成,在精灵工作表中按如下方式显示:
许多使用现代引擎的流氓并不一定关心这类事情,而是能够在任何位置简单地画出任何大小的精灵。Zorbus提供了一个很好的例子,在没有任何建筑限制的情况下,可以使用多种不同的方法来绘制大型生物。
许多盗贼(包括上面的Zorbus)可能会简单地使用大字符显示多瓦片生物,这突出了使用多瓦片生物时需要考虑的另一个问题:局部可见性。盗贼的视野几乎总是以细胞为基础来处理的,那么如果一个多瓦片生物只有一块或多块可见,玩家会看到什么呢?
根据ASCII和CogMind的默认方法,即在每个占用的单元中重复相同的字符,只看到生物的一部分没有太大区别,因为每个单元都包含相同的表示。也许这里最主要的担忧是,玩家可能不能立即分辨出,例如,一个“B”是一个大型庞然大物的角落部分,还是一个单瓦的布劳勒级机器人。这就是早些时候演示这些自动标签的帮助之处--问题一发现就会立即得到回答!
为了演示另一个场景,我们可以使用Ape最近发布的图形CogMind mod,它将默认的多字符样式转换为使用大字符,如下所示:
再说一次,自动标记在这里无论如何都会有帮助,但是一个大的“B”字符的左上角实际上看起来像一个偏离中心的“c”!当然,每一个角落,或者每一边,都会有自己独特的面貌。就快速视觉解析而言,部分可见性在这种方法中不能很好地工作(通常这是首先使用ASCII的优点之一)。
基于这个信息,对于多瓦片生物,我们可能想要打破只显示玩家当前可以看到的细胞的模式,而是允许玩家知道和看到他们至少可以看到部分的任何多瓦片生物的全部范围。这既适用于ASCII,也适用于平铺集。要么正常显示整个生物(如果它的任何部分可见),要么将其全部显示,但考虑淡化当前不在FOV中的那些单元,因为知道某些部分何时从技术上看不到仍然很重要。
尤其是出于深思熟虑,我决定不在FOV之外显示部件(这是一个选项,即使每个部件都使用不同的字符!)。因为在我的情况下,这会使很多UI功能复杂化。不过,我想这对很多其他流氓来说应该没什么问题。
当每个实体一次只能占用一个单元格时,处理实体的相对位置数据相当简单,但是对于占用空间较大的实体该怎么办呢?有些人在这个问题上犹豫不决。对于给定的流氓类型,答案可能取决于其他需求和架构考虑,但在这里我将与您分享适合我的解决方案。
这植根于常规地图数据的格式,因此下面的图表展示了CogMind地图对象的结构:
此图包含一些多余的信息,因为我是从更广泛的讨论中提取的,但这里要注意的概念是:1)整个地图/楼层由Cell对象的二维矩阵表示,2)在每个单元格内是一个Entity对象。“Entity”指的是actor/creature/character/mob/whateveryoucallit,中存储其占有者句柄(有点像指针)的单元,或者如果该位置当前没有实体,则直接为NULL。
虽然我也有一个现有所有实体的列表(从技术上讲,每个派别都作为单独的列表进行管理),而且这种格式对于需要迭代所有实体的某些类型的操作显然很有用,但我发现拥有一个实际的2D空间非常有用,可以在其中执行空间搜索和应用效果等。出于性能原因,这在CogMind中尤其重要,因为一次有数百个实体。
那么,这对多瓦片实体意味着什么呢?Well…。只需将其独特的句柄放在它们占据的每个单元格中:)。
这并不像实体对象本身实际上被复制到任何地方--它只是一个很小的句柄,所以不会严重浪费空间,但绝对是对便利性的重大好处。检查谁占用了这些单元格中的任何一个(或多个)的进程将指向同一实体。每当实体改变位置时,它只是将其控制柄从它占用的所有原始空间移动到新的空间。
每个实体本身也存储其当前占用的所有地图坐标的列表。虽然对于大多数/单元格实体来说,这个列表确实只包含一个坐标,但是这样实现系统使它很容易扩展。同时,尽管对于大多数流氓来说,如果您知道它们的参考位置和大小,通常可以推断出这些额外的坐标,但这样做的速度很慢!
在我的示例中,我选择实体的左上角作为它们的“参考位置”,这是它的列表中第一个出现的位置,在这里,任何其他坐标都是按行优先顺序添加的(请注意,这里的索引将如何方便地反映前面显示的精灵工作表中的偏移量;)。设立一个参考职位的想法稍后会派上用场。
(尽管那里的大多数开发人员没有提到多瓦片实体,但是关于地图对象体系结构,您可以在r/RoguelikeDev上前面的相关常见问题中找到其他灵感。)
在我们进一步讨论之前,重要的是讨论一下形状,因为它与多层生物有关。
到目前为止,盗贼类生物中最常见的多瓦片生物(如果有的话)是所谓的“2×2”,总共占据四个细胞。作为一个正方形,这是相当容易处理的,因为它没有任何凸或凹的元素来使移动或交互复杂化。
非正方形形状(甚至只有矩形!)。是有问题的,因为你可能不得不处理轮换的逻辑和后果,以及其他问题。由于多瓦片生物已经引入了足够的复杂性,大多数盗贼不会冒险进入其他形状。(像Dumuzid这样的东西在这里当然是个例外,因为它是一个中心主题恰好是大小操纵的小游戏。有趣的是,它也不允许轮换。)。不管怎么说,旋转意味着一个明确的面向,而且大多数盗贼没有面向机制,所以通常会避免暗示多个方向的多瓦片生物。
每当roguelikedev社区中出现多瓦片生物的话题时,很有可能是有人询问寻路。在每个参与者正好占据一个空间的游戏中,在网格中移动他们似乎比管理那些需要额外空间来四处移动的人简单得多。
为了寻找多层生物的路径,我们将忽略这些不太常见的场景,同样,也忽略本文范围之外的生物:
非正方形形状(以及与这些形状相关的任何旋转--CDDA有各种形状的旋转多瓦片交通工具,例如,您可以在这里查看)。
尽管如此,如果你使用上面的任何一个,它主要是选择一个主要的参考部分(有问题的生物的)作为寻路的基础,并在它移动时推断其他相连部分的位置,这实际上类似于我们稍后将在这里为普通的正方形多瓦片生物所做的事情。
有一种方法我以前没有用过,但见过其他人用过,那就是明确地标出大型生物可用的空间,然后用这张地图来寻路。以最常见的2×2生物为例,你可以复制你的地图,阻挡任何在其南面、东面或东南面有围墙或其他不可逾越障碍的空地。
因此,根据上面的屏幕截图,寻路算法将依赖于两个独立的地图,一个用于标准大小的演员,另一个用于2×2的生物:
但是,此方法更适用于静态地图,因为由于地形破坏或其他因素导致的地图更改将需要重新计算寻路地图(或至少部分地图)。我喜欢在我的无赖中肆虐的地形破坏,所以这种方法在我的情况下不会真正起到很好的作用。正如你将在下面看到的,另一种动态方法也可以考虑更多的因素,无论如何,生物大小只是其中之一。
动态方法开始更深入地研究如何构建您的寻路系统架构。阿米特在Red Blob有很好的A*寻路资源,如果你想了解更多背景知识的话。我不会在这里复习基本的A*,而是研究如何最好地应用它,这样它就可以特别地考虑任意大小的生物。
除了最简单的无赖世界外,你通常希望让生物实际检查一条路径上的每个潜在细胞,并根据各种相关因素对其进行加权,例如地形移动成本(可能因生物类型和地形而异?),或者生物自身的能力(会游泳吗?飞?。开门吗?某种其他形式的环境相互作用?)。划分此功能的一个很好的方法是:回调。
简而言之,当生物想要找到从A点到B点的路径并调用游戏的寻路函数时,它还提供了一个回调函数,该函数本质上包含如何沿预期路径为单元赋值的指令。*寻径的操作方式是将“成本”值分配给单元格,直到它找到能够以最低总成本到达预期目标的移动序列。因此,回调的工作就是返回给定单元格的成本,以便于这些计算。
如上所述,发送到算法的CogMind的主要移动寻径回调实际上是一个包含多个方法的对象,最初由与寻路器本身相关联的基类(Cartorapher2D)虚拟定义。我发现将回调可能需要处理的一些检查拆分为多个方法会更有效,例如,isValid()用于告知目标是否为有效的移动位置,这在费心开始之前可能很重要:p。(Cartorapher2d也支持通过启用细胞的隐形传送进行寻路,但CogMind本身并不具有这种传送功能,因此回调对象没有定义必要的方法。)。
在哪里可以容纳多瓦片生物呢?首先,记住生物的参考部分是用来计算它们的路径的--路径的起点与它们的左上角匹配,而路径的终点将是它们的左上角的最终位置。
IsValid()函数*只需要确保不仅检查路径上每个单元格的引用位置是否有效,而且同一生物的所有其他相关部分也是有效的(*以及扩展为getMoveCost(),这是在计算和返回单元格的成本之前检查isValid()的内容)。
这种动态方法不需要预先计算或维护任何其他路径地图,可以考虑任何数量的其他因素(不管它们是否一直在变化!),显然这正是我们允许多瓦片生物四处移动的需要,同时还可以考虑它们需要考虑的任何其他因素。
总而言之,通过使用回调查找路径,您可以获得很大的灵活性。我们可以向isValidCell()添加任何我们想要的绝对检查,它们将应用于生物可能考虑占用的任何单元,或者将检查添加到getMoveCost(),检查允许的移动,但可能与移动到/穿过普通单元的成本不同。CogMind不会特别频繁地使用这些功能,尽管如您所见,有一些条件决定了实体是否可以使用不同类型的隐藏门,并且通过已知陷阱或容易塌陷的区域的成本较高(使其不太可能发生)。
旁白:流行的类似无赖的库libtcod(我在我的“如何进行类似无赖的演讲/文章”中推荐的)包括对寻径回调的支持!事实上,在我早期的roguelikedev时代使用libtcod实际上是我最初了解它们的地方:)。
在解决了主要的技术问题之后,当涉及到多层生物时,也有设计方面的问题。当然,你不能就这样把它们放进一个为典型的1×1细胞大小的生物量身定做的世界里,然后指望一切都会好起来:P。
自然,更开放的地图更适合这些大型生物,因为地形是它们移动的最大障碍。虽然流氓地图往往有很多狭小的空间,但如果至少有一些开放的区域让大型生物舒适地占据,即使是地下地图也可以变得更适合大型生物使用。
例如,CogMind最常见的2×2单位,庞然大物。
.