Creé un sistema similar al que buscas en 3D. Tengo un video corto que demuestra la mecánica simple de esto aquí y una publicación de blog aquí .
Aquí hay un pequeño gif que hice de la mecánica de presión detrás de un muro invisible (jugado a alta velocidad):
Permítanme explicar los datos involucrados, para dar una idea de algunas de las características del sistema. En el sistema actual, cada bloque de agua contiene lo siguiente en 2 bytes:
//Data2 Data
//______________________________ _____________________________________
//|0 |0 |000 |000 | |0 |0 |000 |000 |
//|Extra|FlowOut|Active|Largest| |HasSource|IsSource|Direction|Height|
//------------------------------ -------------------------------------
Height
es la cantidad de agua en el cubo, similar a su presión, pero mi sistema solo tiene 8 niveles.
Direction
es la dirección en la que va el flujo. Al decidir dónde fluirá el agua a continuación, es más probable que continúe en su dirección actual. Esto también se utiliza para rastrear rápidamente un flujo hasta su cubo de origen cuando sea necesario.
IsSource
indica si este cubo es un cubo fuente, lo que significa que nunca se queda sin agua. Usado para la fuente de ríos, manantiales, etc. El cubo a la izquierda en el gif de arriba es un cubo fuente, por ejemplo.
HasSource
indica si este cubo está conectado a un cubo fuente. Cuando se conecta a una fuente, los cubos intentarán aprovechar la fuente para obtener más agua antes de buscar otros cubos sin fuente "más completos".
Largest
le dice a este cubo cuál es el flujo más grande entre él y su cubo fuente. Esto significa que si el agua fluye a través de un espacio estrecho, limita el flujo a este cubo.
Active
Es un mostrador. Cuando este cubo tiene un flujo activo que lo atraviesa, hacia él o desde él, el activo se incrementa. De lo contrario, el activo se disminuye aleatoriamente. Una vez que el activo llegue a cero (lo que significa que no está activo), la cantidad de agua comenzará a reducirse en este cubo. Este tipo de actos como evaporación o remojo en el suelo. ( Si tienes flujo, ¡deberías tener reflujo! )
FlowOut
indica si este cubo está conectado a un cubo que está en el borde del mundo. Una vez que se hace un camino hacia el borde del mundo, el agua tiende a elegir ese camino sobre cualquier otro.
Extra
es un bit extra para uso futuro.
Ahora que conocemos los datos, veamos una descripción general de alto nivel del algoritmo. La idea básica del sistema es priorizar el flujo hacia abajo y hacia afuera. Como explico en el video, trabajo de abajo hacia arriba. Cada capa de agua se procesa un nivel a la vez en el eje y. Los cubos para cada nivel se procesan al azar, cada cubo intentará extraer agua de su fuente en cada iteración.
Los cubos de flujo extraen agua de su fuente siguiendo su dirección de flujo hacia arriba hasta que alcanzan un cubo fuente o un cubo de flujo sin padre. Almacenar la dirección del flujo en cada cubo hace que seguir la ruta a la fuente sea tan fácil como atravesar una lista vinculada.
El pseudocódigo para el algoritmo es el siguiente:
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()
Las reglas básicas para expandir un flujo donde (ordenado por prioridad):
- Si el cubo de abajo tiene menos agua, fluya hacia abajo
- Si el cubo adyacente en el mismo nivel tiene menos agua, fluya lateralmente.
- Si el cubo de arriba tiene menos agua Y el cubo de fuente es más alto que el cubo de arriba, fluya hacia arriba.
Lo sé, eso es bastante alto nivel. Pero es difícil entrar en más detalle sin conseguir manera en detalles.
Este sistema funciona bastante bien. Puedo llenar fácilmente pozos de agua, que se desbordan para continuar hacia afuera. Puedo llenar túneles en forma de U como ves en el gif de arriba. Sin embargo, como dije, el sistema está incompleto y aún no he resuelto todo. No he trabajado en el sistema de flujo en mucho tiempo (decidí que no era necesario para alfa y lo puse en espera). Sin embargo, los problemas con los que estaba lidiando cuando lo puse en espera fueron:
Piscinas . Al obtener un gran charco de agua, los punteros de niño a padre son como un desorden loco de cualquier cubo aleatorio seleccionado para fluir en cualquier dirección. Como llenar una bañera con una cuerda tonta. Cuando desee drenar la bañera, ¿debe seguir el camino de la cuerda tonta de regreso a su fuente? ¿O deberías tomar lo que sea más cercano? Entonces, en situaciones en las que los cubos están en un gran grupo, es probable que simplemente ignoren sus flujos primarios y tomen todo lo que esté por encima de ellos. Se me ocurrió un código de trabajo básico para esto, pero nunca tuve una solución elegante con la que pudiera estar feliz.
Múltiples padres . Una secuencia secundaria podría ser alimentada fácilmente por más de una secuencia principal. Pero el niño que tiene un puntero a un padre soltero no lo permitiría. Esto se puede solucionar mediante el uso de suficientes bits para permitir un bit para cada posible dirección principal. Y es probable que cambie el algoritmo para seleccionar aleatoriamente una ruta en el caso de varios padres. Pero, nunca pude probarlo y ver qué otros problemas podrían exponer.