Como criar uma cascata de física 2D

8

Estou tentando criar uma cachoeira que se parece com a primeira imagem abaixo (verifique este vídeo para ter uma idéia melhor do que eu quero alcançar) com propriedades físicas para que ele possa mover ou contornar objetos com coletores em seu curso (semelhante ao segunda imagem abaixo). Embora a cascata vinculada seja 3D, estou interessado em uma implementação 2D.

Observe que os objetos são dinâmicos e alguns se movem com frequência, portanto a cascata precisa remodelar todos os quadros. Os objetos se movem lentamente, movendo-se para posições diferentes ou girando. Como isso pode ser feito?

insira a descrição da imagem aqui

insira a descrição da imagem aqui

OnlyCodeMatters
fonte

Respostas:

13

Eu queria ver se conseguiria fazer isso sem regenerar dinamicamente a malha da cachoeira a cada quadro. Acontece que há um caminho. : D

Animação de cascatas

Cada objeto que pode bloquear a cascata (objetos com um WaterCatcherscript no meu protótipo) tem uma malha de contorno em volta do seu perímetro. (Isso pode ser gerado automaticamente com antecedência usando a forma do colisor)

Essa malha de estrutura de tópicos processa a água que flui ao longo do objeto. Uso um shader para extrair clipa parte que está embaixo do objeto. Também rastreio um ponto de captura esquerdo e direito em que uma cachoeira cai no objeto e flui para a esquerda ou direita, respectivamente, para que eu possa ver clipa parte que fica à esquerda da cachoeira direita e à direita da cachoeira esquerda.

Animação mostrando a malha de pele de água

Então as quedas verticais são apenas primitivas quad básicas, esticadas no comprimento apropriado. Eu uso outro shader para rolar a textura da cachoeira sobre as quedas e desbotá-la nas extremidades superior e inferior. Em seguida, aplico um sistema de partículas de espuma no ponto de impacto para ajudar a cobrir a mistura.

Aqui está um close para que você possa ver as peças componentes.

Close-up ainda de efeito cascata

No topo, tenho uma cachoeira "raiz" para começar. Cada quadro, depois que todos os Update()scripts correm para mover as coisas, ele dispara CircleCastpara baixo, para ver se a água bate em alguma coisa. Se ele atinge a WaterCatcher, ele diz para mostrar a pele da água a jusante do ponto de acerto.

Eu determino "jusante" usando o golpe normal - se estiver muito próximo da vertical, ou se a cascata de entrada ultrapassar as bordas que se inclinam nas duas direções, então derramaremos à esquerda e à direita.

Cada WaterCatcherum tem sua própria cachoeira esquerda e direita, que habilita e posiciona na extremidade oposta, se derramar nessa direção - caso contrário, elas permanecem ocultas. Essas cachoeiras, por sua vez, disparam CircleCastpara baixo para encontrar o que elas derramam, e assim por diante ...

O protótipo ainda tem algumas falhas visuais que podem ser melhoradas - o fluxo de água ao longo de um objeto aparece ao mesmo tempo, em vez de animar, e as regras de fluxo podem usar um pouco de tolerância ou histerese, para que não corra tão facilmente objetos rotativos. Eu acho que essas devem ser questões bastante solucionáveis.

Texturas de fundo, rochas e plataformas rotativas via Kenney


Aqui estão os truques que eu uso no meu shader de fragmento de coletor de água:

// 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
fonte
Isso é ótimo. Você tem planos de fornecer o código aqui ou no GitHub para que a comunidade possa contribuir para fazer melhorias? Caso contrário, considere fazê-lo.
Contenção
Não existem planos no momento. As imagens acima foram criadas com uma prova de conceito rápida e hacky, e não um pacote independente que seria adequado para esse tipo de uso. Deixe-me saber se você precisar de uma mão para replicar alguma coisa e eu posso orientá-lo sobre o que é necessário.
DMGregory
Consegui fazer a partícula de espuma, mas preciso de ajuda para criar o shader de água e, em segundo lugar, prendê-lo em volta das formas (WaterCatcher).
Contenção
Adicionei um trecho de código de sombreador mostrando como o sombreador de recorte funciona.
DMGregory
@DMGregory WoooW !!! Desculpe, não estou aqui há um tempo. Estava trabalhando em outras coisas. Agora estou passando por sua solução
OnlyCodeMatters
1
  1. Você pode deformar a malha em cascata na colisão de objetos para corresponder ao padrão de colisor necessário.

  2. O sistema de partículas de uso pesado mais fácil e mais preciso, mas com melhor desempenho - cria um sistema de partículas com coletores e usa todas as partículas como uma gota de água. Mas isso parece um pouco estranho se você tiver sprite padrão e se a contagem de partículas for pequena e eles forem muito grandes. Mas é um desempenho pesado, então você não quer simulação de moléculas no seu jogo.

Eu iria com 1.

  • Mas CPU de deformação de malha - é lento, mas pode funcionar para você.
  • Eu usaria shaders para conseguir isso - exemplo do shader de água - a malha tem efeitos como se colidisse com outras malhas e é muito mais rápida do que os métodos anteriores. Eu acho que é possível fazer malha para parar a renderização em alguma forma projetada - essa é a alteração que você precisaria fazer no sombreador normal de água, que é difícil de conseguir se você não é familiar com sombreadores.

Nenhuma solução fácil com bom desempenho.

Resultado do sistema de partículas: (Para alterar os valores, tive que esperar cerca de 3-4s, é lento) Resultado da colisão do sistema de partículas

Lua Espontânea _Max_
fonte
Alternativa ao ponto 1, mas pode ser um pouco mais complexo, é ter seu próprio sistema de "partículas", que elimina pontos / coletores de esferas e obedece à física. E então "simplesmente" gere uma malha com base nesses pontos. praticamente como o renderizador de linha funciona. A única coisa que precisa ser resolvida é como você dividiria os pontos em duas direções ao atingir um colisor, como acontece na captura de tela. Mas não pense que precisa ser difícil. OU você pode simplesmente colocar os pontos estaticamente e traçar uma linha entre eles. Você só precisa de um sombreador bonito.
Sidar
@ Sidar Sim, abordagem interessante. Um dos métodos também seria criar vários renderizadores de trilhas e movê-los relativamente do ponto de divisão. E verifique se há colisões por meio de algum componente do colisor - mas isso é apenas uma solução alternativa.
Candid Lua _Max_
De qualquer forma, se o OP não quiser configurá-lo manualmente, os dados precisam ser dinâmicos ou estaticamente armazenados. Gostaria de saber se você poderia usar um sistema de partículas normal com colisão ativada e, em seguida, próximo entre os pontos para desenhar uma faixa quad. A contagem de partículas não precisa ser alta para isso.
Sidar
Isso não é por partícula tho? Eu acho que, dependendo do estilo que você escolher, poderá funcionar muito bem.
Sidar