Eu tenho um sistema de água baseado em grade 2D no meu jogo XNA, temos um método usando autômatos celulares para simular a queda e a propagação da água.
Exemplo de água que flui ladeira abaixo:
Cada bloco pode conter uma massa de 0 a 255 valores de líquido, armazenados em um byte. eu não usofloats
, o antigo sistema de água que eu tinha, no entanto, acrescentou complicações e teve um impacto no desempenho.
Cada telha de água se atualiza com um conjunto simples de regras:
- Se o bloco abaixo tiver espaço, mova-se o máximo possível do bloco atual para o inferior (Flow Down)
- Se os dois lados não são iguais e não são zero e ambos são aceitáveis, obtemos a soma dos 3 blocos (esquerda + corrente + direita) e dividimos por 3 deixando o restante no bloco médio (atual)
- Se a regra acima deu um número 2 como soma, devemos dividir as peças nos dois lados (1, 0, 1)
- Se a regra 2 deu 1 como soma, escolha um lado aleatório para fluir para
- Se a regra 2 falhar, devemos verificar se um lado é aceitável e o outro não. Se isso for verdade, dividimos o bloco atual ao meio pelos 2 blocos
Como posso expandir essa lógica para incluir pressão? A pressão fará com que os líquidos subam sobre as "curvas em U" e preencha os bolsos de ar.
Exemplo de como isso atualmente falha:
A água deve fluir e equalizar em cada lado da curvatura em U. Além disso, criei métodos para descobrir a que distância está o bloco de água e, portanto, quanta pressão ele está sofrendo. Agora eu preciso ser capaz de pegar esses números e aplicá-los a outras áreas para equalizar a pressão.
Respostas:
Note que eu nunca fiz isso; estas são apenas idéias que podem ajudar. Ou pode ser totalmente falso. Eu estava querendo resolver esse problema desde Terraria, mas atualmente não estou trabalhando nesse jogo.
Uma maneira que eu considerei tentar é atribuir a cada bloco de água de superfície (qualquer bloco com água e sem bloco de água acima dele) um valor inicial de pressão igual a (ou em função de) sua altura do fundo do mundo. O valor implícito da pressão de qualquer ladrilho intransitável é
MAX_PRESSURE
(por exemplo, 255), e para um ladrilho a céu aberto éMIN_PRESSURE
(0).A pressão é então espalhada para cima / baixo / lateralmente de qualquer ladrilho com pressão mais alta para ladrilhos com pressão mais baixa durante cada tick, estilo autômato celular. Eu teria que ter uma simulação real para descobrir exatamente o que igualar. A pressão de um bloco deve ser igual à pressão implícita mais a pressão "excessiva" de aproximadamente equalizada (portanto, você só precisará armazenar essa pressão excessiva, não a pressão implícita).
Se um ladrilho de superfície tiver uma pressão maior que sua pressão implícita baseada em altura e se o ladrilho acima tiver espaço livre para a água, uma pequena porção de água será movida para cima. A água flui somente se o ladrilho tiver espaço e pressão mais baixa do que o esperado.
Isso simula aproximadamente a ideia de que quanto mais profunda a água "aponte", mais pressão ela terá, embora os valores da pressão representem mais a altura do que a pressão real (já que se espera que ladrilhos mais altos tenham "pressão" mais alta). Isso faz com que a pressão seja mais ou menos como o
h
termo na equação (mas não realmente):O resultado é que, se a pressão da água for maior do que deveria para sua profundidade, ela será empurrada para cima. Isso significa que os níveis de água em sistemas fechados igualarão a pressão em todos os níveis de altura ao longo do tempo.
Não tenho certeza de como lidar ou se é necessário lidar com as "bolhas de ar" que seriam criadas (onde um ladrilho que não seja de superfície terá quantidades de água não cheias à medida que a água é empurrada para cima). Também não tenho certeza de como você evitaria que as pressões da água fossem desiguais de um lado e depois, depois de assinalar, desiguais do outro lado, para frente e para trás.
fonte
Eu criei um sistema semelhante ao que você procura em 3D. Eu tenho um pequeno vídeo demonstrando a mecânica simples aqui e uma postagem no blog aqui .
Aqui está um pequeno gif que eu fiz da mecânica da pressão atrás de uma parede invisível (tocada em alta velocidade):
Deixe-me explicar os dados envolvidos, para dar uma idéia de alguns dos recursos do sistema. No sistema atual, cada bloco de água contém o seguinte em 2 bytes:
Height
é a quantidade de água no cubo, semelhante à sua pressão, mas meu sistema tem apenas 8 níveis.Direction
é a direção em que o fluxo está indo. Ao decidir para onde a água fluirá a seguir, é mais provável que continue na direção atual. Isso também é usado para rastrear rapidamente um fluxo de volta ao seu cubo de origem, quando necessário.IsSource
indica se este cubo é um cubo de origem, o que significa que nunca fica sem água. Usado para a fonte de rios, nascentes, etc. O cubo à esquerda no gif acima é um cubo de origem, por exemplo.HasSource
indica se este cubo está conectado a um cubo de origem. Quando conectado a uma fonte, os cubos tentam extrair mais água da fonte antes de procurar outros cubos não-fonte "mais completos".Largest
diz a este cubo qual é o maior fluxo entre ele e seu cubo de origem. Isso significa que se a água estiver fluindo através de um espaço estreito, isso limitará o fluxo a este cubo.Active
é um contador. Quando esse cubo tem um fluxo ativo passando por ele, para ele ou a partir dele, o ativo é incrementado. Caso contrário, o ativo é aleatoriamente decrementado. Uma vez que o ativo atinja zero (o que significa não ativo), a quantidade de água começará a ser reduzida neste cubo. Esse tipo de ação atua como evaporação ou imersão no solo. ( Se você tem fluxo, deve ter refluxo! )FlowOut
indica se este cubo está conectado a um cubo que está na extremidade do mundo. Uma vez que um caminho para a extremidade do mundo é feito, a água tende a escolher esse caminho em detrimento de qualquer outro.Extra
é um pouco mais para uso futuro.Agora que conhecemos os dados, vamos analisar uma visão geral de alto nível do algoritmo. A idéia básica do sistema é priorizar o fluxo para baixo e para fora. Como explico no vídeo, trabalho de baixo para cima. Cada camada de água é processada um nível de cada vez no eixo y. Os cubos de cada nível são processados aleatoriamente, cada cubo tentará extrair água de sua fonte em cada iteração.
Os cubos de fluxo retiram a água de sua fonte seguindo a direção do fluxo de volta até atingirem um cubo de origem ou um cubo de fluxo sem pai. Armazenar a direção do fluxo em cada cubo facilita o acompanhamento do caminho para a origem como percorrer uma lista vinculada.
O pseudo-código para o algoritmo é o seguinte:
As regras básicas para expandir um fluxo em que (ordenadas por prioridade):
Eu sei, isso é um nível bastante alto. Mas é difícil entrar em mais detalhes sem sair do caminho em detalhes.
Este sistema funciona muito bem. Posso encher facilmente poços de água, que transbordam para continuar para fora. Posso encher túneis em forma de U, como você pode ver no gif acima. No entanto, como eu disse, o sistema está incompleto e ainda não resolvi tudo. Não trabalho no sistema de fluxo há muito tempo (decidi que não era necessário para o alfa e o colocava em espera). No entanto, os problemas com os quais eu estava lidando quando o coloquei em espera onde:
Piscinas . Ao obter uma grande piscina de água, os indicadores de filho para pai são como uma bagunça louca de qualquer cubo aleatório selecionado para fluir em qualquer direção. Como encher uma banheira com barbante bobo. Quando você deseja drenar a banheira, você deve seguir o caminho da corda boba até sua origem? Ou você deve apenas pegar o que estiver mais próximo? Portanto, em situações em que os cubos estão em uma grande piscina, eles provavelmente devem ignorar os fluxos pai e extrair o que estiver acima deles. Eu vim com algum código básico de trabalho para isso, mas nunca tive uma solução elegante com a qual eu pudesse ser feliz.
Pais múltiplos . Um fluxo filho pode ser facilmente alimentado por mais de um fluxo pai. Mas a criança com um ponteiro para um pai solteiro não permitiria isso. Isso pode ser corrigido usando bits suficientes para permitir um pouco para cada direção pai possível. E provavelmente alterando o algoritmo para selecionar aleatoriamente um caminho no caso de vários pais. Mas nunca cheguei a isso para testar e ver quais outros problemas podem expor.
fonte
Eu meio que concordo com Sean, mas faria um pouco diferente:
Um bloco gera uma pressão igual ao seu próprio peso (quanta água está nele) e a aplica nos blocos abaixo e ao lado dele. Não vejo razão para que sua posição no mundo seja relevante.
Em cada marca, mova a água de alta pressão para baixa pressão, mas mova apenas uma fração da água necessária para equalizar. A água também pode ser empurrada para cima se a pressão no bloco for muito alta para a pressão aplicada no quadrado.
Você obterá loops onde a pressão da água flui muito de uma maneira e depois precisa ser corrigida, mas como você não move toda a quantidade de água por carrapato, esses serão amortecidos. Eu acho que é realmente uma coisa boa, pois você terá efeitos de picos de água à medida que a água inunda uma área como faria na realidade.
fonte
Você pode adicionar uma regra que tenta ir para a esquerda ou direita (através das paredes) com os ladrilhos até encontrar um ponto livre, começando pelas camadas na parte inferior. Se você não conseguir encontrar, o bloco permanecerá na posição atual. Se você encontrar, as outras regras garantirão a substituição do bloco movido (se necessário).
fonte
por que você não pode definir outro tipo de bloco que atua como uma quantidade imóvel de pressão? Portanto, quando você usa o seu modo de mover normalmente os blocos de água e verificar se ele pode subir, ele não pode.
Melhor ainda seria adicionar outra definição a esses blocos que permita ao usuário inserir a quantidade de pressão por bloco, aumentando a pressão de acordo com a quantidade de blocos de água a adicionar.
fonte