グリッドベースの液体シミュレーションでの圧力のシミュレーション


30

XNAゲームには2Dグリッドベースの水システムがあり、セルオートマトンを使用して水の落下と広がりをシミュレートする方法があります。

斜面を流れる水の例:

水の物理

各タイルには、0〜255の値の液体を1バイトで格納できます。私が使用していfloatsた古い水道システムは使用しませんが、複雑さを増し、パフォーマンスに打撃を与えました。

各水タイルは、単純なルールセットで自動的に更新されます。

  1. 下のタイルにスペースがある場合は、可能な限り現在のタイルから下のタイルに移動します(フローダウン)
  2. 2つの側面が同じでなく、ゼロでなく、両方とも通過可能である場合、3つのタイルの合計(左+現在+右)を取得し、3で除算して残りを中央(現在の)タイルに残します
  3. 上記のルールが合計として2の数を与えた場合、タイルを2つの側面(1、0、1)に分割する必要があります
  4. ルール2が合計として1を与えた場合、流入するランダムな側を選択します
  5. ルール2が失敗した場合、一方が合格でもう一方が合格でないかどうかを確認する必要があります。それが当てはまる場合、2つのタイルに対して現在のタイルを半分に分割します

このロジックを拡張してプレッシャーを含めるにはどうすればよいですか?圧力がかかると、液体が「Uベンド」を超えて上昇し、エアポケットが満たされます。

これが現在どのように失敗するかの例:

圧力が失敗する

水はUベンドの両側で流れ、均等化されるはずです。さらに、ウォーターブロックがどれくらい下にあるか、したがってどの程度の圧力が発生しているかを調べるためのメソッドを作成しました。ここで、これらの数値を取得して他の領域に適用し、圧力を均等化できるようにする必要があります。


問題は、セルオートマトンを維持するのが難しいことです。それ以来、各ブロックはその隣にあるもの以上のものを知る必要があります。3Dで必要なシステムに似たシステムを作成しました。これはかなり複雑なシステムですが、2Dではより実行可能になると思います。
マイケルハウス

@ Byte56 セルオートマトンである必要ありません、妥当な速度で実行し続けることができます。
Cyral

3
今晩のうちに時間があれば、完全な回答を作成します。しかし、簡単に言えば、私は本質的に水の経路探索を作成しました。ブロックは、移動するプレッシャーの少ない場所を見つけたいと考えています。彼らは他の水を見つけて、自分よりも水が少ない場所を探します(水の隣の空気が含まれています)。ユースケースの大部分を解決します。
マイケルハウス

感謝します。ドワーフ要塞の製作者とのインタビューをいくつか読んだところ、彼はこれをやったのですが、彼が遭遇した問題のいくつかを克服する方法がわからなかったので、実際に試したことはありませんでした。
Cyral

1
空気圧を追加すると、2つのエアポケットの例が完全に有効になる可能性があることに注意してください(閉じた圧力室)。255 バイトではなく、0〜255の値を使用していると仮定しています。いずれにせよ、そのように全範囲を使用することはおそらくないでしょう。おそらく、「1気圧」の圧力(0〜15気圧)に0〜15に制限し(「負圧」というものはありませんか?)、現在は不足しているより高い圧力を許可します。シムに「空気」ブロックを含めると、水ブロックの自然に高い「重量」により、曲がりの周りを流れるようになります。
時計仕掛けのミューズ

回答:


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軸で一度に1レベル処理されます。各レベルのキューブはランダムに処理され、各キューブは各反復でソースから水を引き出そうとします。

フローキューブは、ソースキューブまたは親のないフローキューブに到達するまで、フローの方向を逆にたどることにより、ソースから水を引き出します。各キューブにフローの方向を格納すると、リンクリストをたどるのと同じくらい簡単にソースへのパスをたどることができます。

アルゴリズムの擬似コードは次のとおりです。

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. 上のキューブの水が少なく、ソースキューブが上のキューブよりも高い場合、上に流れます。

かなり高いレベルです。しかし、詳細に道を譲ることなく、より詳細に取り組むことは困難です。

このシステムは非常にうまく機能します。私は簡単に水の穴を埋めることができます。上のgifにあるように、U字型のトンネルを埋めることができます。しかし、私が言ったように、システムは不完全であり、まだすべてを解決していません。私は長い間フローシステムに取り組んでいませんでした(アルファには必要ないと判断し、保留にしました)。ただし、保留にしたときに対処していた問題は次のとおりです。

  • プール。大きなプールを得るとき、子供から親へのポインタは、どんな方向に流れるように選択されたどんなランダムな立方体の狂った混乱のようです。浴槽に馬鹿げた糸を入れるようなものです。浴槽から水を排出したいときは、愚かなひもの経路をたどって元に戻りますか?または、あなたは最も近いものをとるべきですか?そのため、キューブが大きなプールにある状況では、キューブは単に親フローを無視し、キューブの上にあるものからプルする必要があります。私はこのためにいくつかの基本的な動作コードを思いつきましたが、満足できるエレガントなソリューションはありませんでした。

  • 複数の親。子ストリームは、複数の親ストリームから簡単にフィードできます。しかし、単一の親へのポインタを持つ子供はそれを許可しません。これは、可能な親の方向ごとにビットを使用できるように十分なビットを使用することで修正できます。また、複数の親の場合、アルゴリズムを変更してパスをランダムに選択する可能性があります。しかし、他のどのような問題が公開されるかをテストして確認するために、私はそれを回避しませんでした。


ありがとう!非常に有益です!私はすぐにこれに取り組み始め、すべてがうまくいけば受け入れます。
Cyral

確実なこと。私はあなたのシステムのハイブリッドを想像しますが、これは2Dの世界に非常に効果的です。詳細について議論したい場合は、チャット(@ byte56)でPingしてください。
マイケルハウス

申し分なく、私はこれを試す機会を得る前に1日かそこらかもしれません。
Cyral

3
分かりました。私はおそらくそれを解決するために数ヶ月を費やしました(そして、それを再構成します)。私はしばらくの間だろう:)
マイケルハウス

2

私はショーンに少し同意しますが、私は少し違ったやり方をします。

ブロックは、自重(その中にどれだけの水が入っているか)に等しい圧力を生成し、それをその下と横のブロックに適用します。世界での地位が重要である理由はないと思います。

各ティックで、水を高圧から低圧に移動しますが、均等化に必要な水のほんの一部を移動します。ブロック内の圧力が正方形にかかる圧力に対して大きすぎる場合、水を押し上げることもできます。

水圧が一方向に流れすぎて修正しなければならないループが発生しますが、ティックごとに水の全量を移動しないため、これらは減衰します。実際にあなたがそうするような地域に水があふれるとき、あなたはサージ効果を得るので、それは実際に良いことだと思います。


上から加えられる圧力が大きすぎるときに水が上に移動した場合、それはより低い圧力ブロックに移動しません。上の圧力が大きすぎるためには、下のブロックよりも大きくなければなりません。さらに、圧力は上下、左右に移動する必要があります。
マイケルハウス

@ Byte56あなたは私が言ったことを誤解しています。分析中のブロック内の圧力が上からかけられる圧力に対して高すぎる場合に水が上がると言います。上からの圧力が大きすぎることではありません!
ローレンペクテル

さて、あなたが言ったことを言い換えてみましょう。「分析しているブロックの圧力が上から加えられている圧力よりも大きい場合、水が上がる」ということです。あれは正しいですか?
MichaelHouse

@ Byte56はい。ブロック内の圧力は、その上にある水の重量か、固体表面が上のどこかにあるときに横向きにかかる必要があります。圧力が小さすぎると、上に十分な水がないことを意味するので、水を上げます。
ローレンペクテル

あなたが流れる水を扱っている場合、これは十分ではなく、慣性を考慮する必要がありますか、水が非常に遅く移動することを追加したいと思います。
キューブ

1

下部のレイヤーから始めて、空きスポットが見つかるまで、タイルを左または右に(壁を通り抜けて)試みるルールを追加できます。見つからない場合、タイルは現在の位置に留まります。見つかった場合、別のルールにより、移動したタイルの置換が保証されます(必要な場合)。


これも良いアイデアです。すべての場合にうまくいくかどうかはわかりませんが、検討します。
Cyral

OK!うまくいったかどうか教えてください。よろしく
アルマネグラ

最近少し忙しくなります。
-Cyral

-2

不動の圧力として機能する別のタイプのブロックを定義できないのはなぜですか?したがって、水ブロックを通常の方法で移動し、上に移動できるかどうかを確認する方法を使用すると、それはできません。

さらに良いのは、ユーザーがブロックごとに圧力の量を入力できる別の定義をそれらのブロックに追加し、それに追加する水ブロックの量に応じて圧力を上げることです。


1
「したがって、通常、水ブロックを移動し、上に移動できるかどうかを確認する方法を使用すると、それはできません。」うん...もうできない。それが問題です、私はそれを同じままにする方法を探していません。
Cyral
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.