Simulazione della pressione in una simulazione liquida basata su griglia


30

Ho un sistema idrico basato su griglia 2D nel mio gioco XNA, abbiamo un metodo che utilizza automi cellulari per simulare la caduta e la diffusione dell'acqua.

Esempio di acqua che scorre lungo un pendio:

Fisica dell'acqua

Ogni riquadro può contenere una massa da 0 a 255 valori di liquido, memorizzati in un byte. Non uso floats, il vecchio sistema idrico che avevo fatto, tuttavia ha aggiunto complicazioni e ha avuto un impatto sulle prestazioni.

Ogni tessera acqua si aggiorna da sola con un semplice set di regole:

  1. Se la tessera sottostante contiene spazio, sposta il più possibile dalla tessera corrente a quella inferiore (Scorri verso il basso)
  2. Se i 2 lati non sono uguali e non sono zero ed entrambi sono passabili, otteniamo la somma delle 3 tessere (sinistra + corrente + destra) e la dividiamo per 3 lasciando il resto sulla tessera centrale (corrente)
  3. Se la regola sopra ha dato un numero di 2 come somma, dovremmo dividere le tessere nei due lati (1, 0, 1)
  4. Se la regola 2 ha dato 1 come somma, scegli un lato a caso in cui confluire
  5. Se la regola 2 fallisce, dovremmo verificare se una parte è passabile e l'altra no. Se questo è vero, abbiamo diviso la tessera corrente a metà per le 2 tessere

Come posso espandere questa logica per includere la pressione? La pressione farà salire i liquidi sopra le "curve a U" e riempirà le sacche d'aria.

Esempio su come questo al momento fallisce:

Mancata pressione

L'acqua dovrebbe fluire ed equalizzare su ciascun lato della curva a U. Inoltre, ho creato metodi per scoprire fino a che punto si trova un blocco d'acqua e quindi quanta pressione sta subendo. Ora devo essere in grado di prendere questi numeri e applicarli alle altre aree per equalizzare la pressione.


Il problema è che è difficile mantenerlo un automa cellulare. Da ora in poi ogni blocco ha bisogno di sapere di più di quello che c'è accanto. Ho creato un sistema simile a quello che desideri in 3D. È un sistema piuttosto complesso, ma penso che sarebbe più fattibile in 2D.
MichaelHouse

@ Byte56 Beh, non abbiamo bisogno che siano automi cellulari, purché possiamo farli funzionare a una velocità ragionevole.
Cyral

3
Creerò una risposta completa se trovo un po 'di tempo questa sera. Tuttavia, per dirla in parole povere, ho essenzialmente creato la ricerca del percorso per l'acqua. I blocchi vogliono trovare un posto con meno pressione per andare. Trovano il percorso attraverso l'altra acqua cercando un posto che abbia meno acqua di loro (aria vicino all'acqua inclusa). Risolve una grande maggioranza dei casi d'uso.
MichaelHouse

Grazie, sarebbe apprezzato. Ho letto alcune interviste con il creatore di Dwarf Fortress e ha fatto questo credo, ma non ero sicuro di come superare alcuni dei problemi che ha incontrato, quindi non ci ho mai provato.
Cyral,

1
Notare che, una volta aggiunta la pressione dell'aria, i due esempi di sacche d'aria sono potenzialmente completamente validi (camere a pressione chiuse). Suppongo che tu non stia usando 255 byte , ma piuttosto valori 0-255; in ogni caso, probabilmente non vorrai utilizzare l'intera gamma in quel modo. Probabilmente lo limiterei a, hmm, 0-15 per "1 atmosfera" di pressione (non esiste una pressione "negativa", giusto?), Consentendo pressioni più elevate, che attualmente ti mancano. Una volta inclusi i blocchi "aria" nella simulazione, il "peso" naturalmente più elevato dei blocchi d'acqua dovrebbe far sì che scorra attorno alle curve.
Clockwork-Muse

Risposte:


6

Nota che non l'ho mai fatto; queste sono solo idee che possono aiutare. O potrebbe essere totalmente falso. Volevo affrontare questo problema fin da Terraria, ma al momento non sto lavorando a un gioco del genere.

Un modo in cui ho considerato di provare è quello di dare a ciascun blocco di superficie (qualsiasi blocco con acqua al suo interno e senza alcun blocco di acqua al di sopra di esso) un valore di pressione iniziale pari a (o una funzione della) sua altezza dal fondo del mondo. Il valore di pressione implicita di una piastrella invalicabile è MAX_PRESSURE(diciamo 255), e per una piastrella a cielo aperto è MIN_PRESSURE(0).

La pressione viene quindi distribuita su / giù / lateralmente da qualsiasi tessera con una pressione più elevata a tessere con una pressione inferiore durante ogni tick, stile di automi cellulari. Dovrei ottenere una simulazione reale per capire esattamente a cosa pareggiare. La pressione di un blocco dovrebbe essere uguale alla sua pressione implicita più la pressione "in eccesso" da circa equalizzata (quindi dovresti solo memorizzare questa pressione in eccesso, non la pressione implicita).

Se una piastrella di superficie ha una pressione maggiore della sua pressione implicita in base all'altezza e se la piastrella sopra ha spazio libero per l'acqua, una piccola porzione di acqua viene spostata verso l'alto. L'acqua scorre verso il basso solo se la piastrella ha spazio perché ha una pressione inferiore al previsto.

Ciò simula approssimativamente l'idea che più "acqua" è profonda, maggiore è la pressione che ha, anche se i valori di pressione rappresentano più l'altezza rispetto alla pressione effettiva (poiché si prevede che le piastrelle più alte abbiano una "pressione" maggiore). Questo rende la pressione in qualche modo simile al htermine nell'equazione (ma non proprio):

P' = P + qgh

Il risultato è che se la pressione dell'acqua è più alta di quanto dovrebbe essere per la sua profondità, verrà aumentata. Dovrebbe significare che i livelli dell'acqua nei sistemi chiusi uniformeranno la pressione attraverso tutti i livelli di altezza nel tempo.

Non sono sicuro di come affrontare o se si debba anche affrontare le "bolle d'aria" che verrebbero create (laddove una piastrella non di superficie avrà quantità d'acqua non piene quando l'acqua viene spinta verso l'alto). Sono anche ancora insicuro su come eviteresti che i cicli di pressioni dell'acqua siano disuguali da un lato e quindi dopo che il ticchettio sia disuguale dall'altro lato, avanti e indietro.


20

Ho creato un sistema simile a quello che stai cercando in 3D. Ho un breve video che ne dimostra la semplice meccanica qui e un post sul blog qui .

Ecco una piccola gif che ho fatto della meccanica della pressione dietro un muro invisibile (giocato ad alta velocità):

inserisci qui la descrizione dell'immagine

Lasciami spiegare i dati coinvolti, per dare un'idea di alcune delle funzionalità del sistema. Nel sistema attuale, ogni blocco di acqua contiene quanto segue in 2 byte:

//Data2                          Data
//______________________________  _____________________________________
//|0    |0      |000   |000    |  |0        |0       |000      |000   |
//|Extra|FlowOut|Active|Largest|  |HasSource|IsSource|Direction|Height|
//------------------------------  -------------------------------------
  • Height è la quantità di acqua nel cubo, simile alla tua pressione, ma il mio sistema ha solo 8 livelli.
  • Directionè la direzione in cui sta andando il flusso. Quando si decide dove scorrerà successivamente l'acqua, è più probabile che continui nella sua direzione attuale. Questo è anche usato per rintracciare rapidamente un flusso al suo cubo di origine quando necessario.
  • IsSourceindica se questo cubo è un cubo di origine, il che significa che non finisce mai l'acqua. Utilizzato per la sorgente di fiumi, sorgenti, ecc. Il cubo a sinistra nella gif sopra è un cubo sorgente, per esempio.
  • HasSourceindica se questo cubo è collegato a un cubo di origine. Quando sono collegati a una fonte, i cubi proveranno a toccare la fonte per più acqua prima di cercare altri cubi "più pieni" non di fonte.
  • Largestdice a questo cubo qual è il flusso più grande tra esso e il suo cubo di origine. Ciò significa che se l'acqua scorre attraverso uno spazio ristretto, limita il flusso a questo cubo.
  • Activeè un contatore. Quando questo cubo ha un flusso attivo che lo attraversa, verso di esso o da esso, attivo viene incrementato. Altrimenti attivo viene decrementato casualmente. Una volta che l'attivo raggiunge lo zero (che significa non attivo), la quantità di acqua inizierà a ridursi in questo cubo. Questo tipo di azione agisce come l'evaporazione o l'immersione nel terreno. ( Se hai flusso, dovresti avere riflusso! )
  • FlowOutindica se questo cubo è collegato a un cubo che si trova ai margini del mondo. Una volta creato un sentiero ai confini del mondo, l'acqua tende a scegliere quel sentiero rispetto a qualsiasi altro.
  • Extra è un ulteriore vantaggio per un utilizzo futuro.

Ora che conosciamo i dati, diamo un'occhiata a una panoramica di alto livello dell'algoritmo. L'idea di base del sistema è quella di dare priorità al flusso verso il basso e verso l'esterno. Come spiego nel video, lavoro dal basso verso l'alto. Ogni strato di acqua viene elaborato un livello alla volta nell'asse y. I cubi per ogni livello vengono elaborati in modo casuale, ogni cubo tenterà di estrarre acqua dalla sua fonte su ogni iterazione.

I cubetti di flusso estraggono l'acqua dalla loro sorgente seguendo la loro direzione di flusso verso l'alto fino a raggiungere un cubo di sorgente o un cubo di flusso senza parent. Memorizzare la direzione del flusso in ciascun cubo rende semplice seguire il percorso verso l'origine come attraversare un elenco collegato.

Lo pseudo codice per l'algoritmo è il seguente:

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

Le regole di base per espandere un flusso in cui (ordinato per priorità):

  1. Se il cubo sottostante ha meno acqua, scorrere verso il basso
  2. Se il cubo adiacente sullo stesso livello ha meno acqua, scorrere lateralmente.
  3. Se il cubo sopra ha meno acqua E il cubo sorgente è più alto del cubo sopra, scorrere verso l'alto.

Lo so, è piuttosto alto livello. Ma è difficile entrare in modo più dettagliato senza entrare modo nel dettaglio.

Questo sistema funziona abbastanza bene. Posso facilmente riempire pozzi d'acqua, che traboccano per continuare verso l'esterno. Posso riempire tunnel a forma di U come vedi nella gif qui sopra. Tuttavia, come ho detto, il sistema è incompleto e non ho ancora risolto tutto. Non lavoro sul sistema di flusso da molto tempo (ho deciso che non era necessario per l'alfa e l'ho messo in attesa). Tuttavia, i problemi che ho affrontato quando l'ho messo in attesa dove:

  • Piscine . Quando si ottiene una grande pozza d'acqua, i puntatori da bambino a genitore sono come un pasticcio pazzo di qualunque cubo casuale sia stato selezionato per fluire in qualsiasi direzione. Come riempire una vasca da bagno con una corda sciocca. Quando vuoi svuotare la vasca, dovresti seguire il percorso della stringa sciocca fino alla sua fonte? O dovresti semplicemente prendere ciò che è più vicino? Quindi, nelle situazioni in cui i cubi si trovano in un grande pool, probabilmente dovrebbero semplicemente ignorare i flussi dei loro genitori e trarre da ciò che è sopra di loro. Mi è venuto in mente un codice di lavoro di base per questo, ma non ho mai avuto una soluzione elegante di cui potrei essere felice.

  • Genitori multipli . Un flusso figlio può essere facilmente alimentato da più di un flusso padre. Ma il bambino che ha un puntatore a un genitore single non lo permetterebbe. Questo può essere risolto usando abbastanza bit per consentire un bit per ogni possibile direzione del genitore. E probabilmente cambiando l'algoritmo per selezionare casualmente un percorso nel caso di più genitori. Ma non ci sono mai riuscito per testare e vedere quali altri problemi potrebbero esporre.


Grazie! Molto informativo! Inizierò a lavorarci presto e lo accetterò se tutto va bene.
Cyral,

Cosa certa. Immagino un ibrido del tuo sistema e questo sarebbe molto efficace per un mondo 2D. Ping me in chat (con @ byte56) se vuoi discutere i dettagli.
MichaelHouse

Va bene, potrebbe essere un giorno circa prima che io abbia la possibilità di provarlo.
Cyral,

3
Comprensibilmente. Probabilmente ho passato mesi a elaborarlo (e rielaborarlo). Per un po 'ci sarò :)
MichaelHouse

2

Sono un po 'd'accordo con Sean, ma lo farei in modo leggermente diverso:

Un blocco genera una pressione pari al proprio peso (quanta acqua contiene) e la applica ai blocchi sottostanti e accanto. Non vedo motivo per cui la posizione nel mondo sia rilevante.

Ad ogni tick spostare l'acqua da alta pressione a bassa pressione ma spostare solo una frazione dell'acqua necessaria per equalizzare. L'acqua può anche essere sollevata se la pressione nel blocco è troppo grande per la pressione applicata sul quadrato.

Otterrai anelli in cui la pressione dell'acqua scorre troppo lontano in un modo e quindi deve correggersi, ma poiché non sposti l'intera quantità di acqua per tick questi verranno smorzati. Penso che in realtà sia una buona cosa dato che otterrai effetti di sovratensione mentre l'acqua si allaga in un'area come nella realtà.


Se l'acqua dovesse spostarsi verso l'alto quando la pressione applicata dall'alto era troppo elevata, non si spostava in un blocco di pressione inferiore. Affinché la pressione sopra sia troppo grande, dovrebbe essere maggiore del blocco sottostante. Inoltre, la pressione deve spostarsi verso l'alto, verso il basso e verso sinistra / destra.
MichaelHouse

@ Byte56 Stai fraintendendo quello che ho detto. Sto dicendo che l'acqua sale quando la pressione nel blocco che stai analizzando è troppo alta per la pressione applicata dall'alto, non che la pressione dall'alto sia troppo grande!
Loren Pechtel,

OK, quindi lasciami riformulare ciò che hai detto, così capisco: "l'acqua sale quando la pressione nel blocco che stai analizzando è maggiore della pressione applicata dall'alto". È corretto?
MichaelHouse

@ Byte56 Sì. La pressione nel blocco dovrebbe essere il peso dell'acqua sopra di esso o essere applicata lateralmente quando abbiamo una superficie solida da qualche parte sopra. Una pressione troppo bassa significa che non c'è abbastanza acqua sopra, sposta l'acqua verso l'alto.
Loren Pechtel,

Vorrei solo aggiungere che se hai a che fare con l'acqua che scorre non sarà sufficiente e devi anche considerare l'inerzia o l'acqua si muoverà troppo lentamente.
cubo

1

Puoi aggiungere una regola che provi ad andare a sinistra oa destra (attraverso i muri) con le tessere fino a trovare un punto libero, iniziando con i livelli in basso. Se non riesci a trovare, la tessera rimane sulla posizione corrente. Se lo trovi, le altre regole garantiranno la sostituzione del riquadro spostato (se necessario).


Anche questa è una buona idea, non sono sicuro che funzionerebbe in tutti i casi, ma lo terrò in considerazione.
Cyral,

Ok! Fammi sapere se ha funzionato o meno. saluti
almanegra,

Lo farò, sono stato un po 'occupato ultimamente.
Cyral,

-2

perché non puoi definire un altro tipo di blocco che agisce come una quantità immobile di pressione? Pertanto, quando usi il tuo modo di spostare normalmente i blocchi d'acqua e controllare se può spostarsi verso l'alto, non può.

Ancora meglio sarebbe aggiungere un'altra definizione a quei blocchi che consente all'utente di inserire la quantità di pressione per blocco, aumentando la pressione in base alla quantità di blocchi d'acqua che vi si aggiungono.


1
"pertanto, quando usi il tuo modo di muovere normalmente i blocchi d'acqua e controllare se può spostarsi verso l'alto, non può". Sì ... già non può. Questo è il problema, non sto cercando un modo per farlo rimanere lo stesso.
Cyral,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.