Atualmente, estou começando a aprender OpenGL na escola e comecei a fazer um jogo simples outro dia (por conta própria, não para a escola). Estou usando freeglut e construindo-o em C; portanto, para o loop do jogo, eu realmente estava usando uma função que fiz passar glutIdleFunc
para atualizar todo o desenho e a física de uma só vez. Isso foi bom para animações simples que eu não me importei muito com a taxa de quadros, mas como o jogo é baseado principalmente na física, eu realmente quero (preciso) amarrar a velocidade da atualização.
Portanto, minha primeira tentativa foi fazer minha função passar para glutIdleFunc
( myIdle()
) para acompanhar quanto tempo se passou desde a chamada anterior e atualizar a física (e atualmente os gráficos) a cada milissegundos. Eu costumava timeGetTime()
fazer isso (usando <windows.h>
). E isso me levou a pensar: usar a função ociosa é realmente uma boa maneira de fazer o loop do jogo?
Minha pergunta é: qual é a melhor maneira de implementar o loop do jogo no OpenGL? Devo evitar usar a função ociosa?
Respostas:
A resposta simples é não, você não deseja usar o retorno de chamada glutIdleFunc em um jogo que tenha algum tipo de simulação. A razão para isso é que essa função separa a animação e desenha o código da manipulação de eventos da janela, mas não de forma assíncrona. Em outras palavras, o recebimento e a entrega de eventos da janela interrompem o código de desenho (ou o que você colocar nesse retorno de chamada), isso é perfeitamente adequado para um aplicativo interativo (interação e resposta), mas não para um jogo em que a física ou o estado do jogo deve progredir independente da interação ou render tempo.
Você deseja desacoplar completamente a manipulação de entrada, o estado do jogo e o código de desenho. Existe uma solução fácil e limpa para isso, que não envolve a biblioteca de gráficos diretamente (ou seja, é portátil e fácil de visualizar); você deseja que todo o ciclo do jogo produza tempo e faça com que a simulação consuma o tempo produzido (em pedaços). A chave, no entanto, é integrar a quantidade de tempo que sua simulação consumiu em sua animação.
A melhor explicação e tutorial que encontrei sobre isso é Fix Your Timestep, de Glenn Fiedler
Este tutorial possui o tratamento completo; no entanto, se você não tiver uma simulação física real , poderá pular a verdadeira integração, mas o loop básico ainda se resume a (no pseudo-código detalhado):
Ao fazer isso dessa maneira, as paradas no código de renderização, no tratamento de entradas ou no sistema operacional não fazem com que o estado do jogo fique para trás. Este método também é portátil e independente da biblioteca de gráficos.
GLUT é uma boa biblioteca, no entanto, é estritamente orientada a eventos. Você registra retornos de chamada e dispara o loop principal. Você sempre entrega o controle do seu loop principal usando GLUT. Existem hacks para contornar isso , você também pode falsificar um loop externo usando temporizadores e outros, mas outra biblioteca é provavelmente o melhor (mais fácil) caminho a percorrer. Existem muitas alternativas, eis algumas (com boa documentação e tutoriais rápidos):
fonte
Eu acho que
glutIdleFunc
está bem; é essencialmente o que você acabaria fazendo manualmente de qualquer maneira, se não o usasse. Não importa o que você terá um loop restrito que não é chamado em um padrão fixo, e você precisa fazer a escolha se deseja convertê-lo em um loop de tempo fixo ou mantê-lo em um loop de tempo variável e fazer Certifique-se de todas as suas contas de matemática para interpolar o tempo. Ou mesmo uma mistura deles, de alguma forma.Certamente, você pode ter uma
myIdle
função para a qual passa eglutIdleFunc
, dentro disso, mede o tempo que passou, dividi-lo pelo seu timestap fixo e chama outra função várias vezes.Aqui está o que não fazer:
FixedTimestep não seria chamado a cada 10 milissegundos. Na verdade, seria mais lento que o tempo real, e você pode acabar enganando os números para compensar quando realmente a raiz do problema está na perda do restante quando você divide
timeDifference
por 10.Em vez disso, faça algo como isto:
Agora, essa estrutura de loop garante que sua
fixedTimestep
função seja chamada uma vez a cada 10 milissegundos, sem perder nenhum milissegundo como restante ou algo assim.Devido à natureza multitarefa dos sistemas operacionais, você não pode confiar em uma função chamada exatamente a cada 10 milissegundos; mas o loop acima aconteceria rápido o suficiente para que, esperançosamente, estivesse próximo o suficiente, e você pode assumir que cada chamada
fixedTimestep
incrementaria sua simulação em 10 milissegundos.Leia também esta resposta e especialmente os links fornecidos na resposta.
fonte