quinta-feira, 2 de outubro de 2014

O War Legacy - Parte 2.2 - TimeStep

Olá pessoal, aqui é o Lince. Bem vindos de volta.
Como vocês estão?
Esta aqui é a continuação da Parte 2, então se ainda não a leu, vai lá que eu espero!
Pra começar gostaria de esclarecer uns termos que acabei fazendo confusão no ultimo post.


GameSpeed:
Não falei sobre ele no ultimo post. Ele é o que define a velocidade de processamento da física e das interações no jogo. De modo geral, GameSpeed é a quantidade de vezes que o update() do jogo é chamado.
No caso do TimeStep Fixo, o GameSpeed é designado pelo programador, o que eu chamei anteriormente de FPS fixo é na verdade GameSpeed fixo.
O termo é confuso por que nos jogos ele fica transparente para o jogador, sendo que, para os jogos que usam TimeStep Variável e a implementação intuitiva, o GameSpeed e o FramesPerSecond são a mesma coisa.
FramesPerSecond (FPS):
O amplamente conhecido FPS é basicamente a quantidade de chamadas ao render() do jogo.
Este conceito é mais ouvido porque é muitas vezes usado como parâmetros para medir o desempenho de um jogo.
Exceto na implementação intuitiva, maior FPS significa maior desempenho.

Então, na ultima visita eu prometi uma conclusão sobre o assunto do TimeStep e os comentários sobre a utilização no WL.
O controle do TimeStep é o que faz o jogo rodar numa mesma velocidade em máquinas diferentes.
Nas primeiras versões do War Legacy, nas quais o laço principal do jogo era basicamente o mostrado aqui, o update e o render não tinham nenhuma diferenciação, tudo fazia parte do mesmo laço. E a forma de controlar a velocidade do jogo (ou o FPS) era através de um calculo do FPS atual e um ajuste no parâmetro da função rest(). A função rest(t) pausa o processamento por t milissegundos, um ajuste no valor de t fazia o jogo parar por mais tempo quando a velocidade precisasse diminuir ou por menos tempo quando a velocidade precisasse aumentar.
Um trecho do código que não foi citado quando mostrei aqui o pseudocódigo das primeiras versões do WL era mais ou menos assim:

Pseudocódigo Intuitivo

fps = calcular_fps();
se (fps > FPS_DESEJADO) t++;
se (fps < FPS_DESEJADO) t--;
se (t > 0) rest(t);

FPS_DESEJADO é uma constante com o valor desejado para o fps em jogo. Esse valor foi concebido de forma arbitrária e as velocidades no jogo precisaram ser calculadas a partir dele. Por exemplo, se eu escolhesse 30 para ser o fps desejado e, através de experimentação, tivesse chegado a conclusão que a velocidade ideal de um objeto qualquer (digamos, das naves) seja 150 pixels/segundo então, aplicando uma formula já apresentada aqui, encontro o valor de 5 pixels/frame para as funções de movimento das naves.

A função rest(t) faz o processo "descansar" por aproximadamente t milissegundos. Digo aproximadamente porque há pouca garantia de que será exatamente t milissegundos, e a principal razão disso é a forma que o Sistema Operacional faz o escalonamento dos processos em andamento.
Abstraindo essa parte, apenas ter em mente que rest(t) não garante uma pausa no processo numa quantidade de tempo exata à que queremos já dá pra perceber que esta não é uma boa prática de TimeStep.
Devo adicionar que: usar funções como rest, sleep e afins não é uma prática abominável. Elas têm seu próprio benefício, que é poupar o uso continuo do CPU para economizar bateria em plataformas mobile. Porém, por causa da sua característica de possível não-determinabilidade, as mesmas devem ser usadas de forma responsável e consciente.

Outro detalhe sobre o desempenho dessa minha implementação é que o código de controle do jogo, vulgo update(), estava tão bagunçado e ineficiente que o fps ficava 90% do tempo abaixo do desejado, fazendo o valor de t diminuir abaixo de zero e, consequentemente, fazendo a função rest(t) quase nunca ser chamada.

Então a lição que fica é a seguinte: Controle do TimeStep do jogo é importantissimo para manter a consistência e fluidez do gameplay, mas a organização e planejamento do código do jogo é essencial para que esse controle faça alguma diferença.
Por causa disso, no(s) próximo(s) post(s) vou dar algumas dicas de como programar pensando nesse aspecto, e para começar vou falar da Memória Cache e como programadores de jogos podem usá-la a seu favor, então até lá!

Então é isso, espera que tenham gostado e aprendido alguma coisa, deixa um comentário aí. E como sempre vocês podem me encontrar no tuiter @LinceAssassino ou no email emailDoLince(arroba)gmail(ponto)com para qualquer dúvida, sugestão, crítica ou simplesmente se quiser trocar uma ideia.
Abraços do Lince.



Nenhum comentário:

Postar um comentário