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à):
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.
IsSource
indica 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.
HasSource
indica 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.
Largest
dice 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! )
FlowOut
indica 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à):
- Se il cubo sottostante ha meno acqua, scorrere verso il basso
- Se il cubo adiacente sullo stesso livello ha meno acqua, scorrere lateralmente.
- 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.