Cómo crear una cascada de física 2D

8

Estoy tratando de crear una cascada que se parezca a la primera imagen a continuación (consulte este video para tener una mejor idea de lo que quiero lograr) con propiedades físicas para que pueda moverse o rodear objetos con colisionadores en su curso (similar al segunda imagen a continuación). Aunque la cascada vinculada es 3d, estoy interesado en una implementación 2D.

Tenga en cuenta que los objetos son dinámicos y algunos se mueven con frecuencia, por lo tanto, la cascada tiene que remodelar cada cuadro. Los objetos se mueven lentamente ya sea moviéndose a diferentes posiciones o girando. ¿Cómo se puede hacer esto?

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

OnlyCodeMatters
fuente

Respuestas:

13

Quería ver si podía lograr esto sin regenerar dinámicamente la malla para la cascada en cada cuadro. Resulta que hay un camino. :RE

Animación de cascadas

Cada objeto que puede bloquear la cascada (objetos que llevan un WaterCatcherguión en mi prototipo) tiene una malla de contorno que rodea su perímetro. (Esto se puede generar automáticamente por adelantado utilizando la forma del colisionador)

Esta malla de contorno hace que el agua fluya a lo largo del objeto. Uso un sombreador para clipsacar la parte que está debajo del objeto. También sigo un punto de "captura" izquierdo y derecho donde una cascada cae sobre el objeto y fluye hacia la izquierda o hacia la derecha, respectivamente, para poder clipver la parte que está a la izquierda de la cascada derecha y a la derecha de la cascada izquierda.

Animación que muestra malla de piel de agua

Entonces las caídas verticales son solo primitivas cuádruples básicas, estiradas a la longitud adecuada. Utilizo otro sombreador para desplazar la textura de la cascada sobre las cataratas y desvanecerla en los extremos superior e inferior. Luego coloco un sistema de partículas de espuma en el punto de impacto para ayudar a cubrir la mezcla.

Aquí hay un primer plano para que pueda ver las partes componentes.

Cerrar todavía de efecto cascada

En la parte superior tengo una cascada "raíz" para comenzar. Cada cuadro, después de que todas las Update()secuencias de comandos se han ejecutado para mover las cosas, dispara CircleCasthacia abajo, para ver si el agua golpea algo. Si golpea a WaterCatcher, le dice que muestre su capa de agua aguas abajo del punto de golpe.

Determino "aguas abajo" usando el golpe normal: si está muy cerca de la vertical, o si la cascada entrante abarca bordes que se inclinan en ambas direcciones, entonces derramamos tanto a la izquierda como a la derecha.

Cada uno WaterCatchertiene su propia cascada a la izquierda y a la derecha, que habilita y coloca en su extremo más alejado si se derrama en esa dirección; de lo contrario, permanecen ocultos. Estas cascadas a su vez disparan CircleCasthacia abajo para encontrar en qué se derraman, y así sucesivamente ...

El prototipo todavía tiene algunas fallas visuales que podrían mejorarse: el flujo de agua a lo largo de un objeto aparece de una vez en lugar de animarse, y las reglas de flujo podrían usar un poco de tolerancia o histéresis adicionales para que no se corte tan fácilmente. objetos rotativos Sin embargo, creo que estos deberían ser problemas bastante solucionables.

Fondo, rocas y texturas de plataforma giratoria a través de Kenney


Estos son los trucos que uso en mi sombreador de fragmentos de colector de agua:

// My wraparound geometry is build so the "x+" UV direction
// points "outward" from the object.
// Using derivatives, I can turn this into a vector in screen space.
// We'll use this below to clip out water hanging off the bottom.
float2 outward = float2(ddx(i.uv.x), ddy(i.uv.x));

// i.worldX is the worldspace x position of this fragment
// (interpolated from the vertex shader)

// _LeftX is a material property representing the worldspace x coordinate
// of the rightmost water flow that's spilling left,
// and _RightX the wold x of the leftmost water flow that's spilling right.
float left = _LeftX - i.worldX;   // +ve if we're to the left of a left spill.
float right = i.worldX - _RightX; // +ve if we're to the right of a right spill.

float limit = max(left, right); // +ve if we're in the path of either flow.

// If the "outward" vector is pointing down, make this negative.
limit = min(limit, outward.y + 0.001f);

// If any of the conditions above make limit <= 0, abort this fragment.
clip(limit);

// Otherwise, scroll the water texture!
// Counter-clockwise if we're in the left flow, clockwise otherwise.
i.uv.y -= sign(left) * _Time.y;
DMGregory
fuente
Esto es genial. ¿Tiene algún plan para proporcionar el código aquí o en GitHub para que la comunidad pueda contribuir a realizar mejoras? Si no, por favor considere hacerlo.
Contención
No hay tales planes en la actualidad. Las imágenes anteriores se crearon con una prueba de concepto rápida y hacky, no un paquete independiente que sería adecuado para ese tipo de uso. Avíseme si necesita una mano para replicar algo y puedo guiarlo a través de lo que se necesita.
DMGregory
He podido hacer la partícula de espuma, pero necesito ayuda para crear el sombreador de agua y, en segundo lugar, recortar el sombreador de agua alrededor de las formas (WaterCatcher).
Contención
He agregado un fragmento de código de sombreador que muestra cómo funciona el sombreador de recorte.
DMGregory
@DMGregory WoooW !!! Lo siento, no he estado aquí por un tiempo. Estaba trabajando en otras cosas. Ahora estoy revisando su solución
OnlyCodeMatters
1
  1. Puede deformar la malla de cascada en la colisión de objetos para que coincida con el patrón de colisionador requerido.

  2. El más fácil y más preciso, pero con mayor rendimiento, el sistema de partículas de uso intensivo, crea un sistema de partículas con colisionadores y usa cada partícula como una gota de agua. Pero eso parece un poco extraño si tiene un sprite predeterminado y si el recuento de partículas es pequeño y son demasiado grandes. Pero tiene un alto rendimiento, por lo que no quieres simulación de moléculas en tu juego.

Yo iría con 1.

  • Pero la CPU de deformación de malla es lenta, pero puede funcionar para usted.
  • Usaría sombreadores para lograr esto, por ejemplo, sombreador de agua , la malla tiene efectos como si colisionara con otras mallas y es mucho más rápido que los métodos anteriores. Supongo que es posible hacer una malla para detener el renderizado en alguna forma proyectada: ese es el cambio que necesitaría hacer en el sombreador de agua normal, es complicado de lograr si no está familiarizado con los sombreadores.

No hay solución fácil con buen rendimiento.

Resultado del sistema de partículas: (para cambiar los valores tuve que esperar unos 3-4s, es lento) Resultado de colisión del sistema de partículas

Candid Moon _Max_
fuente
Una alternativa al punto 1, pero podría ser un poco más complejo, es tener su propio sistema de "partículas" que suelte los colisionadores de puntos / esferas y cumpla con la física. Y luego "simplemente" genera una malla basada en esos puntos. más o menos cómo funciona el renderizador de línea. Lo único que debe resolverse es cómo dividir los puntos para ir en 2 direcciones al golpear un colisionador como lo hace en su captura de pantalla. Pero no pienses que tiene que ser difícil. O simplemente puede colocar los puntos estáticamente y dibujar una línea entre ellos. Ya solo necesitas un bonito sombreador.
Sidar
@Sidar Sí, enfoque interesante. Uno de los métodos también sería crear múltiples renderizadores de pistas y moverlos relativamente desde el punto de división. Y verifique las colisiones a través de algún componente colisionador, pero eso es solo una solución.
Candid Moon _Max_
De todos modos, si OP no desea configurarlo manualmente, los datos deben ser dinámicos o estáticos. Me pregunto si podría usar un sistema de partículas normal con colisión habilitada y luego aproximarse entre puntos para dibujar una tira cuádruple. El recuento de partículas no tiene que ser alto para esto.
Sidar
¿No es eso por partícula aunque? Supongo que dependiendo del estilo que elijas podría funcionar bastante bien.
Sidar