程序生成,游戏更新和蝴蝶效果


10

注意:我几天前在Stack Overflow上问了这个问题,但是视图很少,没有任何响应。想通了,我应该问gamdev.stackexchange。

这是关于在不破坏先前生成的内容的情况下通过多个发布后更新来维护程序生成系统的一般问题/建议。

我正在尝试寻找信息和技术来避免在创建游戏程序内容时出现“蝴蝶效应”问题。当使用种子随机数生成器时,可以使用重复的随机数序列来创建可重现的世界。尽管有些游戏只是在生成后将生成的世界保存到磁盘上,但是过程生成的强大功能之一是您可以依靠数字序列的可再现性以相同的方式多次创建区域,从而无需使用坚持不懈。由于我的特定情况的限制,我必须最小化持久性,并且需要尽可能地依赖纯种子浓缩物。

这种方法的主要危险在于,即使在程序生成系统中进行最微小的更改,也可能导致蝴蝶效应,从而改变整个世界。这使得在不破坏玩家正在探索的世界的情况下更新游戏变得非常棘手。

我用来避免此问题的主要技术是在多个阶段设计过程生成,每个阶段都有自己的种子随机数生成器。这意味着每个子系统都是独立的,如果发生故障,将不会影响世界上的所有事物。但是,即使在游戏的孤立部分中,它似乎仍有很大的“破损”潜力。

解决此问题的另一种可能方法是在代码中维护生成器的完整版本,并针对给定的世界实例继续使用正确的生成器。虽然这对我来说似乎是一场维护噩梦,但我很好奇是否有人真的这样做。

因此,我的问题确实是要求提供一般建议,技术和设计模式来解决这种蝴蝶效应问题,尤其是在发行后的游戏更新中。(希望这不是一个广泛的问题。)

我目前正在Unity3D / C#中工作,尽管这是一个与语言无关的问题。

更新:

感谢您的答复。

看起来越来越像静态数据是最好和最安全的方法,而且当无法存储大量静态数据时,在生成的世界中进行长时间的活动将要求对使用的生成器进行严格的版本控制。在我的情况下,限制的原因是需要基于移动的云保存/同步。我的解决方案可能是找到存储有关基本事物的少量紧凑数据的方法。

我发现暴风城的“笼子”概念是思考事物的一种特别有用的方式。笼子基本上是一个种子点,可防止微小变化(例如,将蝴蝶笼中)的影响。


我投票结束这个问题是离题的,因为它太广泛了。
Almo

我知道它的范围很广。您会建议我尝试一个gamedev论坛或其他吗?确实没有任何方法可以使问题更具体。我希望我可能会从在该领域有丰富经验的人那里听到一些我没有想到的狡猾技巧。
null

2
阿尔莫弄错了。它一点也不广泛。这是一个很好的问题,范围很窄,无法给出很好的答案。我认为这是我们许多程序人员经常思考的问题。
工程师

Answers:


8

我认为您已经涵盖了这里的基础:

  • 使用多个生成器或每隔一段时间重新播种一次(例如使用空间哈希)以限制更改的溢出。这可能适用于化妆品内容,但是正如您所指出的那样,它仍然可能导致一个部分中包含破损。

  • 跟踪保存文件中使用的生成器版本并做出适当响应。“适当”的意思可能是...

    • 在游戏中可执行所有以前版本的生成器的历史记录,并使用与保存匹配的任何一种。如果玩家继续使用旧的保存,这将更难以修补错误。
    • 警告播放器此保存文件来自旧版本,并提供链接作为单独的可执行文件访问该版本。适用于广告活动持续数小时至数天的游戏,不适用于您预期持续数周或更长时间播放的广告活动。
    • 仅将最新的n生成器版本保留在可执行文件中。如果保存文件使用的是这些最新版本之一,请(提供)将保存文件更新为最新版本。这将使用适当的生成器将任何过时的状态解压缩为文字(或将新生成器在相同种子上的输出中的增量(如果非常相似)解压缩)。从此以后的任何新状态都来自最新的生成器。不过,长时间不玩的玩家可能会落伍。而且在最坏的情况下,您最终会以文字形式存储整个游戏状态,在这种情况下,您也可能...
  • 如果您希望经常更改生成逻辑,并且不想破坏与以前版本的兼容性,请不要依赖生成器确定性:将整个状态保存在保存文件中。(即,“从轨道上将其核杀。这是唯一可以确定的方法”)


如果您建立了生成规则,那么您是否有办法逆转生成?在给定游戏状态的情况下,IE可以将其还原为种子吗?如果您的数据可行,您可以提供一个更新实用程序,而不是将玩家链接到其他游戏版本,该实用程序可以使用旧系统从种子生成世界,然后使用生成的状态为新生成器生成种子。不过,您可能必须警告玩家等待转换。

1
通常这是不可能的。您甚至无法保证新生成器的种子可以提供与旧生成器相同的输出。通常,这些种子包含大约64位,但是您的游戏可以支持的可能世界数量可能大于2 ^ 64,因此每个生成器只会生成其中的一个子集。更换发电机很可能会导致新的水平子集,这可能与先前的发电机组几乎没有交集。
DMGregory

很难选择“正确”的答案。我选择这个是因为它简洁明了,并以清晰的方式总结了主要问题。谢谢。
空值

4

可以说,这种蝴蝶效应的主要来源不是数字生成-它应该足够容易以确保单个数字生成器的确定性-而是客户端代码使用这些数字。代码更改是保持事物稳定的真正挑战。

代码:单元测试确保在某处的某些微小变化不会在其他地方无意间出现的最好方法是在构建中包括针对每个生成方面的全面单元测试。这对于任何紧凑的代码都是如此,在其中更改一件事可能会影响许多其他事情-您需要对所有代码进行测试,以便您可以在一个构建中看到受影响的内容。

数字:周期序列/时隙假设您有一个数字生成器,可为所有内容提供服务。它不分配含义,只是按顺序吐出数字-就像任何PRNG一样。给定两次运行的种子相同,我们得到的序列相同,是吗?现在,您需要思考一下,并决定可能需要定期为游戏提供30个方面的随机值。在这里,我们分配了一个30个时隙的循环序列,例如序列中的每个第一个数字都是崎layout的地形布局,每个第二个数字都是地形扰动...等等。。。。。。。。。。。。。。所以你的月经是30。

10点以后,您将有20个空闲插槽,可随游戏设计的进行其他方面的使用。当然,这里的代价是您必须为插槽11-30生成数字,即使它们当前未使用,即完成周期,以返回到下一个1-10序列。这有CPU成本,尽管应该很小(取决于可用插槽的数量)。另一个缺点是,您需要确保最终设计可以容纳在开发过程开始时可用的插槽数量中……并且您分配的越多,插槽就越“空”您可能必须仔细检查每个步骤,才能使工作正常进行。

其效果是:

  • 您只有一台发电机,可以为所有零件产生数字
  • 更改您需要为其生成数字的方面的数量,不会影响确定性(前提是您的期间足够大,可以容纳所有方面)

当然,在很长一段时间内,您的游戏将无法公开发布(可以说是Alpha版),因此,如果您意识到,您可以从30个方面减少到20个方面,而不会影响任何玩家,只有您自己你已经分配方式在开始太多插槽。这当然可以节省一些CPU周期。但是请记住,无论如何,良好的哈希函数(您可以编写自己的函数)应该很快就能实现。因此,必须运行额外的插槽并不昂贵。


你好 这听起来与我正在做的事情相似。我通常会根据初始世界种子预先生成一堆子种子。最近,我开始预生成一个冗长的噪声数组,然后每个“插槽”只是该数组的索引。这样,每个子系统都可以获取正确的种子并孤立地工作。另一个很棒的技术是使用x,y坐标为每个位置生成一个种子。我正在此堆栈页面上使用来自欣快感答案的代码: programmers.stackexchange.com/questions/161336/…–
null

3

如果您希望持久使用PCG,建议您将PCG代码本身视为data。正如您要在具有常规内容和生成内容的修订版本中保留数据一样,如果您希望在修订版本之间保留数据,则需要保留生成器。

当然,最流行的方法是将生成的数据转换为静态数据,正如您所提到的。

我不知道会有很多生成器版本存在的游戏示例,因为持久性在PCG游戏中并不常见-这就是为什么permadeath与PCG经常并驾齐驱的原因。但是,在同一游戏中有很多PCG的示例,甚至是相同类型的PCG。例如,Unangband为地牢房间提供了许多单独的生成器,并且当添加新生成器时,生成器仍然工作相同。是否可维护取决于您的实现。保持其可维护性的一种方法是使用脚本来实现生成器,并使它们与其余游戏代码隔离。


这是一个聪明的主意,只是将不同的生成器用于不同的区域。
空值

2

除杂物的随机放置外,我维持约30000平方公里的面积,容纳约100万栋建筑物和其他物体。户外模拟 存储的数据约为4 GB。我很幸运有存储空间,但是它并不无限。

随机是随机的,不受控制。但是可以将其笼罩一点:

  • 控制它的开始到结束(如其他文章所述,种子编号和生成的编号数量)。
  • 限制其数字空间,例如。仅生成0到100之间的整数。
  • 通过添加一个值来偏移其数字空间(例如100 + [在0和100之间生成的数字]产生在100和200之间的随机数)
  • 缩放(例如乘以0.1)
  • 并在其周围套上各种笼子。这正在减少,报废了一代人。例如。如果是在二维空间中生成,则可以在数字对的顶部放置一个矩形,然后将外部的数字报废。或圆形或多边形。如果在3D空间中,则只能接受例如驻留在球体内的三胞胎或其他形状(现在是可视化思考,但这不一定与实际可视化或定位有关)。

就是这样 不幸的是,笼子也消耗数据。

芬兰有句话叫Hajota ja hallitse。转化为分而治之

我很快放弃了精确定义最小细节的想法。随机需要​​自由,所以它获得了自由。让蝴蝶飞翔-在它的笼子里。相反,我专注于采用丰富的方式来定义(和维护!)笼子。只要是蓝色或深蓝色(无聊的雇主曾说过:-),它们是什么车都没关系。沿颜色尺寸,“蓝色或深蓝色”是此处的(很小)笼子。

什么是可控制的,用于控制和管理数字空间?

  • 布尔网格是(位很小!)
  • 角点是
  • 就像是树形结构(=跟随“笼子里的笼子”)

在维护方面和版本在兼容性方面……我们有
:if version = n然后
:elseif version = m then ...是的
,代码库越来越大:-)。

熟悉的东西。正确的前进方法应该是定义一个丰富的方法来进行分而治之,并为此牺牲一些数据。然后,在可能的情况下,给予(本地)随机化自由,而对其进行控制并不重要。

并非完全与DMGregory提出的有趣的“ nuke it fom轨道”不兼容,但也许使用小的且精确的核?:-)


感谢您的回答。这听起来好像需要维护的程序区域很大。我可以看到对于这么大的区域,即使您可以访问很多存储空间,也仍然无法简单地存储所有内容。听起来版本控制的生成器将必须成为前进的道路。
null

在所有答案中,我发现自己对此思考最多。我喜欢您对事物的略微哲学描述。在解释这些想法时,我发现“笼子”一词非常有用,因此谢谢您。让蝴蝶在笼子里飞...)
null

PS:我真的很想知道您从事的游戏。您可以分享这些信息吗?
null

可以为数字空间增加一件事:始终保持接近零值是值得的。您可能已经知道了。接近零可提供最佳的数字精度,而最少的位数可达到最高。以后,您总是可以偏移一大堆接近零的数字,但是您只需要一个数字即可。类似地,您可以(我几乎会说您必须)将距离计算移近零值(带有偏移量)。- 暴风城
暴风城

在前面的评估中,考虑较小的车辆运动,以1帧增量为0.01 [米,单位]:您无法以32位数字精度(单精度)准确地计算10000.1 + 0.01,但是可以计算出0.1 + 0.01。因此,如果“动作”发生在很远的地方(在山后:-),则不要去那里,而是将山移动到您身上(移动10000,那么现在的位置为0.1)。对存储空间也有效。人们可能会贪婪地存储彼此接近的数值。一次存储它们的公共部分,然后分别存储变体-可以节省位!您找到链接了吗?;-)
Stormwind
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.