.:: Menu Rápido ::.

Linux | C/C++ | Downloads | SDL | [×]

segunda-feira, 23 de junho de 2008

Controle de tempo e Frame Rate

Uma das primeiras preocupações quando se vai trabalhar em um jogo é com a taxa de FPS. (Frames per second ou Frame Rate)
Não há segredo algum em limitar a taxa de FPS, e isto pode ser feito facilmente caso seja o desejado.

» Post Completo...

int t1,t2, delay;
delay = 25; //- 1000/25 = 40 FPS
t1 = SDL_GetTicks();
while(bLoop){
while(SDL_PollEvent(&sdlEvent)){
//- loop de eventos
}
logica();
//- calcula os milissegundos passados
t2 = SDL_GetTicks() - t1;
if(t2 < delay){
//- espera o resto do tempo
SDL_Delay(delay - t2);
}
t1 = SDL_GetTicks();
SDL_Flip(screen);
}

Note a sequencia: eventos, lógica, espera e SDL_Flip.
A tela é atualizada com SDL_Flip() que entra na contagem como sendo do próximo frame. Faço isso para não perder nenhum milissegundo.

.:: Trabalhando com tempo ::.
O tempo é algo que não pode ser simulado em uma só interação ! E caso você não concorde comigo eu explico melhor.
Eu posso percorrer todos os valores de uma variável em um loop e gerar meu resultado de uma só vez. Mas se minha variável for um valor de tempo seria inútil fazer isto. Pois não quero um resultado gerado todo de uma só vez... eu não teria movimento !
A regra pra se programar um jogo é "Tudo ao seu tempo". E embora eu já esteja filosofando demais, eu gostaria que você tivesse isto em mente. Tente imaginar este outro ponto de vista, pois é muito importante.

O tempo entre cada frame de um jogo costuma ser constante, mas isso não significa que a animação também deverá ser. Movimento acelerado é um truque escondido na manga, e pode dar uma impressão realista de movimento como se fosse mágica.

As funções seno e cosseno podem ajudar muito neste momento:

void logica(int t1){
SDL_Rect ret, xy;
float f;

// Limpando a tela e criando o piso.
SDL_FillRect(screen, NULL, 0x000000);
ret = (SDL_Rect){0, 300+img->h, 640, 32};
SDL_FillRect(screen, &ret, 0x008000);

// Usando o tempo para encontrar um valor para o deslocamento x
f = cos(t1/1000.0);
xy.x = f*250.0 + 304;

// Usando o tempo para encontrar um valor positivo para o deslocamento y
f = cos(t1/750.0);
if(f<0) f = -f;
xy.y = -f*200.0 + 300;

SDL_BlitSurface(img,0,screen,&xy);
}

Veja que nosso smiley quica sobre o piso como uma bola de borracha e muda de direção como se existisse uma força agindo sobre sua massa:
Imagem de todas as posições assumidas por nosso smiley.

Usar tempo em um jogo é um pouco mais complexo.
Nosso smiley não estaria pulando o tempo todo. Ele estaria em repouso e quando fosse dado o comando para pular faríamos:
» Mudar o estado para pulando.
» Pegar o startTicks. Que é o instante em que foi dado o comando.
» Deslocar nosso smiley em função do tempo. SDL_GetTicks() - startTicks.
» Mudar o estado de volta para repouso quando ele estiver em terra firme.

.:: Controle de tempo ::.
Acho que agora você já entendeu o porque de usar tempo e já deve estar pensando de forma diferente. Mas para que você possa fazer isso sem traumas é preciso de uma classe assistente.
O que aconteceria se você tivesse que pausar o jogo ? Como fazer isso sem ter que tratar o tempo pausado em todos os seus objetos e personagens animados ?! Para isso iremos trabalhar com um objeto timer... o nosso cronometro.
Assim como uma música, que você pode tocar(start), pausar(pause) e parar(stop) é o funcionamento do Timer.
Este timer foi totalmente baseada na classe Timer do Lazy Foo'.

A classe Timer:

class Timer {
private:
int startTicks; // Tempo quando o Timer recebeu start().
int pausedTicks; // Tempo passado quando o Timer recebeu stop().

bool paused; // Ativo quando o Timer estiver em pausa.
bool started; // Ativo quando o Timer estiver ativo.
public:
Timer(); // Construtor
~Timer(); // Destrutor

void start(); // Iniciar a contagem do timer.
void stop(); // Parar a contagem.
void pause(); // Pausar a contagem.
void unPause(); // Continuar contagem.
int getTicks(); // Informa o tempo contado.
bool isStarted(); // Verifica se o Timer está rodando.
bool isPaused(); // Verifica se o Timer está pausado.
};


A classe timer trabalha internamente com SDL_GetTicks(), mas apenas quando chamamos por funções como: start(), pause() e getTicks(). Ela usa os atributos startTicks e pausedTicks para se localizar no tempo e não depende de alimentação por interações.

Você irá encontrar três bons exemplos de tudo que foi comentado aqui no arquivo "código fonte" disponível para download:
Exemplo1: Controle do Frame Rate.
Exemplo2: Um exemplo de animação usando um smiley e funções seno e cosseno.
Exemplo3: Código fonte da classe Timer. Junto com um exemplo e documentação.

.:: Link's ::.
Download: » código fonte
» Advanced Timers (Lazy Foo')

--
Depois de dois meses sem postar, estou aos poucos, retomando meu blog !

Nenhum comentário: