Antes de aplicar extrapolação ao movimento do meu sprite, minha colisão funcionou perfeitamente. No entanto, após aplicar extrapolação ao movimento do meu sprite (para suavizar as coisas), a colisão não funciona mais.
É assim que as coisas funcionavam antes da extrapolação:
No entanto, depois de implementar minha extrapolação, a rotina de colisão é interrompida. Suponho que isso ocorre porque ele está agindo sobre a nova coordenada produzida pela rotina de extrapolação (que está situada na minha chamada de renderização).
Depois de aplicar minha extrapolação
Como corrigir esse comportamento?
Eu tentei colocar uma verificação de colisão extra logo após a extrapolação - isso parece esclarecer muitos problemas, mas eu descartei isso porque colocar a lógica na minha renderização está fora de questão.
Eu também tentei fazer uma cópia da posição spritesX, extrapolando e desenhando usando isso em vez do original, deixando o original intacto para a lógica - essa parece ser uma opção melhor, mas ainda produz alguns efeitos estranhos ao colidir com paredes. Tenho certeza de que essa também não é a maneira correta de lidar com isso.
Encontrei algumas perguntas semelhantes aqui, mas as respostas não me ajudaram.
Este é o meu código de extrapolação:
public void onDrawFrame(GL10 gl) {
//Set/Re-set loop back to 0 to start counting again
loops=0;
while(System.currentTimeMillis() > nextGameTick && loops < maxFrameskip){
SceneManager.getInstance().getCurrentScene().updateLogic();
nextGameTick+=skipTicks;
timeCorrection += (1000d/ticksPerSecond) % 1;
nextGameTick+=timeCorrection;
timeCorrection %=1;
loops++;
tics++;
}
extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks;
render(extrapolation);
}
Aplicando extrapolação
render(float extrapolation){
//This example shows extrapolation for X axis only. Y position (spriteScreenY is assumed to be valid)
extrapolatedPosX = spriteGridX+(SpriteXVelocity*dt)*extrapolation;
spriteScreenPosX = extrapolationPosX * screenWidth;
drawSprite(spriteScreenX, spriteScreenY);
}
Editar
Como mencionei acima, tentei fazer uma cópia das coordenadas do sprite especificamente para desenhar ... isso tem seus próprios problemas.
Em primeiro lugar, independentemente da cópia, quando o sprite está em movimento, é super suave, quando para, balança levemente para a esquerda / direita - pois ainda está extrapolando sua posição com base no tempo. Esse é um comportamento normal e podemos 'desligá-lo' quando o sprite parar?
Tentei ter sinalizadores para esquerda / direita e extrapolar apenas se um deles estiver ativado. Também tentei copiar a última e a atual posição para ver se há alguma diferença. No entanto, no que diz respeito à colisão, isso não ajuda.
Se o usuário estiver pressionando, digamos, o botão direito e o sprite estiver se movendo para a direita, quando atingir uma parede, se o usuário continuar pressionando o botão direito, o sprite continuará animado para a direita, enquanto estiver parado na parede ( portanto, não está realmente se movendo), no entanto, porque a bandeira certa ainda está definida e também porque a rotina de colisão está constantemente movendo o sprite para fora da parede, ainda parece para o código (não para o jogador) que o sprite ainda está se movendo e, portanto, a extrapolação continua. Então, o que o jogador veria é o sprite 'estático' (sim, está animando, mas na verdade não está se movendo pela tela), e de vez em quando treme violentamente enquanto a extrapolação tenta fazer isso ..... .. Espero que ajude
Respostas:
Ainda não posso postar um comentário, por isso vou postar isso como resposta.
Se eu entendi o problema corretamente, é mais ou menos assim:
Eu posso pensar em 3 soluções possíveis. Vou listá-los na ordem que é mais desejável para menos IMHO.
Código de exemplo para # 2.
Eu acho que o número 2 provavelmente seria o mais rápido e fácil de começar uma corrida, embora o número 1 pareça ser uma solução mais lógica. Dependendo de como você lida com a sua solução de tempo delta nº 1, pode ser quebrada da mesma maneira por um delta grande; nesse caso, você pode precisar usar os nºs 1 e 2 juntos.
EDIT: Eu entendi mal o seu código anteriormente. Os loops devem renderizar o mais rápido possível e atualizar em um intervalo definido. É por isso que você interpolará a posição do sprite, para lidar com o caso em que você está desenhando mais do que atualizando. No entanto, se o loop ficar para trás, você fará uma pesquisa de atualização até ser pego ou pular o número máximo de quadros desenhados.
Dito isto, o único problema é que o objeto está se movendo após uma colisão. Se houver uma colisão, o objeto deve parar de se mover nessa direção. Portanto, se houver uma colisão, defina sua velocidade como 0. Isso deve impedir que a função render mova o objeto ainda mais.
fonte
Parece que você precisa separar totalmente a renderização e a atualização da física. Normalmente, a simulação subjacente será executada em intervalos de tempo discretos e a frequência nunca mudará. Por exemplo, você pode simular o movimento da sua bola a cada 1/60 de segundo, e é isso.
Para permitir uma taxa de quadros variável, o código de renderização deve operar com uma frequência variável, mas qualquer simulação ainda deve estar em um intervalo de tempo fixo. Isso permite que os gráficos leiam a memória da simulação como somente leitura e configure interpolação em vez de extrapolação.
Como a extrapolação está tentando prever onde serão os valores futuros, mudanças repentinas no movimento podem gerar enormes erros de extrapolação. É melhor renderizar sua cena sobre um quadro por trás da simulação e interpolar entre posições conhecidas discretas.
Se você quiser ver alguns detalhes da implementação, já escrevi uma seção curta sobre esse tópico em um artigo aqui . Por favor, consulte a seção chamada "Timestepping".
Aqui está o código psuedo importante do artigo:
A
RenderGame
função é de maior interesse. A idéia é usar a interpolação entre posições discretas de simulação. O código de renderização pode fazer suas próprias cópias dos dados somente leitura da simulação e usar um valor interpolado temporário para renderizar. Isso lhe dará um movimento muito suave, sem problemas tolos como o que você parece estar tendo!fonte