【问题标题】:Mathematical question: procedural generation of a galaxy数学问题:星系的程序生成
【发布时间】:2008-12-07 23:32:51
【问题描述】:

我将制作一个完全由程序生成的太空/交易/战斗游戏。但是,我知道将整个银河系的所有细节都存储在内存中是不可行的。结果,我一直认为我可以用一颗种子来生成一个太阳系,从那个太阳系,你可以使用跳跃门前往其他太阳系。问题是,如果我从起始太阳系跳到另一个太阳系,我需要能够回到具有完全相同特征(行星、小行星等)的完全相同的起始太阳系。

基本上,我需要能够从一个数字生成整个星系。从那个生成一个太阳系的数字中,我需要能够生成与第一个太阳系相连的所有其他太阳系,以及与这些太阳系相连的所有太阳系,依此类推。如果我回到它们,每个太阳系都必须保持完全相同的功能。此外,每个太阳系的链接数量可以是随机的,也可以是固定的,由您选择。不过随机的会更好。

【问题讨论】:

  • 听起来你需要一个可以前后移动的 PRNG。前段时间我写了一个梅森捻线器的实现,它可以前后移动。真的,这并不难。
  • 小心点。如果你除以零,我们可能都会消失!
  • 这里有什么问题?

标签: procedural-generation


【解决方案1】:

如果你有勇气,你可以比看看Ian Bell did it 的原始版本 Elite 做得更糟

【讨论】:

  • 我不得不承认,我受到了可能的精英宇宙的巨大启发。 :)
  • 在 Elite 2 中,它甚至是整个星系(虽然很小,但仍然很小),整个游戏都可以放在一张软盘上 (1.44 MB)。
  • 我无法像我想的那样阅读 C 代码;有人能给我解释一下他是怎么做到的吗?
  • @Dove:斐波那契序列 - 您可以从两个种子中编码/生成无限范围的整数变量。这是终极压缩:)
【解决方案2】:

在 Gamasutra 上查看这个系列:

A Real-Time Procedural Universe, the first four links

另外,这个:Algorithms for an infinite universe

【讨论】:

    【解决方案3】:

    这是我理解的基本概念。假设你已经到达星系#42,你需要找出里面有什么。它有nplanets 行星——一个介于 0 和 10 之间的数字,比如说:

    >>> star_system = 42
    >>> nplanets = hash('nplanets%d' % star_system) % (10 + 1)
    >>> nplanets
    4
    

    好的,那么到了 #2 行星,游戏开始时有多少空间站在轨道上?查找 0 到 3 之间的数字:

    >>> planet = 2
    >>> nstations = hash('nstations%d/%d' % (star_system, planet)) % (3 + 1)
    >>> nstations
    1
    

    等等。这些数字都是索引的散列函数(在这种情况下,星系统#42,行星#2),减少到适当的范围。由于散列函数是确定性但“随机”的,因此每次都是相同的,但对玩家来说是随机的。

    当然,散列带有长序列(如“nstations”)的字符串并不是最快的方法,但它显示了这个想法。

    【讨论】:

      【解决方案4】:

      看看原来的Worms 游戏。 我认为它声称有大约 40 亿个可能的级别。 每个级别都是基于大约 20 个字符的短种子字符串生成的。 这决定了

      • 关卡的主题(北极、森林等)
      • 风景的形状
      • 地面很滑
      • 预建关卡细节(雪人、岩石...)的位置
      • 你的蠕虫队、地雷和武器箱的位置。

      如果您喜欢某个关卡,您可以记下种子字符串,并在以后使用它来重新生成相同的关卡。

      这是一个非常复杂但具有确定性的函数示例,只有一个输入参数。我认为这是您需要的基本概念。

      【讨论】:

        【解决方案5】:

        你不能只对星系 ID 进行 SHA1,例如:

        银河 1

        Sha1(1) = 356a192b7913b04c54574d18c28d46e6395428ab
        

        银河 2

        Sha1(2) = da4b9237bacccdf19c0760cab7aec4a8359010b0
        

        银河 3

        Sha1(3) = 77de68daecd823babbb58edb1c8e14d7106e83bb
        

        然后你可以分段代码,IE:

        前 4 个字符 = 行星数

        356a
        da4b
        77de
        

        你需要某种字符串到数字的算法,一个简单的方法是获取每个非数字字符的 ASCII 码,然后将它们全部相乘或其他。

        所以现在我们知道我们的银河系中有多少行星,那么银河系的 x,y,z 维度如何?

        接下来的 9 个字符 = Galaxy 尺寸 (x,y,z)

        原理同上,把代码变成大数。还要进行一些敏感性检查,您不会想要一个 10milesx10milesx10miles 的星系,其中有 2000 万颗行星。有某种加权算法,比如最小大小是# of planets * 10000。你需要使用数字来确保范围都是兼容的,并且从哈希中选择的字符实际上会给你一个合理的范围。

        或者,您可以用一个随机数代替这个,在星系的最小和最大大小之间选择一个数字,但使用一个恒定的 RNG 种子,例如星系 ID!这样,星系的大小对于观察者来说基本上是“随机的”,但它们每次都是一样的。

        等等等等!

        这是获取宇宙属性的一种方式,但是行星属性呢?比如人口和其他东西?

        如果你有拥有 20,000 个行星的 Galaxy 1,你可以这样做:

        Sha1('1:340') = bc02ab36f163baee2a04cebaed8b141505cc26b5
        

        也就是说,银河一号,行星 340。然后,您可以随意拼接该代码。使用哈希的好处是每个星球都应该有一个完全唯一的代码。

        【讨论】:

          【解决方案6】:

          我认为值得注意的是,

          1. 为相同输入生成相同随机输出的生成器称为伪随机数生成器“PRNG”。通常,您在一开始就给它一个“种子”输入数字,然后从中提取随机数字,无需进一步输入即可调用它。注意:你不能“回到”更早的号码,至少不能不从头开始。

          2. 使用坐标作为每次调用的输入调用的类似 PRNG 的函数通常是“噪声”函数。在这里,您没有“无法返回”的问题 - 只需再次使用“早期”输入进行调用。 噪声函数使用 PRNG(作为后端;或者至少可以),它仍然可以在一开始就播种,因此您不会失去“一个数字中的宇宙”功能。

          3. 虽然您每次都可以只使用 PRNG 并将星系坐标组合成一个“种子”,但您最多只能得到“白噪声”,没有其他属性。噪声功能更适合这项工作,因为可以选择甚至调整它以使您具有平铺/平滑/螺旋状的外观/等。结果。 例如,搜索使用 perlin 噪声制作的纹理图像。 我希望你能看到你可以创建相同的噪声函数,例如成千上万的随机云,但是通过根据您的需要调整噪声函数(不仅仅是种子或坐标),您可能会得到熔岩或星系。不过,调整它可能并非易事。

          4. 每次调用的输入坐标数决定了噪声函数的维数,因此对于二维地图(或纹理等),您可以使用二维噪声函数。然后你每次都称它为noise2d(x,y)。

          在您的情况下,我会尝试使用 3 维单纯形噪声函数(单纯形来自 perlin noise 的作者,建议更好)。

          每个星系统坐标三元组然后给你一个结果编号。下一个决定是:这个数字代表什么?为了充分利用单纯形噪声的平滑特性,我会将较小的数字映射到较空的太阳系,将较大的数字映射到质量较大的系统。 或者,也许更好,对于每个系统调用单工噪声多次带有子坐标。中等大小的结果数字是行星,小数字是真空或小行星。大数字明星等。

          该主题不活跃,而且很旧,但搜索可能会像我的一样在这里结束。

          【讨论】:

            【解决方案7】:

            每个太阳系的随机种子是一个可行的解决方案,但我觉得你在这里找错了树。

            玩家可以做些什么来改变那里的东西吗? (比如说,建造一些东西,开采可消耗的资源等?)如果是这样,你无论如何都必须保存状态。

            玩家可以在不返回那里的情况下查看这个地方的情况吗? (如果他不能,为什么不呢?!)你是要查找它还是要再生整个太阳系只是为了找出一条关于它的信息? (PRNG 解决方案不允许您只获得太阳系的一部分,您必须制作整个东西。)

            究竟有多少细节需要保存?

            【讨论】:

              【解决方案8】:

              我不认为“星系”中真的有那么多信息是你无法在今天的计算机上存储的。假设一个星系有 100 颗恒星,每颗恒星有 10 颗行星,每颗行星有 3 颗卫星。那是 100 颗恒星 + 1,000 颗行星 + 3,000 颗卫星,您必须跟踪,即 4,100 颗天体。

              以下是我们可能想要为行星跟踪的事情。

              质量 X,Y,Z 位置 一天的长度(在它自己的轴上旋转的时间) 年长 人口 50种不同资源的资源量

              假设每个值都需要一个 double 来存储它,并且我们有 57 个值要存储(让我们四舍五入说 100),那么我们有 100 个值 * 8 个字节 * 4100 个主体 = 3,280,000 个字节。现在,这是 3 兆的数据。这可能看起来很多,但实际上并没有那么多。另外,我不认为你真的想在一个星系中有这么多恒星。游戏确实太大而无法探索,并且可能会变得难以控制,无法尝试实际模拟给定星系中正在发生的所有事情。

              这样看。如果你拿 SimCity 之类的游戏,将城市网格上的每个方格视为一个潜在的星球,然后你就会意识到在一个小文件中可以存储多少信息,这样你就不必随机生成任何东西。

              【讨论】:

              • 100 颗恒星甚至算不上银河系的一小部分。 Elite 2 有一个直径约 5000 光年的星系,可以放在 1.44 MB 的软盘上。
              • Amiga 版本的 Frontier - Elite II 由单个可执行文件组成,大约大小为 700 KiB。该游戏的特色类似于 10^12 个恒星系统,每个人都由多个天体(多颗恒星、行星、卫星、小行星,如果有人居住的话,还有空间站和空间港口)组成。
              • 一个真正的星系不适合计算机,因为它们有数十亿颗恒星......对于大星系,32 位 int 甚至不足以枚举所有恒星......
              • 虽然“我认为“星系”中并没有太多的信息无法存储在今天的计算机上。”是真的(取决于详细程度),但假设的数字是荒谬的。请参阅en.wikipedia.org/wiki/Galaxy(“星系的大小从只有几亿 (10^8) 颗恒星的矮星到拥有一百万亿 (10^14) 颗恒星的巨星”)
              【解决方案9】:

              我使用 Mersenne Twister。它是 PRNG,它接受任何长度的种子数组。
              例如,我想在坐标 x=25,y=72 上生成星系。我用种子 [25,72] 重新启动捻线机。
              如果我想在这个星系中生成第 1138 颗行星,我使用 [25,72,1138]。
              国家? [25,72,1138,10]
              城市? [25,72,1138,10,32]
              等等。
              使用这种方法,您只需使用一个数字(x 坐标之前的那个,在我们的例子中是 25 之前)就可以生成数十亿个对象。
              现在有一些项目在使用它。
              Noctis:anynowhere.com/
              无限宇宙:http://www.infiniverse-game.com/

              【讨论】:

                【解决方案10】:

                假设你从星系的种子开始,即 1234,取这个种子,生成 10000 个随机数,每个随机数代表一个星系。当您接近恒星时,您获取恒星的随机数,并将其作为新随机函数的种子。为围绕恒星运行的天体数量生成一个随机数,并为每个天体生成一个数(始终使用第二个随机函数),依此类推。 我不知道这是否对您有帮助,但您需要记住,随机函数内部是混乱的,对于初始条件,整个函数会发生变化。

                银河系中恒星的种子必须始终产生相同的恒星,恒星的种子必须产生相同的物体,等等。

                您始终可以使用统计数据、密度计算、改进结果来让事情变得更有趣。始终检查函数是否会针对相同的输入产生相同的结果。

                对不起,如果我的英语很烂,我来自阿根廷,英语不是我训练有素的素质之一:p

                PD:我在做同样类型的游戏;)

                【讨论】:

                  【解决方案11】:

                  您可以从某个种子(“母数”)构建一个由 N 位数字组成的伪随机数。然后,您将数字分组并用于回答您的问题。

                  示例:N=20

                  -> 一位数:有多少个额外的跳跃门?
                  -> 三位数:用于生成每个可用跳转的相应长度的种子
                  -> 六位数:生成这个太阳系的种子
                  -> 十位数:为每个相连的太阳系生成一个新的 20 位数种子的种子

                  然后递归。每个系统(具有稳定的轨道等)在时间 0 生成,您必须计算它现在的样子

                  当然,这种从母系统开始的方法意味着当前系统离母系统越远,生成数据所需的时间就越长。此外,这种方式会生成一棵树,而不是一张网(这是我所期望的)。

                  我认为生成坐标会更好——使用平面中的极坐标,也许是三维的椭球体。

                  【讨论】:

                  • 我认为 Web 部件至关重要。也许我应该采取自上而下的方法。
                  【解决方案12】:

                  我隐约记得以前做过这件事。在 90 年代初期,分形风靡一时,我记得有一家公司为游戏程序员提供世界。他们创造了一个无限的宇宙,充满了太阳和行星的星系,事件一直延伸到山谷和行星上地方的纹理。他们提议为游戏开发商寻找合适的虚拟房地产。游戏开发者将获得软件来渲染和使用它,以及在这个分形宇宙中他们的财产的确切坐标。

                  我在谷歌上搜索了几分钟“分形游戏世界星球宇宙”等,但没有找到。可能是Pandromeda,但我记不太清了。

                  你应该为这个想法研究分形。您所需要的只是一个连续的数字字段,您可以从种子中重新创建这些数字,然后将这些数字呈现为具有不同属性的恒星、行星和卫星。

                  【讨论】:

                    【解决方案13】:

                    如果你真的想回到一个固定的状态,我不认为从单个值生成程序是正确的方法。

                    假设,您在每个平面中有一个 256x256 系统的固定网格,在宇宙中有 16 个平面。每架飞机有多达 512 个交易站和多达 8 个与其他飞机的链接。所有的交易站和链接都在一个固定的位置。 您的初始种子值必须至少为 2^76 才能对所有可能的宇宙进行编码。 添加更多对象(行星、船舶等),数量呈指数增长。

                    编辑: 如果您在每个系统中不允许多个交易站或链接,则它会少一些。 我会使用一些永久存储,可能是像 Firebird 或 sqlite 这样的嵌入式数据库。顺便说一句,我目前正在开发这样的游戏。

                    【讨论】:

                    • 如果您使用伪随机生成器则不会。
                    • ...所以两个随机的 64 位值足以容纳该信息,对吧?
                    【解决方案14】:

                    这是我想出的。不知道它是否会是最终版本。

                    想象一个六边形网格,每个顶点都有一个太阳系。因为,我们在一个六边形网格上,所以从任何顶点只有三条线。一个总是水平的,另外两个是对角线的。如果我们给起始种子一个值n,我们可以给水平连接到起始点的太阳系一个值n+1,其他的得到n+2和n-2的值。

                    哦,废话。我们不一定会得到一个网格。该死的。让我们再试一次。

                    【讨论】:

                      【解决方案15】:

                      如果您使用伪随机数生成器,则可以保证您生成的每个随机数都将从给定种子开始以相同的顺序出现。 生成由给定数字作为种子的系统的代码在每次生成时都会显示相同。

                      使用伪随机数流中的第一个数字来生成“门”的数量。 通过每个门并从数字流中获取一个值,以分配给每个目标系统并为其播种。

                      根据该种子生成每个系统的特征。

                      有几种已知的算法可以生成随机种子。

                      Mersenne twister一个破解

                      【讨论】:

                        【解决方案16】:

                        只要您使用相同的种子调用 srandom(),您就会从 random() 中获得相同的值。因此,只需一次调用 srandom() 就可以将星系统中的所有内容都建立在基础之上……然后,您只需要为整个星系统存储 1 个整数(种子)。现在就是压缩!

                        【讨论】:

                          【解决方案17】:

                          这是我的第二个改进的解决方案。玩家将从一个随机生成的太阳系开始。每个系统都连接到 1 到 4 个其他系统。将它们视为北、南、东和西系统。如果玩家要通过北跳门,他将被带到一个种子比之前的系统多一个的系统。如果他往南走,那个系统的种子就会少一粒。 2+ 和 2- 分别代表东和西。到这些系统的距离(以秒差距或光年为单位)是根据系统的种子和您到达的方向计算的。这样,星系的大小仅受用于容纳种子的数量的最大值和最小值的限制。

                          通往其他星系的曲速孔将放置在距起始系统一定距离的位置。下一个星系就像这个星系的延续,种子数量将以同样的方式增加,而位于星系曲速洞另一端的系统将只是“东”或“北” " 从启动系统连接。

                          顺便说一句,与上述解决方案不同,这种递增种子的使用导致了网络。此外,您可以看到此方法使用四边形,而上述解决方案使用六边形,这使得无法创建网络。

                          当然,所有这些都是基于这样的假设,即所有种子都会生成一个随机的数字序列,该序列不同于任何其他序列。这使得每个系统都是独一无二的。

                          【讨论】:

                          • 但是除非您存储所有其他随机序列并进行比较,否则您不会知道随机序列是唯一的。否则,你只是在赌赔率。
                          • 是的,我相信每个数字都会给我一个唯一的数字序列。但是,如果有一千亿个太阳系,即使有重复,我怀疑玩家永远不会看到它们。
                          【解决方案18】:

                          我怀疑你将面临的最大问题是有一个命名系统来以对玩家一致且有意义的方式命名所有这些对象 - 尽管我们确实有 naming real objects systematically. 的方案我忘记了 Elite 的命名是否约定在某一点之后就失效了……

                          【讨论】:

                            【解决方案19】:

                            从数学上讲,您希望随机/伪随机生成一个无向连通图。在这个图中,每个节点都是一个太阳系,每条边都是一个跳门。

                            1) 创建 N 个节点并为每个节点随机分配一个空间坐标。这些节点最终会成为你的太阳系。

                            2) 使用 Deluanay 三角剖分算法生成边。我建议使用 Deluanay 三角剖分,因为它会创建一个外观相当漂亮的地图,并且没有任何门相互交叉,但您实际上可以使用任何您想要的算法。我真的不知道你在找什么。

                            3) 如果你使用了 Deluanay 三角剖分,我建议你消除一定数量的边以创建“稀疏性”。这将使地图更有趣,因为某些地点将成为交通枢纽,而其他地点则只是进站。

                            4) 保存此图表。这是你的宇宙。不要放错地方或扔掉你的宇宙。尽可能高效地存储它,但不要删除任何信息。

                            5) 为每个节点分配一个种子,并使用该种子生成每个太阳系。

                            6) 恭喜,您现在拥有一个包含任意数量的太阳系和跳跃门的宇宙。

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 1970-01-01
                              • 2011-06-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2018-06-04
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多