在基于网格的液体模拟中模拟压力


30

我在XNA游戏中有一个基于2D网格的水系统,我们有一种使用元胞自动机模拟水的滴落和扩散的方法。

沿斜坡流下的水的示例:

水物理学

每个图块可以包含0到255个质量的液体值,以字节为单位存储。我没有使用floats旧的供水系统,但是它增加了复杂性并影响了性能。

每个水铺瓦都会使用一组简单的规则进行更新:

  1. 如果下面的图块中有空间,请尽可能多地从当前图块移至最下面的图块(向下流动)
  2. 如果2个边不相同且都不为零并且都可以通过,我们将获得3个图块的总和(左+当前+右),并将其除以3,剩下的就放在中间(当前)图块上
  3. 如果上述规则给出的总和为2,则我们应将图块分为两侧(1、0、1)
  4. 如果规则2的总和为1,则选择一个随机面流入
  5. 如果规则2失败,我们应该检查一侧是否可以通过,另一侧是否可以通过。如果是这样,我们将当前图块一分为二

如何扩展这种逻辑以包含压力?压力会使液体从“ U形弯头”上方上升并充满气穴。

当前如何失败的示例:

压力故障

水应在U型弯管的两侧流动并平衡。此外,我还创建了一些方法来找出水障碍到底有多远,以及因此而受到的压力。现在,我需要能够获取这些数字并将其应用于其他区域以均衡压力。


问题是很难将其保持为细胞自动机。从现在起,每个区块都需要了解更多,而不仅仅是下一步。我创建了一个与3D所需系统类似的系统。这是一个非常复杂的系统,但我认为它在2D模式下会更可行。
MichaelHouse

@ Byte56好吧,只要我们可以使其以合理的速度运行,我们就不需要将它用作细胞自动机。
Cyral

3
如果今天晚上有空,我会给出一个完整的答案。但是,简单地说,我实质上是为水创建了寻路。街区希望找到压力较小的地方。他们通过寻找其他水寻找比其水少的地方(包括水的空气)。它解决了大多数用例。
MichaelHouse

谢谢,不胜感激。我阅读了《矮人要塞》制作人的一些采访,他确实做到了这一点,但是我不确定如何克服他遇到的一些问题,所以我从未真正尝试过。
Cyral

1
请注意,一旦增加气压,两个气袋示例就可能完全有效(封闭的压力腔)。我假设您使用的不是255 个字节,而是0-255;无论如何,您可能都不希望那样使用全部范围。对于“ 1个大气压”,我可能会将其限制在0mm到15毫米之间(没有“负”压力,对吗?),允许您目前缺乏更高的压力。一旦在模拟中包含“空气”块,水块的自然较高的“重量”应会导致其在折弯处流动。
Clockwork-Muse

Answers:


6

请注意,我从未这样做过;这些只是可能有用的想法。或者可能完全是伪造的。自Terraria以来,我一直想解决这个问题,但目前尚未从事此类游戏。

我考虑过尝试的一种方法是为每个地表水块(其中有水并且上面没有水块的任何水块)赋予初始压力值,该值等于(或者是其距世界底部的高度的函数)。任何无法通行的瓷砖的隐含压力值为MAX_PRESSURE(例如255),而露天瓷砖的隐含压力值为MIN_PRESSURE(0)。

然后,在每个滴答,细胞自动机样式期间,压力会从较高压力的任何瓷砖向上/向下/向侧面扩散到较低压力的瓷砖。我必须进行实际的模拟才能弄清楚要等于什么。块的压力应等于其隐含压力加上均衡附近的“过剩”压力(因此,您只需要存储此过剩压力,而不是隐含压力)。

如果表面瓷砖的压力大于其隐式基于高度的压力,并且上面的瓷砖具有自由的水空间,则一小部分水将向上移动。仅当两个瓷砖都有足够的空间且压力比预期的要低时,水才会向下流。

这粗略地模拟了这样一个想法,即水的“点”越深,它所具有的压力就越大,尽管压力值表示的高度比实际压力要大(因为期望更高的瓷砖具有更高的“压力”)。这使得压力有点像h方程中的术语(但不是真的):

P' = P + qgh

结果是,如果水的压力高于其应有的深度,它将被推上去。这应该意味着封闭系统中的水位将随着时间的推移均衡所有高度上的压力。

我不确定如何处理或不确定是否需要处理将产生的“气泡”(当水被向上推时,非表面瓷砖的水量将不满)。我也仍然不确定如何避免水压循环在一侧不相等,然后在另一侧滴答不答之后来回移动。


20

我创建了一个类似于您在3D环境中所追求的系统。我有一个视频短片,说明它的简单机械在这里和博客文章在这里

这是我在不可见的墙壁(高速播放)后面的压力力学制成的一点gif:

在此处输入图片说明

让我解释所涉及的数据,以了解系统的某些功能。在当前系统中,每块水包含2字节的内容:

//Data2                          Data
//______________________________  _____________________________________
//|0    |0      |000   |000    |  |0        |0       |000      |000   |
//|Extra|FlowOut|Active|Largest|  |HasSource|IsSource|Direction|Height|
//------------------------------  -------------------------------------
  • Height 是立方体中的水量,类似于您的压力,但是我的系统只有8个水位。
  • Direction是流动的方向。在决定下一个水流向何处时,它更有可能沿当前方向继续流动。在需要时,它还可用于快速向后追溯流回到其源多维数据集。
  • IsSource指示此多维数据集是否为源多维数据集,这意味着它永远不会耗尽水。用于河流,泉水等的源。例如,上方gif左侧的多维数据集是源多维数据集。
  • HasSource指示此多维数据集是否已连接到源多维数据集。连接到水源时,多维数据集将尝试在寻找其他“更饱满”的非水源多维数据集之前,先从水源中获取更多的水。
  • Largest告诉此多维数据集及其源多维数据集之间最大的流量是多少。这意味着,如果水流过一个狭窄的缝隙,它将限制该立方体的流量。
  • Active是一个柜台。当此多维数据集有活动流经过,到达或从中流出时,活动流将递增。否则,活动值将随机减少。一旦激活达到零(意味着不激活),该立方体中的水量将开始减少。这种行为就像蒸发或浸入地下。(如果有流量,就应该退潮!
  • FlowOut指示此多维数据集是否已连接到位于世界边缘的多维数据集。一旦建立了通往世界边缘的道路,水往往会优先选择该道路。
  • Extra 是将来使用的额外费用。

既然我们已经知道了数据,那么让我们看一下该算法的高级概述。该系统的基本思想是优先考虑流出和流出。正如我在视频中解释的那样,我从头开始工作。每层水在y轴上一次处理一次。每个级别的多维数据集都是随机处理的,每个多维数据集将在每次迭代中尝试从其源中抽取水。

流动立方体按照其流动方向返回,从水源中拉出水,直到它们到达水源立方体或没有母体的流动立方体。将流动方向存储在每个多维数据集中,使遵循到源的路径就像遍历链表一样容易。

该算法的伪代码如下:

for i = 0 to topOfWorld //from the bottom to the top
   while flowouts[i].hasitems() //while this layer has flow outs
       flowout = removeRandom(flowouts[i]) //select one randomly
       srcpath = getPathToParent(flowout) //get the path to its parent
       //set cubes as active and update their "largest" value
       //also removes flow from the source for this flow cycle
       srcpath.setActiveAndFlux() 

//now we deal with regular flow
for i = 0 to topOfWorld //from the bottom to the top
    while activeflows[i].hasitems() //while this layer has water
        flowcube = removeRandom(activeflows[i]) //select one randomly
        //if the current cube is already full, try to distribute to immediate neighbors
        flowamt = 0
        if flowcube.isfull 
           flowamt = flowcube.settleToSurrounding
        else
           srcpath = getPathToParent(flowcube) //get the path to its parent
           flowamt = srcpath.setActiveAndFlux()
           flowcube.addflow(flowamt)

        //if we didn't end up moving any flow this iteration, reduce the activity
        //if activity is 0 already, use a small random chance of removing flow
        if flowamt == 0
           flowcube.reduceActive()

 refillSourceCubes()

扩展流的基本规则,其中(按优先级排序):

  1. 如果下面的立方体水少,则向下流
  2. 如果相邻的同一个立方体的水较少,则横向流动。
  3. 如果上方的多维数据集的水较少,并且源多维数据集高于上方的多维数据集,则向上流动。

我知道,那是很高的水平。但它很难进入更详细的没有得到的方式进入细节。

该系统运行良好。我可以很容易地装满水坑,水溢出继续向外延伸。如上图所示,我可以填满U形隧道。但是,正如我所说,系统不完整,我还没有解决所有问题。我已经很长时间没有在流量系统上工作了(我认为alpha不需要它,所以我将其搁置)。但是,当我将其置于以下位置时,我正在处理的问题是:

  • 水池。当得到一大堆水时,从儿童到父母的指针就像是一团乱麻,无论选择了哪个随机立方体都可以向任何方向流动。就像用愚蠢的绳子填充浴缸一样。当您想排干木盆时,您是否应该沿着傻弦的路径回到其根源?还是应该随便拿什么?因此,在多维数据集位于一个大池中的情况下,它们可能应该只是忽略其父流,而从其上方的任何内容中拉取。为此,我想出了一些基本的工作代码,但是从来没有一个我满意的优雅解决方案。

  • 多父母。一个子流很容易由多个父流提供。但是孩子有一个指向单亲的指针是不允许的。可以通过使用足够的位以允许每个可能的父方向使用一个位来解决此问题。如果有多个双亲,可能会更改算法以随机选择一条路径。但是,我从来没有绕过它来测试和查看可能会出现的其他问题。


谢谢!非常翔实!如果一切顺利,我将尽快开始研究并接受。
Cyral

当然可以 我想像一下您的系统的混合体,这个混合体对于2D世界非常有效。如果您想讨论详细信息,请与我聊天(使用@ byte56)。
MichaelHouse

好吧,可能要等一天左右才能有机会尝试一下。
赛勒尔(Cyral),

3
可以理解 我可能花了几个月的时间来解决(并重新解决)。我会待一会儿:)
MichaelHouse

2

我有点同意肖恩的观点,但我会有所不同:

砌块产生的压力等于其自身重量(其中有多少水),并将其施加到下方和旁边的砌块。我认为没有任何理由与它在世界上的地位相关。

在每个刻度上,水从高压移动到低压,但仅移动均衡所需的一部分水。如果砌块中的压力太大,以至于向下施加在正方形上的压力也可以将水推上去。

您将得到回路,其中水压会以一种方式流得太远,然后必须进行校正,但是由于您不会移动每个刻度的全部水量,因此这些回路将被阻尼。我认为这实际上是一件好事,因为像现实中那样,当水泛滥到某个区域时,您将获得电涌效应。


如果当从上方施加的压力太大时水要向上移动,它就不会移动到低压块中。为了使上面的压力太大,它必须大于下面的块。另外,压力必须向上和向下以及向左/向右移动。
MichaelHouse

@ Byte56您误解了我说的话。我的意思是,当您正在分析的模块中的压力对于从上方施加的压力而言过高时,水就上升了,而不是从上方施加的压力过大!
罗伦·佩希特尔

好的,让我重新表述您的意思,以便理解:“当您要分析的模块中的压力大于从上方施加的压力时,水就会上升”。那是对的吗?
MichaelHouse

@ Byte56是的。砌块中的压力应该是上面的水的重量,或者当我们上面的某个地方有固体表面时向侧面施加的压力。压力太小意味着上方没有足够的水,请向上移动水。
罗伦·佩希特尔

我想补充一点,如果您要处理流动的水,这将是不够的,并且还必须考虑惯性,否则水的流动速度会太慢。
多维数据集

1

您可以添加一条规则,尝试使用图块向左或向右(穿过墙),直到找到一个自由点,从底部的图层开始。如果找不到,则图块将停留在当前位置。如果找到,则其他规则将保证替换已移动的图块(如有必要)。


这也是一个好主意,不确定是否在所有情况下都可以使用,但我会考虑的。
Cyral

好!让我知道它是否有效。问候
almanegra

我会,最近有点忙。
Cyral

-2

为什么不能定义另一种不能移动的压力块?因此,当您以正常方式移动水块并检查水块是否可以向上移动时,它就无法移动。

更好的做法是在这些块中添加另一个定义,使用户可以输入每个块的压力量,并根据添加到其中的水块量来增加压力。


1
“因此,当您使用正常方式移动水块并检查水块是否可以向上移动时,它就不能移动。” 是的...已经不能了。多数民众赞成在问题,我不是在寻找一种方法来保持它不变。
Cyral
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.