Física dos tornados de jogos: como a aerodinâmica é implementada em Just Cause 4 (tráfego)

Jacques Kerner é engenheiro de software sênior da Avalanche Studios.


Como se o jogo não fosse louco o suficiente antes

1. Introdução


As séries de jogos Just Cause e Avalanche Studios são conhecidas por sua tecnologia de mundo aberto, oferecendo uma jogabilidade variada e emocionante. A versão mais recente do jogo - Just Cause 4 - adicionou desastres de vento e clima, que se tornaram uma novidade na pilha de tecnologias que aprofundam a jogabilidade. Mas condições ambientais extremas foram originalmente concebidas não apenas como uma maneira de simular um mundo mais crível. A fúria da natureza é controlada pelas forças do mal que se opõem a Rico Rodriguez. Pretendemos fazer com que o vento se manifestasse com mais clareza e condições climáticas extremas não parecessem eventos repentinos estranhos a este mundo. Este artigo apresenta as técnicas desenvolvidas por nós para implementar o vento em todas as suas manifestações do ponto de vista físico, bem como a reação de todos os objetos a ele.

[Sob gato, cerca de 120 MB de arquivos GIF]


JC4 Tempestade Tropical - Conceito Precoce (Volta)

Uma oferta que não poderíamos recusar


Como o desenvolvimento do Just Cause 3 estava quase completo, a maioria da equipe mudou para a pré-produção do Just Cause 4, e o minúsculo kernel teve que trabalhar em patches para JC3 e conteúdo para download (“DLC”). Hamish Young e eu, programador principal e designer de veículo principal, focamos no DLC do Mech Land Assault. Deveríamos ser o principal designer de física (e a mecânica do jogador) e o principal programador de física JC4, mas o DLC nos absorveu completamente no momento em que o design da nova série de franquias estava sendo criado, suas características atraentes e funções importantes foram determinadas. Desta vez, foi criado um protótipo em larga escala para testar a nova mecânica básica e a reação de Rico ao vento. Um rascunho da trama foi escrito e a editora Square Enix aprovou a direção inicial do desenvolvimento. A única coisa que restava era dominar o conceito. Mas como fazer isso sem comprometer o desempenho? Assim que começamos o projeto, atacamos o problema de duas frentes: 1. estabelecemos limites amplos para evitar os piores cenários (spoiler: não obtivemos sucesso) 2. lidamos com várias manifestações de condições climáticas extremas e, em particular, ventos de furacões, crie um sistema que forneça um comportamento realista, mas que se adapte bem ao número e à densidade desejados de objetos.


Tempestade de areia no JC4 - Early Concept Art (Volta)

Controle de danos


O gargalo na simulação em tempo real, e principalmente nos jogos em mundo aberto, é o número de corpos físicos em colisão. As principais despesas surgem devido ao cálculo de colisões de muitos corpos em movimento que colidem entre si e com um ambiente estático (relevo, edifícios). É por isso que motores físicos como Havok separam corpos ativos e inativos. Os corpos ativos são verificados quanto a colisões com outros corpos e requerem custo computacional total. Se o corpo ativo não se mover por vários quadros, o mecanismo físico o marcará como inativo, e a partir desse momento ele poderá ser completamente ignorado até que seja "acordado" pelo corpo ativo que se aproxima. Esses corpos inativos geralmente repousam no chão e as verificações de colisão entre eles e a terra não são mais realizadas. É óbvio que a presença onipresente de vento no mundo aberto se tornará uma ameaça para esse sistema, por isso era importante garantir que o vento permanecesse moderado e puramente cosmético quando exposto a grandes áreas, ou forte e ativando fisicamente quando ocorrendo em pequenos volumes e locais onde a quantidade corpos potencialmente ativos são pequenos. Falei sobre essas limitações ao nosso departamento de design nos estágios iniciais do desenvolvimento, para que não ficasse preso. A princípio, parecia que os projetistas haviam escutado e decidido limitar as trajetórias de eventos climáticos extremos a rotas de destruição predeterminadas, dentro das quais deveriam ser aplicadas regras mais estritas para a construção do mundo. E eles observaram algumas dessas restrições. Mas a tentação era grande demais, e a pressão dos colegas que queriam tornar o jogo o melhor possível era insuperável. O que eu estava pensando? Imediatamente depois disso, os projetistas criaram um tornado de vários quilômetros de altura, passando pela capital do país - a parte mais densamente povoada da ilha. Esse furacão ousadamente não apenas metade da capital, mas também alguns dos programadores inocentes que não aderiram a esse recurso básico do jogo com muita rigor.


Nevasca em JC4 - Early Concept Art (Volta)

Abordagem geral


Em geral, a física climática extrema no JC4 exigia os seguintes ingredientes:

  • Modelo de arrasto aerodinâmico aplicado a todos os objetos dinâmicos do mundo, levando em consideração sua forma e tamanho
  • Fontes eólicas que correspondem em forma e distribuição a condições climáticas extremas (tempestade, tornado), mas também fornecem padrões de propagação eólica em todo o mundo
  • Otimização dos sistemas acima para que o jogo caiba no orçamento de cargas de computação

Esses três problemas precisavam ser resolvidos simultaneamente e o mais rápido possível. Muitas decisões de projeto dependiam da possibilidade de implementar todos os três aspectos e, durante os primeiros meses, nos sentimos insatisfeitos com os designers que esperavam pacientemente pela oportunidade de brincar com o sistema em larga escala.

Primeiro, criamos um modelo de arrasto aerodinâmico. Após sua conclusão, cada objeto aprendeu subitamente a responder de maneira bastante realista ao vento. Por exemplo, se algum objeto cai de um penhasco ou é jogado por uma explosão, todos os objetos diminuem a velocidade e giram realisticamente no ar, o que já valeu o esforço. Mas, na ausência de fontes eólicas, era difícil dizer como elas se comportariam em condições de vento extremo. Embora tivéssemos orgulho de nós mesmos e o fato de que a experiência e a intuição nos ajudassem a encontrar rapidamente uma abordagem para os dois primeiros problemas que atendiam aos requisitos de desempenho, decidimos que tivemos muita sorte quando a primeira versão do tornado começou a funcionar exatamente como esperávamos. Ela levantou todos os objetos dinâmicos e os torceu de maneira bastante realista, sem ocupar todo o tempo do processador. O diretor do JC4, Francesco Antollini, que muitas vezes perguntava sobre a situação do tornado, aparecia próximo ao meu local de trabalho e expressou seu alívio: acho que perdi a oportunidade de envergonhá-lo por duvidar de mim; mas na verdade eu me senti da mesma maneira.

Do ponto de vista físico, o JC4 foi um avanço em comparação com o JC3: é um mundo mais denso, cheio, vibrante e bonito, que é muito mais interessante de destruir. E tudo isso foi possível nas mesmas plataformas de destino (XBox One e PlayStation 4). Não surpreendentemente, a otimização levou muito tempo. Nosso diretor técnico Dave Barrett aproveitou imediatamente nossa vantagem: tínhamos os indicadores de desempenho necessários para o JC3; portanto, o chefe do departamento de desenvolvimento de motores, Daniel Pieroni, recebeu a tarefa de determinar quais custos máximos de desempenho e memória podem ser usados ​​para cada aspecto técnico. Recebemos uma física generosa de 8,5 ms em 4 threads de 33 ms de tempo do processador, alocados para criar um quadro com uma frequência de 30 vezes por segundo. A Havok ocupou grande parte desse orçamento para reconhecer possíveis colisões entre objetos, calcular contatos, resolver restrições e “integrar” o movimento de todos os corpos ativos para determinar sua posição após 33 ms de tempo simulado. Calculei aproximadamente que nosso orçamento para todos os cálculos relacionados à aerodinâmica e ao vento deve ser de aproximadamente 1 ms em 4 threads. Basicamente, conseguimos manter o orçamento, embora seja provavelmente melhor perguntar a Daniel sobre isso. A solução apresentada abaixo é rápida o suficiente para tempo real ao calcular várias centenas de objetos em CPUs modernas. É claro que, como muito mais, pode ser otimizado ainda mais, adaptando-o para trabalhar na GPU.

No restante do artigo, falarei brevemente sobre como resolvemos cada um dos problemas e, para os interessados, apresentarei nos detalhes das aplicações e nos cálculos matemáticos.


Vento em JC4 - Arte conceitual inicial (Ironklad Studios)

Modelo de resistência


Nosso objetivo na criação do modelo de resistência foi avaliar as forças de resistência e torques, que se assemelhariam aproximadamente às aplicadas a um corpo de forma arbitrária no mundo real. Observe como éramos ambiciosos aqui - ninguém exigia um realismo enorme. No entanto, tínhamos alguns requisitos. Em primeiro lugar, as forças de resistência devem neutralizar o movimento do corpo. Em segundo lugar, a força deve ser calculada a partir da equação básica do arrasto aerodinâmico, ou seja, variar de acordo com a área de superfície da figura e o quadrado da velocidade de movimento no ar. Finalmente, de alguma forma, a forma e o tamanho do corpo devem ser levados em consideração para que objetos com a mesma área de superfície, mas com formas muito diferentes, se comportem de maneira diferente. Fiquei tentado a simplesmente aproximar a forma de cada corpo de acordo com o paralelogramo que a descreve, mas achei que era necessário algo mais, porque era óbvio - a forma de muitos objetos pode diferir significativamente de uma caixa ou mesmo de um pequeno número de caixas. Minha experiência anterior trabalhando com sistemas que aproximam aproximadamente o volume de objetos 3D usando voxels ou esferas me fez procurar uma solução melhor. Me ofereceram a seleção manual dos coeficientes de resistência para cada objeto, mas queria evitar mais um estágio de ajuste manual em um pipeline já complicado. Portanto, continuei a procurar por uma maneira automatizada. E estou feliz por não ter desistido.

A maneira mais simples de avaliar a pressão do vento em cada superfície da forma de colisão de nossos objetos é levar em conta apenas o vento que entra, ignorando toda a circulação de ar ao redor dos objetos e a viscosidade do ar ao seu redor. Como se a resistência do ar consistisse apenas em avançar a massa de ar na frente do objeto ou sua absorção, por outro lado. Um protótipo inicial mostrou que esse método era bom o suficiente para nossos propósitos.

O "único" problema era que a soma das contribuições de milhares de triângulos para cada figura individual durante a execução do jogo era uma ordem de magnitude superior ao nível de custo exigido. Em cenas "pesadas", é possível alcançar facilmente mais de 100.000 rostos. Seria ótimo se pudéssemos calcular as forças antecipadamente. O primeiro método de "força bruta" consistiu em um cálculo preliminar das forças e torques aerodinâmicos que surgem quando um corpo se move em velocidades diferentes em várias direções no ar parado quando gira em diferentes velocidades angulares em diferentes direções. Então, nós temos uma tabela de pesquisa na qual podemos obter valores durante a execução do jogo. Isso pode ser imaginado como um túnel de vento virtual, incluído durante a compilação de nosso conteúdo, no qual colocamos todos os objetos por sua vez e os expomos a diferentes velocidades de movimento ou rotação, colocando cada objeto em ângulos diferentes e cada vez medindo forças e torques.

Como será esta tabela? Os valores conhecidos em tempo de execução são a velocidade linear e angular do corpo, portanto S=5, ou seja, apenas cerca de 300 KB por objeto. E ainda assim, isso é pelo menos uma ordem de magnitude maior que o consumo de memória permitido.

Para reduzir significativamente o número de amostras, usei duas técnicas. Primeiramente, converti o cálculo para uma forma linear em uma soma de contribuições baseadas no cálculo preliminar de forças e torques separadamente para apenas um corpo em movimento e apenas em rotação. Em segundo lugar, aproximei isso em uma fórmula analítica para extrapolar valores em velocidades arbitrárias com base em uma reação pré-calculada e apenas a uma velocidade de 1 m / se uma velocidade de 1 rad / s. Isso reduziu a tabela pré-calculada para apenas 7 KB por objeto. Infelizmente, para isso, tive que realizar várias aproximações. A razão para o problema é que, mesmo com um modelo muito simples de resistência, somos confrontados com o fato de que as forças aplicadas ao corpo, isto é, rotação e deslocamento, não são apenas a soma das forças aplicadas separadamente para rotação e deslocamento. Mesmo que eu tivesse que adicionar aproximações, consegui salvar alguns dos termos causados ​​por uma combinação de rotação ao mover, semelhante ao componente Coriolis. A conclusão da redação pode ser encontrada no Apêndice 1. Como resultado, em vez de armazenar diretamente as forças e torques de cada amostra, armazenamos o vetor de membros  mathcalA( hatu)usado nas fórmulas lineares. Cada amostra corresponde a uma velocidade linear unitária relativa ao ar a 1 m / s ou a uma velocidade angular unitária relativa ao ar a 1 rad / s, em uma determinada direção. Os componentes desse vetor são usados ​​na fórmula, que também leva em consideração as verdadeiras velocidades lineares e angulares do objeto em relação ao ar ao calcular as forças e torques que precisam ser aplicados ao objeto. O Apêndice 1 detalha como isso acontece.

Para armazenar a tabela de pesquisa, usamos um mapa de cubo, ou seja, um cubo cujas faces estão divididas em grades de tamanho, digamos células 3x3, como um cubo de Rubik. Armazenamos valores nos cantos das células, para que cada face possa armazenar valores 4x4. Em cada canto da célula, calculamos o vetor que vai do centro do cubo para o canto e normalizamos esse vetor para obter a direção da unidade. Usamos esse vetor de direção da unidade como  mathcalA( hatu)para o canto da célula. Como resultado, obtemos 6 tabelas (uma por aresta) de 16 elementos com 19 valores flutuantes cada, com um pouco mais de 7 KB, e isso é muito conveniente. Existem maneiras de reduzir ainda mais o número de amostras, mas com esse método é muito fácil interpolar a direção da velocidade arbitrária: como ela sempre cai na célula, sempre é possível usar os valores nos quatro cantos dessa célula para executar a interpolação bilinear para essa direção específica. A técnica de mapas cúbicos e interpolação bilinear é muito amplamente usada na renderização, portanto, é muito fácil encontrar informações sobre ela e o código correspondente.


Figura 1. Mapa cúbico de velocidades unitárias em relação ao ar. Nesta figura, destaquei em vermelho uma célula separada de um mapa cúbico para ilustrar o princípio. Tendo velocidade linear  hat omegaem relação ao ar, amostramos novamente o mesmo mapa cúbico para encontrar a célula azul e usamos os 4 coeficientes armazenados em seus cantos. Em seguida, misturamos todos os valores de acordo com a fórmula do apêndice 1.

Embora isso tenha funcionado na maioria dos casos, tivemos dificuldades em alterar o centro de massa ou a escala de objetos em tempo de execução. Nossas soluções para esses problemas podem ser encontradas nos apêndices 2 e 3.

Ao desenvolver esse modelo, não tinha certeza do número de amostras suficientes para obter um bom comportamento dos objetos que se moviam e giravam no ar; portanto, não sabia exatamente se havia memória suficiente no disco em tempo de execução. Paralelamente, começamos a criar novos ativos de destruição para a Havok, com centenas de fragmentos de vários tamanhos, às vezes várias centenas de partes para um ativo. Percebi que alguns desses ativos exigiriam a mesma quantidade de memória, como muitos outros ativos no total, se cada fragmento tivesse seu próprio mapa cúbico. O modelo geral transmitia bem algumas características únicas de objetos complexos, mas era demais para pequenos fragmentos. Além disso, eu não tinha certeza do custo no tempo de execução, e poder retornar a um modelo de custo mais baixo era uma boa maneira de reduzir o risco. Portanto, encontrei um texto muito compacto (fazendo aproximações generosas) baseado apenas em caixas delimitadoras de objetos. Pode ser encontrado no apêndice 4.

Volume de vento


Introduzimos o conceito de volumes de vento, apoiando a idéia de que um vento forte e ativador fisicamente só pode ocorrer em espaços finitos e começamos a classificá-los de acordo com a forma e a distribuição do vento. Dividimos os volumes de vento nos seguintes tipos principais:

  • Volume de vento cilíndrico usado para nevascas, tempestades de areia e tempestades tropicais
  • Volume de vento tornado - um conjunto vertical de cilindros centralizados em relação a uma ranhura vertical que se deforma com o tempo
  • Túneis de vento compostos por várias figuras conectadas em forma de cápsula (cápsulas com raios diferentes em extremidades diferentes), formando algo como um "buraco de minhoca"

Essa restrição do vento pelo volume final foi criticada várias vezes e, no último momento, quase o abandonaram, substituindo-o por um "vento topográfico". O vento em todos os lugares deveria corresponder ao movimento das nuvens na atmosfera superior e fornecer ao jogador um fluxo ascendente de ar ao encontrar uma inclinação crescente do terreno.Nós o implementamos, mas a falta de uma exibição gráfica clara do vento nos forçou a abandonar essa função. Os caras do departamento de renderização já tinham muitas tarefas para vender nuvens, rios, cachoeiras, atualizando para o DirectX 12 e muito mais.

Em retrospecto, as tempestades se tornaram os volumes mais simples de vento, tanto em termos de forma (cilindro) quanto na distribuição do vento (quase unidirecional para uma tempestade de areia ou girando em torno de um eixo central para uma nevasca). Por outro lado, tornados e túneis de vento tornaram-se mais complexos e merecem uma explicação mais detalhada.

Tornado


Tínhamos uma idéia clara e simples de como os objetos deveriam girar em torno de um tornado, mas não concordamos com a distribuição dos ventos necessários para alcançar esse resultado. Em vez de um debate sem fim, fizemos os vários componentes do campo eólico personalizáveis ​​em tempo de execução, para que Hamish pudesse experimentá-los e encontrar rapidamente uma opção de trabalho. Um tornado é essencialmente uma pilha de cilindros horizontais cujos centros estão localizados na ranhura central, que se deforma ao longo do tempo. Nesses cilindros horizontais, decompusemos o campo de vento de tornado em coordenadas cilíndricas com um componente tangente, um componente radial e um componente vertical. Cada componente pode ser ajustado usando curvas variáveis.


2. — . , , . . , .

Assim como em muitos outros recursos, e como costuma acontecer em nosso setor, Hamish e eu frequentemente mudamos da configuração para o código até o resultado ser bom o suficiente. Hamish ajustou as curvas ao seu gosto, jogando dezenas de contêineres de 12 metros ao longo do tornado para ver seu comportamento. Ele usou a velocidade máxima registrada do vento (cerca de 230 mph) para o tornado. Depois de algum tempo, Hamish descobriu que sua configuração era complicada porque era necessário fazer com que o vento correspondesse a todos os lugares a um simples movimento rotacional, mas ao mesmo tempo atraía objetos para o centro. A velocidade angular já fazia parte do campo, não apenas empurrando, mas também girando objetos em um tornado. Isso garantiu que, por exemplo, objetos presos no centro girassem no lugar, como queríamos.Portanto, Hamish pediu para começar com o vento, em todos os lugares correspondentes a essa velocidade angular no centro, ou seja, comece com o campo de velocidade de um campo de rotação ideal, no qual um campo de vento personalizado pode ser adicionado. Após essa última configuração, as curvas começaram a consistir nos seguintes componentes:

  • - fator do componente radial (movimento radial)R(d)
  • é o fator do componente tangente (movimento radial)T(d)
  • - fator do componente vertical (movimento radial)V(d)
  • - multiplicador de componentes horizontal (movimento vertical)H(h)
  • - fator do componente vertical (movimento vertical)Φ(h)

Onde é a altura acima do fundo do tornado, eh é a distância do centro. A velocidade do vento tornado é dada pela equação:d

V(d,h)=[VtT(d)H(h)+Ωd]t^+VrR(d)H(h)r^+VuV(d)Φ(h)up^


Quando os objetos são acelerados para a velocidade tangente do tornado a uma determinada distância, o componente tangente das forças de resistência do ar é essencialmente zero, porque o ar e o objeto se movem na tangente em uníssono. O restante da velocidade do vento atrai o objeto para dentro, pois o planeta atrai satélites para dobrar sua trajetória, de modo que ele permaneça em órbita; Além disso, os objetos são empurrados para cima para alcançar maiores alturas. Empurrar objetos para cima é a otimização de desempenho mais importante, evitando cálculos dispendiosos dos contatos de centenas de objetos e da Terra. Também ajuda que, com a altura do tornado, ele aumenta o raio distribuindo objetos espacialmente para reduzir a probabilidade de colisão e cálculo de contato. Um tornado no mundo real é formado pela troca de ar quando uma camada de ar frio está no topo de uma camada de ar quente.O ar quente sobe como um redemoinho em torno do centro de um tornado, que é o ar frio descendo. Portanto, em princípio, no centro do funil, a velocidade do vento deve ser fortemente direcionada para baixo. Como você pode ver, isso não é implementado conosco para reduzir a probabilidade de empurrar objetos para o chão e puxá-los para um tornado; caso contrário, isso aumentaria o caro cálculo de colisões.


3. (), (). Ωdo campo rotativo do vento com o qual começamos, a fim de entender melhor se é necessário um campo adicional para o bom funcionamento do tornado. Na versão à direita, um campo rotativo é adicionado. À distância, vemos um vento muito rápido, em qualquer ponto direcionado a cerca de 45 graus do raio. A seção vertical à esquerda mostra quanto o furacão deve puxar para o centro. Depois, há um pequeno intervalo de transição no qual quase não há vento, e atrás dele existe um forte campo rotativo no centro. A força do campo de vento é mostrada em cores, quanto mais vermelha, mais forte.


Figura 4. Linhas dos caminhos ao redor do campo de vento de tornado - a ranhura central da linha.

Um tornado direto parece chato. Para deformar, usamos 2 splines cúbicos conectados pelas extremidades, dos quais 3 pontos de controle estão em órbita e se movem em velocidades diferentes em torno de uma linha vertical imaginária. O perfil do tornado resultante muda com o tempo, passando lentamente da forma S para a forma C e vice-versa. O Apêndice 5 detalha como criamos splines. Curiosamente, o tornado no modo deformado não funcionou tão bem. Objetos circulando na órbita de um tornado direto agora se dispersavam assim que a divisão central do tornado se afastou deles e, infelizmente, caiu no chão. Para corrigir esse problema, adicionamos outro termo à velocidade devido à deformação do próprio tornado. No final, essa deformação deve surgir principalmente devido ao movimento do ar, e nos parecia natural adicionar esse componente. Isso resolveu o problema lindamente, fornecendo linhas de caminho muito semelhantes à figura abaixo.


Figura 5. Linhas de caminhos ao redor do campo de vento de tornado - a ranhura central se dobra ao longo do tempo


Figura 6. Um exemplo de um tornado em um jogo, ele destrói e absorve tudo em seu caminho. Este é o Havok Visual Debugger (VDB), uma ferramenta extremamente útil que nos permite observar elementos simulados.

Túneis de vento


Os túneis de vento foram projetados para fornecer controle físico do vento em partes do mundo, como desfiladeiros e cavernas, bem como colunas de fumaça na frente de grandes ventiladores industriais e canhões de vento. Eles podem ser usados ​​para guiar o jogador em determinados espaços ou missões. Nossos primeiros protótipos usavam um cilindro tradicional com um campo vetorial uniforme de velocidade constante paralelo ao eixo de rotação do cilindro. Colocar todos esses cilindros em cena foi uma tarefa minuciosa; portanto, usamos uma spline cúbica de Bezier em torno da qual um círculo de raio variável é esticado. Essa forma é aproximada dividindo o spline central em figuras em forma de cápsula, ou seja, em cápsulas com raios diferentes nas bordas. O campo de velocidade neles também sofreu algumas mudanças. Inicialmente, o vento no túnel era tangente à ranhura central. Definimos as velocidades do vento em cada ponto de controle do spline e elas foram interpoladas de um ponto de controle para outro ao longo de um segmento do spline. No entanto, Joshua Espinosa (o designer que trabalhou nos túneis de vento e os movimentos do jogador neles) logo descobriu que precisava de mais dois componentes. Um componente é o upstream, dando ao jogador uma elevação artificial adicional, sempre apontando para cima. Chamamos o outro componente de "retração", ele empurra o jogador em direção ao spline central, mas acima de tudo nas bordas do túnel, caindo para o centro a zero. Finalmente, o limite de redução na parte externa do túnel garantiu uma transição suave da parte externa para a parte externa do túnel. A espessura desse limite é especificada como uma porcentagem do raio; atua ao longo de todo o túnel de vento.


Figura 7. Túnel de vento dividido em uma sequência de figuras em forma de cápsula sobrepostas. A separação ocorre quando a direção do spline muda ou quando não é mais possível executar a interpolação longitudinal linear do raio (A) ou da velocidade do vento (B). Calculamos a segunda derivada dessas variáveis ​​ao longo do spline para determinar quando dividir.


Figura 8. Túneis de vento editados no Apex Engine. O túnel de vento é dividido em cápsulas da maneira correta, dependendo do spline, alterações no raio e velocidade do vento ao longo do spline. Conforme declarado na próxima seção, você também pode ver a separação espacial que classifica as figuras em forma de cápsula em curvas de Morton. Esses cálculos são rápidos o suficiente para atualizações em tempo real; Além disso, você pode interagir rapidamente com o túnel de vento para testá-lo. A ferramenta de edição foi feita retrabalhando algumas partes da ferramenta de criação do rio com a adição da renderização de depuração.


Figura 9. Um exemplo de um longo túnel de vento, permitindo que um jogador se mova rio acima para uma montanha. A corrente do rio empurra Rick para o mar, e o vento pode devolvê-lo rapidamente dentro da terra, no topo do mesmo rio. O departamento de mecânica do jogador notou que os ventos laterais de Rico eram muito fortes, então a principal influência do vento é acelerar o jogador (com um vento de cauda) ou frear (vento de frente) e dar-lhe uma elevação adicional (componente a montante).


Figura 10. Rico em macacão no mesmo túnel de vento. Aqui o jogador não controlou a direção. A princípio, Rico perde altitude até atingir a parte externa do túnel de vento, o que diminui gradualmente seu declínio. Então a parte central cria um fluxo ascendente suficiente para superar a gravidade e rapidamente a acelera no cânion. O código JC3 Wingsuit para interação adequada com o vento foi desenvolvido por Joshua Espinosa, Hamish Young e Rickard Granfeld.

Otimização de solicitação de vento


Com o potencial de centenas de corpos ativos, o cálculo do vento local em sua posição deve ser um processo rápido. Começamos a otimizar as consultas calculando o AABB (caixa delimitadora alinhada ao eixo) para cada volume de vento e armazenando-as em uma matriz compactada. Isso permite a rejeição de força bruta, porque é possível verificar rapidamente a posição em relação a cada AABB sem falhas de cache. Em cada quadro AABB, os volumes de vento na matriz são atualizados para se preparar para a solicitação.

Se a posição estiver dentro do volume da tempestade, calcularemos diretamente o efeito do vento usando uma forma cilíndrica. Para um tornado, você precisa fazer mais cálculos, porque na parte superior é muito maior do que na parte inferior, enquanto a maior densidade de objetos fica na parte inferior. A solução AABB não corta muito bem os objetos perto do tornado, por isso usamos cilindros empilhados uns sobre os outros. Eles transmitem a forma do furacão muito mais perto e são usados ​​para cortar objetos antes de calcular o vento. A otimização mais recente do tornado foi evitar projetar a posição da consulta no spline central. Isso foi possível porque, embora o tornado da spline seja pontos interpolados em 3D e, portanto, seja uma curva paramétrica em 3D, na prática os pontos de controle interpolados por ele são distribuídos uniformemente e sequencialmente ao longo da vertical, o que nos dá uma curva bastante simples. Em geral, projetar um ponto em um spline 3D ou encontrar um ponto nesse spline com a mesma altura requer uma pesquisa ao longo desse spline. Mas, em vez disso, decidimos alterar a maneira como a spline é amostrada usando a altura acima da parte inferior do tornado como um parâmetro de amostragem para a própria spline. Isso altera levemente a forma do spline, mas o spline ainda passa pelos pontos de interpolação. Mas, ao mesmo tempo, para encontrar um ponto no spline na mesma altura, tudo se resume a transferir a altura da posição da solicitação para ele, seguido pela fixação da altura do ponto na altura da solicitação. Para pontos de controle localizados de maneira desigual ao longo do eixo vertical, a diferença é perceptível, mas quando eles estão localizados uniformemente, é quase invisível. Também poderíamos mudar a maneira como a curva central é construída usando a função altura cúbica em vez da curva paramétrica, mas, como muitas outras otimizações, essa apareceu tarde demais.

Os túneis de vento também exigiam otimização, porque a projeção de cada ponto de consulta no spline central de cada túnel de vento nem sequer era considerada. Felizmente, conseguimos aproveitar a tecnologia implementada simultaneamente para os rios pela Engin Silasun: um banco de dados espacial esparso. Dividimos o espaço em 32 O( log(n)). Se a célula não estiver no banco de dados espacial, não haverá vento no ponto de consulta causado pelos túneis de vento. Se a célula for encontrada, você precisará executar uma verificação mais cara. Além disso, essa verificação deve ser bastante rápida e, portanto, cortamos os túneis de vento em figuras em forma de cápsula. No caso geral, as cápsulas são utilizadas porque é possível calcular a distância ao seu segmento central a baixo custo. Figuras em forma de cápsula são um pouco mais caras, mas ainda mais rápidas do que encontrar a menor distância para um spline cúbico. Outra propriedade de um sistema espacialmente holístico de túneis de vento é que podemos lembrar o índice de células de um determinado objeto localizado no quadro anterior e usá-lo como uma dica para acelerar a consulta no quadro seguinte, porque provavelmente já está nesse ou nos vizinhos. celular.


Figura 11. Figuras em forma de cápsula de túneis de vento são inseridas em um banco de dados espacial esparso, de acordo com as células que cruzam. O resultado é uma matriz de 9 células, cada uma das quais possui apenas algumas figuras em forma de cápsula.

Em conclusão


A combinação das técnicas que usamos provou ser altamente eficaz. Por si só, essas técnicas são usadas ativamente na programação. A maior parte do trabalho está na seleção e na garantia da colaboração adequada para resolver o problema atual. Isso é típico na programação de videogames: está cheio de pequenos problemas que exigem soluções simples, porém confiáveis.

O modelo de resistência ao ar é extremamente simplificado e não reflete algumas propriedades importantes do fluxo real de fluido. Por exemplo, uma folha em um ângulo de 45 graus gerará mais elevação do que em nosso modelo aproximado. Mas o bom disso é que forças e torques não são calculados em tempo de execução, e uma solução com um mapa cúbico evita uma simulação mais detalhada. De fato, verificamos os cálculos de forças e torques gerados em todo o mapa cúbico do objeto de teste (um carro antigo do JC4) usando o software de dinâmica de fluidos computacional de código aberto (OpenFOAM). A comparação mostrou que não estávamos muito distantes dos resultados corretos, mas a “forma” dos mapas cúbicos era diferente, e espero que seja diferente ainda mais para objetos em forma de asa. No entanto, demorou várias horas para calcular o software; portanto, é óbvio que essa solução não é adequada para uso em todos os objetos. Nosso método levou menos de um segundo por objeto. Mas acho que seria muito interessante para jogos baseados em física ter coeficientes aerodinâmicos rápidos, mas realistas, para todos os objetos. Suponha que Rico possa coletar objetos em enormes bumerangues. Brinquei brevemente com essa idéia, observando a rápida queda e rotação de sementes de helicóptero de bordo, e criei um modelo no Maya para ver o que acontece. O resultado acabou sendo melhor do que o esperado, e o objeto realmente girou lentamente quando caiu. Mas parece-me que, para uma rotação mais rápida, será necessário melhorar significativamente o modelo de resistência do ar. Como costuma acontecer, simplesmente não tivemos tempo suficiente.


Figura 12. Esquerda: todas as orientações de um carro a óleo antigo de Just Cause 4, colocado em um túnel de vento virtual para preencher um mapa cúbico com uma grade 5x5 em cada face. Direita: o Paraview visualizou os resultados para duas orientações - em um ângulo acima (acima) e atrás (abaixo). Vermelho indica alta pressão, azul indica baixa pressão, linhas indicam fluxos.

Tornados e tempestades funcionaram de maneira muito eficaz. A deformação do furacão ainda pode ser melhorada. Se passássemos mais tempo nisso, provavelmente conseguiríamos tornar tornados aquáticos (é claro, com tubarões em vez de vacas), banheiras de hidromassagem ou tornados ardentes. Tivemos outra idéia - desenvolver um canhão que cria um mini-tornado.

Túneis de vento foram usados ​​em diferentes partes do mundo para controlar o vento em uma ilha, como desfiladeiros, missões, em torno de algumas bases militares. Eles ajudam o jogador a atravessar as vastas extensões do nosso mundo aberto com ventos ascendentes estrategicamente localizados. Mas acho que seria melhor usar a ideia do vento topográfico, que não tivemos tempo de implementar. Seria mais racional implementá-lo primeiro, isso nos daria um vento generalizado e sistemático que quase não requer ajuste, e os túneis de vento forneceriam controle local sobre os desvios dele.

E, finalmente, foi uma idéia lógica usar barreiras contra o vento como mecânica de jogo. Implementamos um protótipo jogável de obstáculos, que foi mostrado dentro da equipe, mas o abandonamos devido à falta de tempo para a implementação correta. Sua física era apenas parte do custo total desse recurso, que deveria ser suportado pela maioria da equipe de desenvolvimento.

Mas nada pode me distrair do quanto estou orgulhoso do que conseguimos fazer no JC4. Nossa visualização de condições climáticas extremas e, em particular, de tornados, combinada com a capacidade de Rico de se mover no espaço, tornou possível criar uma jogabilidade absolutamente única e excepcional. Espero que você tenha gostado de jogar JC4 tanto quanto nós quando o criamos.


Apêndice 1: Modelo de resistência ao ar


Nós vemos o corpo  mathcalBdefinido pela malha Ti,i in[0,N1].


Figura 13. O corpo i em[0,N1].

Considere um triângulo  vecVi:

 vecVi= vecV+ vec Omega times vecOCi=Vi  hatvi



Figura 14. Triângulo e velocidade em seu centro.

Agora precisamos escrever força elementar  vecFiagindo neste triângulo. É aqui que precisamos levar em conta nossos requisitos:

 vecFi=Ai Vi ( vecVi cdot hatni)  hatni


Isso está em conformidade com os requisitos, porque a força quase sempre é direcionada para o lado contra a direção do movimento, proporcional à área da superfície do triângulo e proporcional ao quadrado da velocidade do triângulo em relação ao ar. Para que a força atue em todo o corpo, basta resumir essas forças:

 vecF= sum vecFi= sumAi Vi ( vecVi cdot hatni)  hatni


Enquanto nos esforçamos para reduzir tudo para 1 m / se 1 rad / s, vamos reescrever com  hat omega:

 vecF= sumAi  |V hatv+ Omega hat omega times vecOCi |(V hatv+ Omega hat omega times vecOCi) cdot hatni  hatni


E agora a primeira grande aproximação:

 |V hatv+ Omega hat omega times vecOCi | aproximadoV+ Omega | hat omega times vecOCi |


Isso é equivalente a aproximar o comprimento da hipotenusa de um triângulo pela soma dos comprimentos de seus dois lados. Essa aproximação também é conhecida como "distância do bloco da cidade" (distância de Manhattan). Quando eu estava trabalhando nessa tarefa, estava em Manhattan, então seria tolice não usá-la. Portanto, temos o seguinte:

 vecF= sumAi (V+ Omega | hat omega times vecOCi |)(V hatv+ Omega hat omega times vecOCi) cdot hatni  hatni


Nós decompomos:

 vecF=V2 sumAi hatv cdot hatni hatni+ Omega2 sumAi | hat omega times vecOCi |( hat omega times vecOCi) cdot hatni hatni+V Omega sumAi( hat omega times vecOCi) cdot hatni hatni+ OmegaV sumAi | hat omega times vecOCi |( hatv cdot hatni) hatni


A princípio, isso pode parecer intimidador. Mas então você percebe que é isso que estávamos procurando. Observe que apenas valores são encontrados  | hat omega times vecOCi |com seu valor médio entre todos os triângulos:

 | hat omega times vecOCi | Avgmédio( | hat omega times vecOCi |)i in[0,N1]


Vamos denotá-lo por  hat omega. Agora podemos finalmente escrever:

 vecF=V(V+ Omega  overlinevr( hat omega)) vecFt( hatv)+ Omega2 vecFr( hat omega)+V Omega vecFc( hat omega)


onde:

\ begin {align *} \ vec {F} _t (\ hat {v}) & = \ sum {-A_i \ hat {v} \ cdot \ hat {n} _i \ \ hat {n} _i} \\ \ vec {F} _r (\ hat {\ omega}) & = \ sum {-A_i \ | \ hat {\ omega} \ times \ vec {OC_i} \ | (\ hat {\ omega} \ times \ vec { OC_i}) \ cdot \ hat {n} _i \ \ hat {n} _i} \\ \ vec {F} _c (\ hat {\ omega}) & = \ sum {-A_i (\ hat {\ omega} \ vezes \ vec {OC_i}) \ cdot \ hat {n} _i \ \ hat {n} _i} \\ \ overline {v_r} (\ hat {\ omega}) & = {1 \ over N} \ sum {\ | \ hat {\ omega} \ times \ vec {OC_i} \ | } \ end {align *}


Momento dessas forças em relação ao ponto Oopode ser obtido de maneira semelhante. E, como resultado, obtemos o seguinte:

 left. vecM right|O( hatv, hatw)=V(V+ Omega  overlinevr( hat omega)) vecMt( hatv)+ Omega2 vMr( hat omega)+V Omega vecMc( hat omega)


onde:

\ begin {align *} \ vec {M} _t (\ hat {v}) & = \ sum {-A_i \ hat {v} \ cdot \ hat {n} _i \ (\ vec {OC_i} \ times \ hat {n} _i)} \\ \ vec {M} _r (\ hat {\ omega}) & = \ sum {-A_i \ | \ hat {\ omega} \ times \ vec {OC_i} \ | (\ hat {\ omega} \ times \ vec {OC_i}) \ cdot \ hat {n} _i \ (\ vec {OC_i} \ times \ hat {n} _i)} \\ \ vec {M} _c (\ hat {\ omega}) & = \ sum {-A_i (\ hat {\ omega} \ times \ vec {OC_i}) \ cdot \ hat {n} _i \ (\ vec {OC_i} \ times \ hat {n} _i)} \ end {align *}


Dadas as definições acima, agora podemos formar um vetor  mathcalA( hatu):

 mathcalA( hatu)= beginbmatrix vecFt( hatu) vecFr( hatu) vecFc( hatu) vecMt( hatu) vecMr( hatu) vecMc( hatu) overlinevr( hatu) endbmatrix


Este é um vetor de 19 valores e nos referimos à tabela de amostra  hatw, mas podemos encontrar valores bastante próximos e, em seguida, usá-los diretamente ou usá-los para interpolar a saída. E, nesta fase, o mapa cúbico descrito no artigo entra em jogo.

Apêndice 2: centro de massa


Você pode perceber que os valores mostrados acima são calculados em relação a um ponto arbitrário Pi. A força total que atua no corpo é simplesmente a soma de todas as forças:

 vecF= sum vecfi



Momentos Osão definidos como:

 left. vecmi right|O= vecOPi times vecfi


e o momento total dessas forças em relação ao ponto Oé igual a:

M|O=mi|O=OPi×fi


Agora é apenas uma definição matemática, uma fórmula que não pode ser usada diretamente para alterar o movimento do corpo, porque o centro de gravidade nesta fórmula pelo teorema de Schal é igual a:G

M|O=(OG+GPi)×fi=OG×fi+GPi×fi


Reconhecemos o momento das forças em relação a , portanto:F

M|O=OG×F+M|G


E é muito útil para nós se calcularmos a força total inalterado e aplicou o seguinte torque ao centro de massa:F

M|G=M|O+GO×F


No começo, eu decidi apenas tomar a origem do corpo como um ponto no centro do paralelogramo delimitador do corpo, que ainda deve estar próximo ao centro de massa e localizar a amostragem de vento no local mais lógico em relação ao corpo.O

Apêndice 3: Dimensionamento


Outra falha apareceu quando balões foram introduzidos no jogo. Essas esferas são corpos esféricos, cujo tamanho pode variar com um fator de 5 ou 10. Como no caso do centro de massa, era muito caro recalcular as propriedades aerodinâmicas em tempo de execução. É possível derivar uma fórmula analítica?


Figura 15 Corpo OsCsi=s OCi

Considere mais uma vez o corpo , mas agora para um objeto em escala. Temos:F

Fs=V2Aiv^n^in^i+Ω2Aiω^×OCsi(ω^×OCsi)n^in^i+VΩAi(ω^×OCsi)n^in^i+ΩVAiω^×OCsi(v^n^i)n^i


Usando as mesmas aproximações da primeira vez e substituindo , obtemos:s OCi

Fs=V(Vs2+Ωs3 vr¯(ω^))Ft(v^)+Ω2s4Fr(ω^)+VΩs3Fc(ω^)


Para o centro de massa do corpo escamado Gs.

Apêndice 4: Modelo de resistência simplificado para paralelogramo


Existem muitas maneiras de aproximar forças e torques para um paralelogramo, e diferentes fórmulas podem ser obtidas. Compacto o suficiente, fácil de implementar em códigos e cálculos em tempo de execução, pode ser inferido ignorando o efeito do movimento angular na força linear e o efeito da velocidade linear no torque. Tomando o tamanho do paralelogramo delimitador , podemos tirar a seguinte fórmula compacto:ez

F=ρV2eyez,exez,exeyv^


M=ρ6Ω2exeyezw^ey2+ez2,ex2+ez2,ex2+ey2


Apêndice 5: Spline Center Tornado


O tornado é centrado em torno de uma curva central cuja forma está mudando lentamente. Implementamos isso usando dois splines cúbicos consecutivos de Bezier: para o fundo eC0 para o topo. SplineC1 tem pontos de controleC0P0 , P1, P2e eP3 controlado por pontosC1P4 , P5, P6e P7. A posição do furacão está em um ponto no chão e o ponto mais altoP0 começa logo acima da camada de nuvens, mas fica atrasado, seguindo lentamente a posição no chão. Como as curvas estão conectadas em uma extremidade,P7P3 e coincide no espaço. Também usamos três órbitas de referência horizontal para pontosP4P1 , P2e P5. Finalmente foi localizado exatamente no meio entre os pontosP3P2 e eP5 no ponto médio entreP6P5 e P7.

Criando um spline central tornadoEvolução da spline no centro de tornados no tempo

Referências


  1. Just cause 4
  2. Havok
  3. Fundamentos de estruturas de dados multidimensionais e métricas . Hanan samet
  4. Curva de ordem Z
  5. Mapeamento de cubo
  6. Espuma aberta
  7. Volta
  8. Ironklad studios

Source: https://habr.com/ru/post/pt441678/


All Articles