Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Introdução
Tiques de física e quadros renderizados
Um conceito fundamental para entender no Godot é a distinção entre ticks de física (às vezes chamados de iterações ou quadros de física) e quadros renderizados. A física prossegue a uma taxa de tick fixa (definida em Project Settings > Physics > Common > Physics Tick per Second), que é padrão em 60 ticks por segundo.
No entanto, o motor não irá necessariamente renderizar na mesma taxa. Embora muitos monitores atualizem a 60 Hz (ciclos por segundo), muitos atualizam em frequências completamente diferentes (por exemplo, 75 Hz, 144 Hz, 240 Hz ou mais). Mesmo que um monitor seja capaz de mostrar um novo quadro, por exemplo, 60 vezes por segundo, não há garantia de que a CPU e a GPU serão capazes de fornecer quadros nessa taxa. Por exemplo, ao rodar com V-Sync, o computador pode ser muito lento para 60 e atingir apenas os prazos para 30 FPS, caso em que os quadros que você vê mudarão a 30 FPS (resultando em engasgos ou stuttering).
Mas há um problema aqui. O que acontece se os tiques de física não coincidirem com os quadros? O que acontece se a taxa de tiques de física estiver fora de fase com a taxa de quadros? Ou pior, o que acontece se a taxa de tiques de física for menor do que a taxa de quadros renderizados?
Este problema é mais fácil de entender se considerarmos um cenário extremo. Se você definir a taxa de tiques de física para 10 tiques por segundo em um jogo simples com uma taxa de quadros renderizados de 60 FPS. Se traçarmos um gráfico das posições de um objeto em relação aos quadros renderizados, você poderá ver que as posições parecerão "pular" a cada 1/10 de segundo, em vez de fornecer um movimento suave. Quando a física calcula uma nova posição para um novo objeto, ele não é renderizado nessa posição por apenas um quadro, mas por 6 quadros.
Esse salto pode ser visto em outras combinações de tick/taxa de quadros como falhas, ou 'jitter' (instabilidade), causados por esse efeito de escada devido à discrepância entre o tempo de tick da física e o tempo de quadro renderizado.
O que podemos fazer sobre os quadros e tiques estarem fora de sincronia?
Bloquear a taxa de tique/quadro juntas?
A solução mais óbvia é se livrar do problema, garantindo que haja um tique de física que coincida com cada quadro. Essa costumava ser a abordagem em consoles antigos e computadores de hardware fixo. Se você sabe que cada jogador usará o mesmo hardware, pode garantir que ele seja rápido o suficiente para calcular tiques e quadros a, por exemplo, 50 FPS, e terá a certeza de que funcionará muito bem para todos.
No entanto, os jogos modernos geralmente não são mais feitos para hardware fixo. Você frequentemente planejará lançar para computadores desktop, celulares e muito mais. Todos os quais possuem enormes variações de desempenho, bem como diferentes taxas de atualização de monitor. Precisamos encontrar uma maneira melhor de lidar com o problema.
Adaptar a taxa de tiques?
Em vez de projetar o jogo com uma taxa de tiques de física fixa, poderíamos permitir que a taxa de tiques mude de acordo com o hardware do usuário final. Poderíamos, por exemplo, usar uma taxa de tiques fixa que funcione para aquele hardware, ou até mesmo variar a duração de cada tique de física para corresponder a uma duração de quadro específica.
Isso funciona, mas há um problema. A física (e a lógica do jogo, que muitas vezes também é executada no _physics_process) funciona melhor e de forma mais consistente quando executada a uma taxa de tiques fixa e predeterminada. Se você tentar executar a física de um jogo de corrida projetada para 60 TPS (tiques por segundo) a, por exemplo, 10 TPS, a física se comportará de maneira completamente diferente. Os controles podem se tornar menos responsivos e as colisões/trajetórias podem ser totalmente diferentes. Você pode testar seu jogo exaustivamente a 60 TPS e depois descobrir que ele quebra nas máquinas dos usuários finais quando executado a uma taxa de tiques diferente.
Isso pode dificultar a garantia de qualidade com bugs difíceis de reproduzir, especialmente em jogos AAA, onde problemas desse tipo podem ser muito dispendiosos. Isso também pode ser problemático para jogos multijogador em termos de integridade competitiva, já que executar o jogo em certas taxas de tiques pode ser mais vantajoso do que em outras.
Bloquear a taxa de tiques, mas usar interpolação para suavizar os quadros entre os tiques de física
Esta se tornou uma das abordagens mais populares para lidar com o problema, embora seja opcional e venha desativada por padrão.
Já estabelecemos que a configuração de física/lógica de jogo mais desejável para consistência e previsibilidade é uma taxa de tiques de física fixa no momento do design. O problema é a discrepância entre a posição física registrada e onde "queremos" que um objeto físico seja mostrado em um quadro para dar um movimento suave.
A resposta acaba sendo simples, mas pode ser um pouco difícil de entender no início.
Em vez de acompanhar apenas a posição atual de um objeto físico no motor, acompanhamos tanto a posição atual do objeto quanto a posição anterior no tique de física anterior.
Por que precisamos da posição anterior (na verdade, de toda a transformação, incluindo rotação e escala)? Usando um pouco de mágica matemática, podemos usar a interpolação para calcular qual seria a transformação do objeto entre esses dois pontos, em nosso mundo ideal de movimento contínuo e suave.
Interpolação linear
A maneira mais simples de conseguir isso é a interpolação linear, ou lerping, que você já deve ter usado antes.
Consideremos apenas a posição, e uma situação em que sabemos que a coordenada X do tique de física anterior era de 10 unidades, e a coordenada X do tique de física atual é de 30 unidades.
Nota
Do not worry too much about the math, as the engine can mostly take care of it. Although linear interpolation is described here for simplicity, Godot itself may use different, more complex forms of interpolation.
A fração de interpolação de física
Se nossos tiques de física estiverem acontecendo 10 vezes por segundo (para este exemplo), o que acontece se o nosso quadro renderizado ocorrer no tempo de 0,12 segundos? Podemos fazer algumas contas para descobrir onde o objeto estaria para obter um movimento suave entre os dois tiques.
Antes de tudo, precisamos calcular o quão avançado no tique de física queremos que o objeto esteja. Se o último tique de física ocorreu em 0,1 segundos, estamos a 0,02 segundos (0,12 - 0,1) de um tique que sabemos que levará 0,1 segundos (10 tiques por segundo). A fração decorrida do tique é, portanto:
fraction = 0.02 / 0.10
fraction = 0.2
Isso é chamado de fração de interpolação física, e é convenientemente calculado para você pelo Godot. Ele pode ser recuperado em qualquer quadro chamando Engine.get_physics_interpolation_fraction.
Calculando a posição interpolada
Assim que tivermos a fração de interpolação, podemos inseri-la em uma equação padrão de interpolação linear. A coordenada X seria, portanto:
x_interpolated = x_prev + ((x_curr - x_prev) * 0.2)
Substituindo nosso x_prev por 10 e x_curr por 30:
x_interpolated = 10 + ((30 - 10) * 0.2)
x_interpolated = 10 + 4
x_interpolated = 14
Vamos detalhar isso:
Sabemos que o X começa a partir da coordenada no tique anterior (
x_prev), que é de 10 unidades.Sabemos que após o tique completo, a diferença entre o tique atual e o tique anterior terá sido adicionada (
x_curr - x_prev) (que é de 20 unidades).A única coisa que precisamos variar é a proporção desta diferença que adicionamos, de acordo com o quão avançados estamos no tique de física.
Nota
Embora este exemplo interpole a posição, o mesmo pode ser feito com a rotação e a escala dos objetos. Não é necessário saber os detalhes, pois o Godot fará tudo isso por você.
Transformações suavizadas entre tiques de física?
Juntar tudo isso mostra que deve ser possível ter uma estimativa agradável e suave da transformação dos objetos entre o tique de física atual e o anterior.
Mas espere, você deve ter notado algo. Se estamos interpolando entre os tiques atual e anterior, não estamos estimando a posição do objeto agora, estamos estimando a posição do objeto no passado. Para ser exato, estamos estimando a posição do objeto entre 1 e 2 tiques no passado.
No passado
O que isso significa? Esse esquema funciona, mas significa que estamos efetivamente introduzindo um atraso entre o que vemos na tela e onde os objetos deveriam estar.
Na prática, a maioria das pessoas não notará esse atraso, ou melhor, normalmente não é objetável. Já existem atrasos significativos envolvidos em jogos, nós simplesmente não os notamos normalmente. O efeito mais significativo é que pode haver um pequeno atraso na entrada, o que pode ser um fator em jogos de reflexo rápido. Em algumas dessas situações de entrada rápida, você pode querer desativar a interpolação física e usar um esquema diferente, ou usar uma taxa de tick alta, o que mitiga esses atrasos.
Por que olhar para o passado? Por que não prever o futuro?
There is an alternative to this scheme, which is: instead of interpolating between the previous and current tick, we use math to extrapolate into the future. We try to predict where the object will be, rather than show it where it was. This can be done and may be offered as an option in future, but there are some significant downsides:
A previsão pode não estar correta, especialmente quando um objeto colide com outro objeto durante o tique de física.
Onde uma previsão estava incorreta, o objeto pode extrapolar para uma posição "impossível", como dentro de uma parede.
Desde que a velocidade do movimento seja lenta, essas previsões incorretas podem não ser um grande problema.
Quando uma previsão estava incorreta, o objeto pode ter que pular ou reajustar bruscamente de volta ao caminho corrigido. Isso pode ser visualmente incômodo.
Interpolação de timestep fixo
No Godot, todo esse sistema é chamado de interpolação física, mas você também pode ouvir ser chamado de 'interpolação de passo de tempo fixo' (fixed timestep interpolation), pois ele interpola entre objetos movidos com um passo de tempo fixo (ticks de física por segundo). De certa forma, o segundo termo é mais preciso, porque também pode ser usado para interpolar objetos que não são movidos pela física.
Dica
Embora a interpolação de física seja geralmente uma boa escolha, existem exceções em que você pode optar por não usar a interpolação de física integrada do Godot (o usá-la de forma limitada). Uma categoria de exemplo são os jogos multijogador via internet. Os jogos multijogador frequentemente recebem informações baseadas em tiques ou tempo de outros jogadores ou de um servidor, e estas podem não coincidir com os tiques de física locais, de modo que uma técnica de interpolação personalizada pode, muitas vezes, ser uma escolha melhor.