O que você está procurando?
Hero background image
Desempenho aprimorado da física para uma jogabilidade suave

Este é o quinto de uma série de artigos que apresenta dicas de otimização para seus projetos Unity. Use-os como um guia para executar taxas de quadros mais altas com menos recursos. Depois de experimentar essas práticas recomendadas, não deixe de conferir as outras páginas da série:

A física pode criar uma jogabilidade complexa, mas isso tem um custo de desempenho. Depois de conhecer esses custos, você pode ajustar a simulação para gerenciá-los adequadamente. Use estas dicas para manter-se dentro de sua taxa de quadros alvo e criar uma reprodução suave com o Physics integrado do Unity (NVIDIA PhysX).

Interface de opções de cozimento no editor Unity
Verifique seus colisores

As malhas usadas na física passam por um processo chamado Cooking. Isso prepara a malha para que ela possa trabalhar com consultas de física, como raycasts, contatos e assim por diante.

Um MeshCollider tem várias CookingOptions para ajudá-lo a validar a malha para a física. Se tiver certeza de que sua malha não precisa dessas verificações, você pode desativá-las para acelerar o tempo de cozimento.

Nas CookingOptions de cada MeshCollider, desmarque as opções EnableMeshCleaning, WeldColocatedVertices e CookForFasterSimulation. Essas opções são valiosas para malhas geradas proceduralmente em tempo de execução, mas podem ser desativadas se suas malhas já tiverem os triângulos adequados.

Se estiver visando especificamente o PC, certifique-se de manter a opção Use Fast Midphase ativada. Isso muda para um algoritmo mais rápido do PhysX 4.1 durante a fase intermediária da simulação (o que ajuda a restringir um pequeno conjunto de triângulos com potencial de interseção para consultas de física). As plataformas que não são de desktop ainda devem usar o algoritmo mais lento que gera R-trees.

Os colisores de malha geralmente são caros, portanto, considere substituir os colisores de malha mais complexos por outros primitivos ou simplificados para aproximar a forma original.

Saiba mais na documentação do CookingOptions.

Interface de BakeMeshJob
Use Physics.BakeMesh

Se você estiver gerando malhas processualmente durante o jogo, poderá criar um Mesh Collider em tempo de execução. No entanto, ao adicionar um componente Mesh Collider diretamente à malha, a física é cozida na thread principal. Isso pode consumir um tempo significativo da CPU.

Use o Physics.BakeMesh para preparar uma malha para uso com um Mesh Collider e salve os dados de bake com a própria malha. Um novo Mesh Collider que faça referência a essa malha reutilizará esses dados pré-cozidos, em vez de cozer a malha novamente. Isso pode ajudar a reduzir o tempo de carregamento da cena ou o tempo de instanciação posteriormente. Para otimizar o desempenho, você pode descarregar o cozimento da malha para outro thread com o Sistema de trabalho C#.

Consulte este exemplo para obter detalhes sobre como preparar malhas em vários threads.

Interface de configurações do projeto de física
Ajuste suas configurações

Nas configurações do jogador, marque Prebake Collision Meshes sempre que possível. Também recomendamos revisar a configuração da Collision Matrix para garantir que os objetos do jogador e da mecânica do jogo estejam nas camadas corretas.

A remoção de retornos de chamada de acionadores para camadas desnecessárias pode ser uma grande vitória, portanto, tente simplificar sua Layer Collision Matrix. Você pode editar suas configurações de física em Project Settings > Physics.

Saiba mais na documentação da Collision Matrix.

Fixed Timestep padrão no Unity Editor
Modificar a frequência da simulação

Os mecanismos de física funcionam em um intervalo de tempo fixo. Para ver a taxa fixa em que seu projeto está sendo executado, vá para Edit > Project Settings > Time.

O campo Fixed Timestep define o delta de tempo usado por cada Physics Step. Por exemplo, o valor padrão de 0,02 segundos (20 ms) é equivalente a 50 quadros por segundo (fps), ou 50 Hz.

Como cada quadro no Unity leva uma quantidade variável de tempo, ele não está perfeitamente sincronizado com a simulação física. O mecanismo faz a contagem regressiva até o próximo intervalo de tempo físico. Se um quadro for executado ligeiramente mais lento ou mais rápido, o Unity usará o tempo decorrido para saber quando executar a simulação física no Timestep adequado.

Se um quadro levar muito tempo para ser preparado, isso pode levar a problemas de desempenho. Portanto, se o seu jogo sofrer um pico (por exemplo, instanciar muitos GameObjects ou carregar um arquivo do disco), o quadro poderá levar 40 ms ou mais para ser executado. Com o intervalo de tempo fixo padrão de 20 ms, isso faria com que duas simulações de física fossem executadas no quadro seguinte para "alcançar" o intervalo de tempo variável.

Simulações físicas extras, por sua vez, adicionam mais tempo para processar o quadro. Em plataformas de baixo custo, isso pode levar a uma espiral descendente de desempenho.

Um quadro subsequente que leva mais tempo para ser preparado faz com que o acúmulo de simulações físicas também seja maior. Isso resulta em quadros ainda mais lentos e mais simulações a serem executadas por quadro. O resultado é um desempenho enfraquecido.

Eventualmente, o tempo entre as atualizações físicas pode exceder o intervalo de tempo máximo permitido. Após esse limite, o Unity começa a perder atualizações de física e o jogo fica travado.

Para evitar problemas de desempenho com a física:

  • Reduzir a frequência da simulação: Para plataformas de baixo custo, aumente oFixed Timestep para um pouco mais do que a taxa de quadros desejada. Por exemplo, use 0,035 segundos para 30 fps no celular. Isso pode ajudar a evitar essa espiral de queda no desempenho.
  • Diminuir o intervalo de tempo máximo permitido: O uso de um valor menor (como 0,1 segundo) sacrificará um pouco a precisão da simulação física, mas também limitará o número de atualizações físicas que podem ocorrer em um quadro. Faça experiências com valores para encontrar algo que funcione para os requisitos de seu projeto.
  • Simule a etapa de física manualmente, se necessário: Desative a opção Simulação automática nas configurações de física e invoque diretamente o Physics.Simulate durante a fase de atualização do quadro. Isso permite que você determine ativamente quando executar a etapa de física. Passe Time.deltaTime para Physics.Simulatepara manter a física em sincronia com o tempo de simulação.
  • Observação: Essa abordagem pode causar instabilidades na simulação de física, especialmente em cenas com física complexa ou tempos de quadro altamente variáveis. Use-o com cautela.

Saiba mais na documentação do Physics.Simulate.

Interface do tipo de fase ampla
Use o Box Pruning para cenas grandes

O mecanismo de física do Unity é executado em duas etapas:

  • A fase ampla: Coleta possíveis colisões usando um algoritmo Sweep and Prune
  • A fase estreita: Quando o mecanismo realmente calcula as colisões

A configuração padrão de fase ampla de Sweep and Prune Broadphase(Edit > Project Settings > Physics > Broadphase Type) pode gerar falsos positivos para mundos que são geralmente planos e têm muitos colisores. Se seu cenário for grande e quase todo plano, evite esse problema e mude para o Automatic Box Pruning ou Multibox Pruning Broadphase. Essas opções dividem o mundo em uma grade, onde cada célula da grade executa Sweep e Prune.

O Multibox Pruning Broadphase permite que você especifique manualmente os limites do mundo e o número de células da grade, enquanto o Automatic Box Pruning calcula tudo para você.

Veja a lista completa de propriedades de Física aqui.

Iterações padrão do solucionador
Modificar iterações do solucionador

Se você quiser simular um corpo físico específico com mais precisão, aumente seu Rigidbody.solverIterations. Isso substitui o Physics.defaultSolverIterations, que pode ser encontrado em Edit > Project Settings > Physics > Default Solver Iterations.

Para otimizar suas simulações de física, defina um valor relativamente baixo no defaultSolveIterations do projeto. Em seguida, aplique valores personalizados mais altos de Rigidbody.solverIterations às instâncias individuais que precisam de mais detalhes.

Obtenha mais informações sobre Rigidbody.solverIterations.

Sincronização automática desativada em uma cena
Desativar a sincronização automática de transformação

Quando você atualiza uma Transform, o Unity não a sincroniza automaticamente com o mecanismo de física. O Unity acumula transformações e espera que a atualização da física seja executada ou que o usuário chame Physics.SyncTransforms.

Se quiser sincronizar a física com suas transformações com mais frequência, você pode definir Physics.autoSyncTransform como True (também encontrado em Project Settings > Physics > Auto Sync Transforms). Quando isso é ativado, qualquer Rigidbody ou Collider nessa Transform, juntamente com seus filhos, é automaticamente atualizado com a Transform.

Entretanto, se isso não for absolutamente necessário, desative-o. Uma série de consultas físicas sucessivas (como raycasts) pode levar a uma perda de desempenho.

Saiba mais sobre o Physics.SyncTransforms.

Única instância de Collision
Reutilização de retornos de chamada de colisão

Eventos de colisão e acionamento (OnCollisionEnter, OnCollisionStay, OnCollisionExit, OnTriggerEnter, OnTriggerStay, OnTriggerExit) serão acionados para qualquer MonoBehaviour que implemente essas funções e atenda aos critérios de interação. Esses eventos também serão enviados aos MonoBehaviours desativados.

Por isso, é recomendável implementar essas funções somente quando necessário, pois as funções vazias e não essenciais serão chamadas. Deve-se tomar cuidado especial com OnCollisionStay e OnTriggerStay porque eles podem ser chamados várias vezes, dependendo do número de colisores envolvidos.

As chamadas de retornoMonoBehaviour.OnCollisionEnter, MonoBehaviour.OnCollisionStaye MonoBehaviour.OnCollisionExit também recebem uma instância de colisão como parâmetro. Essa instância de colisão é alocada no heap gerenciado e deve ser coletada pelo lixo.

Para reduzir a quantidade de lixo gerado, ative o Physics.reuseCollisionCallbacks(encontrado em Project Settings > Physics > Reuse Collision Callbacks). Com isso ativo, o Unity atribui apenas uma única instância de par de colisão a cada retorno de chamada. Isso reduz o desperdício para o coletor de lixo (GC) e melhora o desempenho geral.

Observação: Se você fizer referência à instância de colisão fora dos retornos de chamada de colisão para pós-processamento, deverá desativarReuse Collision Callbacks.

Saiba mais sobre o Physics.reuseCollisionCallbacks.

Mover colisores estáticos

Static Colliders são GameObjects com um componente Collider, mas sem um Rigidbody. Ao contrário do seu nome, você pode mover um Static Collider.

Basta modificar a posição do corpo físico, acumular as alterações de posição e sincronizar antes da atualização física. Você não precisa adicionar um componente Rigidbody ao Static Collider apenas para movê-lo. Mas se você quiser que o Static Collider interaja com outros corpos físicos de uma forma mais complexa, dê a ele um corpo rígido cinemático. Usar Rigidbody.position e Rigidbody.rotation para movê-lo em vez de acessar o componente Transform. Isso garante um comportamento mais previsível do mecanismo de física.

Observação: Na física 2D, não mova os colisores estáticos porque a reconstrução da árvore consome muito tempo.

Obtenha mais informações sobre Rigidbodies.

Usar consultas sem alocação

Para detectar e coletar colisores a uma certa distância, em uma direção específica, use raycasts e outras consultas de física como BoxCast.

Consultas de física que retornam vários colisores como uma matriz, como OverlapSphere ou OverlapBoxprecisam alocar esses objetos no heap gerenciado. Isso significa que o coletor de lixo precisa, eventualmente, coletar os objetos alocados, o que pode diminuir o desempenho se isso acontecer no momento errado.

Para reduzir essa sobrecarga, use as versões NonAlloc dessas consultas. Por exemplo, se estiver usando o OverlapSphere para coletar todos os possíveis colisores em torno de um ponto, certifique-se de usar OverlapSphereNonAlloc. Isso permite que você passe uma matriz de colisores (o parâmetro de resultados) para atuar como um buffer.

O método NonAlloc funciona sem gerar lixo. Caso contrário, ele funciona como o método de alocação correspondente. Lembre-se apenas de definir um buffer de resultados de tamanho suficiente ao usar um método NonAlloc. O buffer não aumenta se ficar sem espaço.

Saiba mais na documentação do método NonAlloc.

Consultas em lote para raycasting

Embora você possa executar consultas de raycast com Physics.Raycastisso pode consumir uma quantidade significativa de tempo de CPU. Isso é especialmente verdadeiro se você tiver um grande número de operações de raycast (por exemplo, calcular a linha de visão para 10.000 agentes).

Usar RaycastCommand para fazer a consulta em lote usando o C# Job System. Isso descarrega o trabalho do thread principal para que os raycasts possam ocorrer de forma assíncrona e em paralelo.

Veja um exemplo na documentação do RaycastCommands.

Interface do depurador de física
Visualize com o depurador de física

Use a janela Physics Debug(Window > Analysis > Physics Debugger) para ajudar a solucionar problemas de colisores ou discrepâncias. Essa janela mostra um indicador codificado por cores dos GameObjects que podem colidir uns com os outros.

Para obter mais informações, consulte a página de visualização do Physics Debug.

chave de unidade art. 21 11
Obtenha o e-book gratuito

Um dos nossos guias mais abrangentes reúne mais de 80 dicas práticas sobre como otimizar seus jogos para PC e console. Criadas por nossos engenheiros especializados em Success e Accelerate Solutions, essas dicas detalhadas o ajudarão a aproveitar ao máximo o Unity e a aumentar o desempenho do seu jogo.

Você gostou deste conteúdo?