Separando a lógica / atualização do código de renderização / desenho em um único encadeamento usando suspensão

9

Eu li que a velocidade dos objetos do jogo não deve ser prejudicada pelo FPS, mas sim pelo tempo. Como posso separar o código de atualização / desenho para maximizar o desempenho sem limitar a taxa de desenho e fornecer uma taxa de atualização lógica constante com base no tempo?

Meu pseudo-código atual é o seguinte

loop
{
    draw();
    if (ticksElapsed() > 100)
    {
        update();
        ticks+= ticksElapsed();
    }        
}

O problema é que o código do desenho dificulta o desempenho da taxa de atualização (). E consome 100% da CPU, porque se o sono é acionado, ele desativa as duas funções de desenho / lógica.

Também estou usando SDL e ele não parece ter uma opção vsync. Também ouvi falar dos termos tempo fixo e variável, mas não tenho certeza de como isso pode ser feito com o sono ()

Oskenso Kashi
fonte
11
Você não precisa desperdiçar 100% de energia da CPU apenas por aguardar, coloque um sono (0) no final dos intervalos enquanto ticksElapsed () <100. O SO retornará ao thread imediatamente se não houver outro thread que quer correr. Mas não desperdiçando 100% de energia da CPU.
Maik Semder
No entanto, melhor solução para tal configuração fio um 1 é usar vsync, se você não pode vsync, então sono chamada (0) em um loop até que você alcançou a taxa de quadro de destino, em seguida, atualizar e desenhar
Maik Semder

Respostas:

3

No seu snippet de código, parece que você está tentando executar seu jogo no modo de etapa de tempo fixo, aguardando se o desenho e a atualização demorar menos de 15ms (60fps). Isso é possível e você adivinhou certo que isso não pode ser feito usando uma chamada de suspensão, porque você não sabe exatamente quanto tempo vai dormir. O ciclo de espera ocupada é a boa solução.

No entanto, considere o caso em que sua atualização e desenho excedem 15ms, agora você tem o desenho e a atualização do jogo mais lentos. Agora você pode fazer duas coisas: detectar esse estado e soltar quadros (pule o desenho e vá direto para a atualização até que esteja sincronizado novamente); no entanto, se o computador estiver lento, ele nunca será recuperado.

Uma outra solução é tornar sua lógica de atualização independente por tempo fixo. Você não precisa de um thread separado para isso, basta respeitar a velocidade com que as coisas devem se mover. Em vez de 5 pixels por tick, você deve usar 50 pixels por segundo. Você precisaria de um cronômetro de alta precisão para conseguir isso, e toda a sua lógica de atualização deve poder acessar o cronômetro para ver quanto tempo se passou desde a última atualização.

Basicamente você vai de:

void UpdatePlayer()
 player.x += 10;

Para

void UpdatePlayer(float elapsedSeconds) //the total seconds elapsed since last update
 player.x += walkspeed * elapsedSeconds;
Roy T.
fonte
Então meu motor sempre consumirá 100% e não há nada que eu possa realmente fazer sobre isso?
Oskenso Kashi
11
Ele consumirá tantos ciclos quanto o agendador permitir, mas isso não é realmente um problema, pois você não pode fazer muitas outras coisas enquanto joga um jogo :).
Roy T.
@Oskenso No entanto, é um problema se você usar mais de 1 thread, o thread principal não permitirá que os outros funcionem o máximo que puderem, desperdiçando muita energia computacional no loop while, você deve considerar um sono
Maik Semder
@Maik Semder: você tem uma solução para o sono (x) não ser preciso? Após o intervalo de suspensão, o encadeamento está pronto para ser executado. Mas não é garantido que um encadeamento pronto seja executado imediatamente. Isso depende do agendador. Quando você estiver usando dois tópicos existem outras soluções, para que ver este excelente artigo: altdevblogaday.com/2011/07/03/threading-and-your-game-loop
Roy T.
11
@Roy sleep (0) é a solução. Ele retorna imediatamente se não houver outro thread que deseja executar ( Sleep WinAPI ) e oferece a outros threads a chance de executar. Se o outro encadeamento não der a chance de executar o encadeamento principal, você terá um problema de encadeamento, mas bloquear todo o resto sem chamar o sono em primeiro lugar o torna ainda pior e dificilmente é uma solução. A chave é chamar suspensão (0) e testar o tempo decorrido até atingir a taxa de quadros fixa desejada, para que você não perca 100% da CPU apenas por aguardar.
Maik Semder