Simulation des Drucks in einer netzbasierten Flüssigkeitssimulation


30

Ich habe in meinem XNA-Spiel ein auf einem 2D-Gitter basierendes Wassersystem. Wir haben eine Methode, die zellulare Automaten verwendet, um das Fallen und die Ausbreitung von Wasser zu simulieren.

Beispiel von Wasser, das einen Hang hinunter fließt:

Wasserphysik

Jede Kachel kann eine Masse von 0 bis 255 Flüssigkeitswerten enthalten, die in einem Byte gespeichert sind. Ich benutze nicht floatsdas alte Wassersystem, das ich getan habe, aber es fügte Komplikationen hinzu und hatte einen Leistungseinbruch.

Jedes Wasserplättchen aktualisiert sich selbst mit einem einfachen Regelwerk:

  1. Wenn die Kachel darunter Platz hat, bewegen Sie sich so weit wie möglich von der aktuellen Kachel zur untersten (Nach unten fließen).
  2. Wenn die beiden Seiten nicht gleich und nicht Null sind und beide passierbar sind, erhalten wir die Summe der 3 Kacheln (links + aktuell + rechts) und teilen sie durch 3, wobei der Rest auf der mittleren (aktuellen) Kachel verbleibt
  3. Wenn die obige Regel eine Zahl von 2 als Summe ergibt, sollten wir die Kacheln in zwei Seiten teilen (1, 0, 1)
  4. Wenn Regel 2 1 als Summe ergibt, wählen Sie eine zufällige Seite, in die Sie einfließen möchten
  5. Wenn Regel 2 fehlschlägt, sollten wir prüfen, ob eine Seite passierbar ist und die andere nicht. Wenn dies zutrifft, teilen wir die aktuelle Kachel für die 2 Kacheln in zwei Hälften

Wie kann ich diese Logik um Druck erweitern? Durch den Druck steigen Flüssigkeiten über die "U-Bögen" und füllen die Lufteinschlüsse.

Beispiel, wie dies derzeit fehlschlägt:

Druck ausgefallen

Das Wasser sollte auf jeder Seite des U-Bogens fließen und sich ausgleichen. Außerdem habe ich Methoden entwickelt, um herauszufinden, wie weit ein Wasserblock entfernt ist und wie viel Druck er ausübt. Jetzt muss ich in der Lage sein, diese Zahlen zu nehmen und sie auf die anderen Bereiche anzuwenden, um den Druck auszugleichen.


Das Problem ist, dass es schwierig ist, es als zellulare Automaten zu halten. Seitdem muss jeder Block mehr als nur wissen, was sich daneben befindet. Ich habe ein System erstellt, das dem gewünschten in 3D ähnelt. Es ist ein ziemlich komplexes System, aber ich denke, es wäre in 2D besser umsetzbar.
MichaelHouse

@ Byte56 Nun, wir brauchen es nicht, um zellulare Automaten zu sein, solange wir es mit angemessener Geschwindigkeit laufen lassen können.
Cyral

3
Ich werde eine vollständige Antwort erstellen, wenn ich heute Abend etwas Zeit finde. Einfach ausgedrückt, habe ich die Wegfindung für das Wasser geschaffen. Blöcke wollen mit weniger Druck irgendwo hingehen. Sie finden den Weg durch das andere Wasser und suchen nach einem Ort, der weniger Wasser als sie hat (Luft neben Wasser eingeschlossen). Es löst eine große Mehrheit der Anwendungsfälle.
MichaelHouse

Danke, das wäre sehr dankbar. Ich habe ein paar Interviews mit dem Hersteller der Zwergenfestung gelesen und er hat das getan, glaube ich, aber ich war nicht sicher, wie ich einige der Probleme überwinden konnte, auf die er gestoßen ist, also habe ich es nie wirklich versucht.
Cyral

1
Beachten Sie, dass nach dem Hinzufügen des Luftdrucks die beiden Beispiele für Lufteinschlüsse möglicherweise vollständig gültig sind (geschlossene Druckkammern). Ich gehe davon aus, dass Sie nicht 255 Bytes verwenden , sondern Werte zwischen 0 und 255 ; Auf jeden Fall werden Sie auf diese Weise wahrscheinlich nicht die gesamte Bandbreite nutzen wollen. Ich würde es wahrscheinlich auf, hmm, 0-15 für '1 Atmosphäre' Druck begrenzen (es gibt keinen 'negativen' Druck, richtig?), Was höhere Drücke zulässt, die Ihnen derzeit fehlen. Sobald Sie die 'Luft'-Blöcke in die Sim einbeziehen, sollte das natürlich höhere' Gewicht 'der Wasserblöcke dazu führen, dass sie um die Kurven fließen.
Clockwork-Muse

Antworten:


6

Beachten Sie, dass ich das noch nie gemacht habe. Dies sind nur Ideen, die helfen können. Oder könnte völlig gefälscht sein. Ich wollte dieses Problem seit Terraria angehen, arbeite aber derzeit nicht an einem solchen Spiel.

Ich habe versucht, jedem Oberflächenwasserblock (jedem Block mit Wasser und ohne Wasserblock darüber) einen Anfangsdruckwert zu geben, der gleich (oder abhängig von) seiner Höhe vom Grund der Welt ist. Der implizite Druckwert einer unpassierbaren Fliese ist MAX_PRESSURE(z. B. 255), und für eine offene Fliese ist MIN_PRESSURE(0).

Der Druck wird dann von jedem Plättchen mit höherem Druck nach oben / unten / seitwärts auf die Plättchen mit niedrigerem Druck während jedes Ticks im zellularen Automatenstil verteilt. Ich müsste eine tatsächliche Simulation erstellen, um genau herauszufinden, worauf es ankommt. Der Druck eines Blocks sollte gleich dem impliziten Druck plus dem "Überdruck" von ungefähr ausgeglichen sein (Sie müssen also nur diesen Überdruck speichern, nicht den impliziten Druck).

Wenn eine Fliese einen Druck hat, der größer ist als ihr impliziter Druck auf der Höhe, und wenn die darüber liegende Fliese freien Platz für Wasser hat, wird ein kleiner Teil des Wassers nach oben bewegt. Wasser fließt nur dann nach unten, wenn beide Fliesen Platz haben und einen geringeren Druck als erwartet haben.

Dies simuliert grob die Vorstellung, dass je tiefer der Wasser- "Punkt", desto mehr Druck vorhanden ist, obwohl die Druckwerte mehr die Höhe als den tatsächlichen Druck repräsentieren (da bei höheren Fliesen ein höherer "Druck" erwartet wird). Dies macht den Druck ein bisschen wie der hAusdruck in der Gleichung (aber nicht wirklich):

P' = P + qgh

Das Ergebnis ist, dass wenn der Druck des Wassers höher ist, als er für seine Tiefe sein sollte, es nach oben gedrückt wird. Dies sollte bedeuten, dass der Wasserstand in geschlossenen Systemen den Druck über alle Höhenstufen hinweg im Laufe der Zeit ausgleicht.

Ich bin mir nicht sicher, wie ich damit umgehen soll oder ob ich überhaupt mit den "Luftblasen" umgehen muss, die entstehen würden (wenn eine nicht oberflächliche Fliese nicht volle Wassermengen aufweist, wenn Wasser nach oben gedrückt wird). Ich bin mir auch immer noch unsicher, wie Sie vermeiden könnten, dass Wasserdruckschleifen auf der einen Seite ungleich sind und dann nach dem Ankreuzen auf der anderen Seite ungleich sind, hin und her.


20

Ich habe ein System erstellt, das dem in 3D ähnelt, nach dem Sie suchen. Ich habe ein kurzes Video die einfache Mechanik davon demonstriert hier und eine Blog - Post hier .

Hier ist eine kleine Beschreibung der Druckmechanik hinter einer unsichtbaren Wand (gespielt mit hoher Geschwindigkeit):

Bildbeschreibung hier eingeben

Lassen Sie mich die Daten erläutern, um einen Eindruck von einigen Funktionen des Systems zu erhalten. In dem gegenwärtigen System enthält jeder Wasserblock das Folgende in 2 Bytes:

//Data2                          Data
//______________________________  _____________________________________
//|0    |0      |000   |000    |  |0        |0       |000      |000   |
//|Extra|FlowOut|Active|Largest|  |HasSource|IsSource|Direction|Height|
//------------------------------  -------------------------------------
  • Height ist die Wassermenge im Würfel, ähnlich wie Ihr Druck, aber mein System hat nur 8 Ebenen.
  • Directionist die Richtung, in die der Fluss fließt. Bei der Entscheidung, wohin das Wasser als nächstes fließen soll, ist es wahrscheinlicher, dass es in die aktuelle Richtung fließt. Dies wird auch verwendet, um einen Flow bei Bedarf schnell bis zu seinem Quellcube zurückzuverfolgen.
  • IsSourceGibt an, ob es sich bei diesem Würfel um einen Quellwürfel handelt. Dies bedeutet, dass ihm niemals das Wasser ausgeht. Wird für die Quelle von Flüssen, Quellen usw. verwendet. Der Würfel links im GIF oben ist beispielsweise ein Quellwürfel.
  • HasSourceGibt an, ob dieser Cube mit einem Quellcube verbunden ist. Wenn Würfel an eine Quelle angeschlossen sind, versuchen sie, auf die Quelle zu tippen, um mehr Wasser zu erhalten, bevor sie nach anderen "volleren" Würfeln suchen, die keine Quelle sind.
  • Largestteilt diesem Würfel mit, welcher Fluss zwischen ihm und seinem Quellwürfel am größten ist. Das heißt, wenn Wasser durch einen engen Spalt fließt, wird der Durchfluss zu diesem Würfel begrenzt.
  • Activeist ein Zähler. Wenn dieser Würfel einen aktiven Fluss durchläuft, zu ihm oder von ihm, wird active inkrementiert. Ansonsten wird active zufällig dekrementiert. Sobald Aktiv Null erreicht (dh nicht aktiv), wird die Wassermenge in diesem Würfel verringert. Diese Art von Handlungen wirken wie Verdampfen oder Einweichen in den Boden. ( Wenn Sie Strömung haben, sollten Sie Ebbe haben! )
  • FlowOutGibt an, ob dieser Würfel mit einem Würfel am Rand der Welt verbunden ist. Sobald ein Weg zum Rand der Welt gefunden ist, wählt das Wasser diesen Weg eher als jeden anderen.
  • Extra ist ein zusätzliches Bit für die zukünftige Verwendung.

Nachdem wir die Daten kennen, sehen wir uns eine allgemeine Übersicht über den Algorithmus an. Die Grundidee des Systems besteht darin, das Ab- und Abfließen zu priorisieren. Wie ich im Video erkläre, arbeite ich von unten nach oben. Jede Wasserschicht wird auf der y-Achse stufenweise abgearbeitet. Die Würfel für jedes Level werden nach dem Zufallsprinzip verarbeitet. Jeder Würfel versucht bei jeder Iteration, Wasser aus seiner Quelle zu ziehen.

Strömungswürfel ziehen Wasser aus ihrer Quelle, indem sie ihrer Strömungsrichtung folgen, bis sie einen Quellwürfel oder einen Strömungswürfel ohne übergeordnetes Element erreichen. Durch das Speichern der Flussrichtung in jedem Cube wird das Verfolgen des Pfads zur Quelle so einfach wie das Durchlaufen einer verknüpften Liste.

Der Pseudocode für den Algorithmus lautet wie folgt:

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()

Die Grundregeln zum Erweitern eines Ablaufs lauten (geordnet nach Priorität):

  1. Wenn der Würfel darunter weniger Wasser hat, fließen Sie nach unten
  2. Wenn der benachbarte Würfel auf demselben Niveau weniger Wasser hat, fließen Sie seitlich.
  3. Wenn der darüberliegende Würfel weniger Wasser enthält UND der Quellwürfel höher als der darüberliegende Würfel ist, fließen Sie nach oben.

Ich weiß, das ist ziemlich hoch. Aber es ist schwer , mehr ins Detail zu bekommen , ohne sie Weg ins Detail.

Dieses System funktioniert ziemlich gut. Ich kann leicht Wassergruben auffüllen, die überlaufen, um nach außen zu gelangen. Ich kann U-förmige Tunnel füllen, wie Sie oben im GIF sehen. Wie gesagt, das System ist unvollständig und ich habe noch nicht alles ausgearbeitet. Ich habe lange nicht mehr am Flow-System gearbeitet (ich entschied, dass es für Alpha nicht benötigt wurde und stellte es in die Warteschleife). Die Probleme, mit denen ich mich befasste, als ich es auf Eis legte, waren jedoch:

  • Pools . Wenn Sie einen großen Wasserbecken bekommen, sind die Zeiger von Kind zu Eltern wie eine verrückte Sauerei, egal welcher zufällige Würfel ausgewählt wurde, um in welche Richtung zu fließen. Als würde man eine Badewanne mit einer dummen Schnur füllen. Wenn Sie die Wanne entleeren möchten, sollten Sie dem Pfad der dummen Schnur zurück zu ihrer Quelle folgen? Oder solltest du einfach nehmen, was am nächsten ist? In Situationen, in denen sich Cubes in einem großen Pool befinden, sollten sie wahrscheinlich nur die übergeordneten Flows ignorieren und von allem, was sich über ihnen befindet, ziehen. Ich hatte einen grundlegenden Arbeitscode dafür, aber nie eine elegante Lösung, mit der ich zufrieden sein konnte.

  • Mehrere Eltern . Ein untergeordneter Stream kann leicht von mehr als einem übergeordneten Stream gespeist werden. Aber das Kind, das einen Zeiger auf einen einzelnen Elternteil hat, würde das nicht zulassen. Dies kann behoben werden, indem genügend Bits verwendet werden, um ein Bit für jede mögliche übergeordnete Richtung zuzulassen. Und wahrscheinlich wird der Algorithmus so geändert, dass bei mehreren Elternteilen zufällig ein Pfad ausgewählt wird. Aber ich bin nie dazu gekommen, um zu testen und zu sehen, welche anderen Probleme dies aufdecken könnte.


Vielen Dank! Sehr informativ! Ich werde bald anfangen daran zu arbeiten und es akzeptieren, wenn alles gut geht.
Cyral

Sichere Sache. Ich stelle mir einen Hybrid Ihres Systems vor, und dieser wäre für eine 2D-Welt sehr effektiv. Pingen Sie mich im Chat an (mit @ byte56), wenn Sie Details besprechen möchten.
MichaelHouse

Okay, vielleicht dauert es einen Tag, bis ich die Gelegenheit bekomme, das auszuprobieren.
Cyral

3
Verständlicherweise. Ich habe wahrscheinlich Monate damit verbracht, es auszuarbeiten (und es erneut auszuarbeiten). Ich werde aber eine Weile hier sein :)
MichaelHouse

2

Ich stimme Sean zu, aber ich würde es ein bisschen anders machen:

Ein Block erzeugt einen Druck in Höhe seines Eigengewichts (wie viel Wasser sich darin befindet) und legt ihn an die darunter und daneben liegenden Blöcke an. Ich sehe keinen Grund, warum seine Position in der Welt relevant ist.

Bewegen Sie bei jeder Zecke das Wasser von Hochdruck auf Niederdruck, bewegen Sie jedoch nur einen Bruchteil des zum Ausgleich benötigten Wassers. Wasser kann auch nach oben gedrückt werden, wenn der Druck im Block zu hoch ist, als dass der Druck auf das Quadrat ausgeübt werden könnte.

Sie erhalten Schleifen, in denen der Wasserdruck zu weit in eine Richtung fließt und dann korrigiert werden muss. Da Sie jedoch nicht die gesamte Wassermenge pro Tick bewegen, werden diese gedämpft. Ich denke, es ist tatsächlich eine gute Sache, da es zu Überspannungen kommt, wenn Wasser in einen Bereich strömt, wie Sie es in der Realität tun würden.


Wenn sich Wasser nach oben bewegt, wenn der von oben ausgeübte Druck zu hoch ist, bewegt es sich nicht in einen Block mit niedrigerem Druck. Damit der Druck darüber zu groß ist, müsste er größer sein als der Block darunter. Zusätzlich muss sich der Druck sowohl nach oben als auch nach unten und links / rechts bewegen.
MichaelHouse

@Byte56 Du interpretierst falsch, was ich gesagt habe. Ich sage, das Wasser steigt an, wenn der Druck in dem zu analysierenden Block zu hoch ist, um von oben ausgeübt zu werden, nicht, dass der Druck von oben zu hoch ist!
Loren Pechtel

OK, lassen Sie mich das, was Sie gesagt haben, umformulieren, damit ich verstehe: "Das Wasser steigt, wenn der Druck in dem zu analysierenden Block größer ist als der von oben ausgeübte Druck". Ist das korrekt?
MichaelHouse

@ Byte56 Ja. Der Druck im Block sollte das Gewicht des Wassers darüber sein oder seitlich angewendet werden, wenn wir irgendwo darüber eine feste Oberfläche haben. Zu wenig Druck bedeutet, dass oben nicht genug Wasser ist. Bewegen Sie das Wasser nach oben.
Loren Pechtel

Ich möchte nur hinzufügen, dass, wenn Sie mit fließendem Wasser zu tun haben, dies nicht ausreicht und Sie auch die Trägheit berücksichtigen müssen, da sich das Wasser sonst zu langsam bewegt.
Würfel

1

Sie können eine Regel hinzufügen, die versucht, mit den Kacheln nach links oder rechts (durch die Wände) zu gehen, bis Sie eine freie Stelle finden, beginnend mit den Ebenen auf der Unterseite. Wenn Sie nicht finden können, bleibt die Kachel an der aktuellen Position. Wenn Sie feststellen, garantieren die anderen Regeln das Ersetzen der verschobenen Kachel (falls erforderlich).


Dies ist auch eine gute Idee, nicht sicher, ob es in allen Fällen funktionieren würde, aber ich werde darüber nachdenken.
Cyral

Okay! Lassen Sie mich wissen, ob es funktioniert hat oder nicht. Grüße
Almanegra

Ich werde in letzter Zeit nur ein bisschen beschäftigt sein.
Cyral

-2

Warum können Sie keine andere Art von Block definieren, die als unbewegliche Menge an Druck wirkt? Wenn Sie also die Wasserblöcke normal bewegen und prüfen, ob sie sich nach oben bewegen lassen, kann dies nicht passieren.

Noch besser wäre es, diesen Blöcken eine weitere Definition hinzuzufügen, mit der der Benutzer die Menge des Drucks pro Block eingeben und den Druck entsprechend der Menge der hinzugefügten Wasserblöcke erhöhen kann.


1
"Daher kann es nicht funktionieren, wenn Sie die Wasserblöcke normal bewegen und prüfen, ob sie sich nach oben bewegen lassen." Ja ... es kann schon nicht. Das ist das Problem, ich bin nicht auf der Suche nach einer Möglichkeit, es gleich zu halten.
Cyral
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.