Timming no Allegro

Por Daniel Loureiro

Uma das primeiras dúvidas que surge com a programação de jogos é com a temporização (“timming”). Coisas do tipo: “gostaria que o lutador gordo fosse um pouco mais lento que o oponente”, “quero uma contagem regressiva numa velocidade, meu jogador em outra, e o boomerangue dele em outra”, entre outras.

De fato, quando o jogo começa a ficar complexo, é comum termos elementos que executam em velocidades diferentes. Em programação isto é obtido executando trechos de código em velocidades diferentes: o código responsável por atualizar a contagem regressiva deve ser executado em uma velocidade (1 segundo, por exemplo), o código que modifica a animação do personagem em outra, e assim por diante.

O Allegro provê rotinas que facilitam esta tarefa. A primeira coisa que o programador deve fazer, é colocar cada um dos trechos de código em uma função diferente. Desta forma, temos uma função para atualizar a contagem regressiva, outra para movimentar o personagem, outra para animá-lo, etc.

Depois, deve-se chamar uma função específica do Allegro e passar 2 parâmetros: um é a função que desejamos que seja executada em determinada velocidade, o outro é o período entre uma execução e outra (em milésimos de segundo). Assim, passar a função “Mova_Personagem” e o valor 50, fará que “Mova_Personagem” seja executada a cada 50 ms, ou a 20 vezes por segundo (1000/50).

Você pode querer usar as macros do Allegro que convertem segundos para ms, execuções por segundo para ms, execuções por minuto para ms, e até mesmo (para os que gostam de padronizar) ms para ms (!!!). Entre as funções básicas de timing, existem mais 2: uma para a execução automática de uma função e a outra inicializa as funções de timing do Allegro.

Existem também outras funções relacionadas com o timing: algumas inúteis, que são idênticas às tradicionais, porém com outros nomes; outras para sincronização vertical (para melhorar a qualidade gráfica); e outras de uso geral (como uma nova versão para o famoso “delay”).

Abaixo, veremos as 3 funções mais básicas:

  • “int install_int(void (*proc)(), int speed)”. Esta é a função principal, e serve para criar um “timer” (função que será executada automaticamente). Ela põe a função passada pelo 1o parâmetro na lista de funções a serem executadas automaticamente pelo Allegro. Note que “speed” é o período em que a função será executada automaticamente. Se a função já estiver na lista, ela apenas modifica o período dado por “speed”. Obs.: A função passada por parâmetro não pode retornar algo, nem ter parâmetros, isto é, tem que ser algo como: “void Funcao(void)”.
  • “void remove_int(void (*proc)())”. Retira a função da lista (somente se ela existir), parando a execução automática dela.
  • “int install_timer(void)”. Inicializa as funções de timing do Allegro. Já que a função “install_int” chama ela se o programador ainda não o fez, seu uso não é obrigatório, mas recomendado por motivos de padronização.

Para converter para os milésimos usados por “speed”, você pode usar as seguintes macros:

MACRO CONVERTE O MESMO QUE
SECS_TO_TIMER(msegs) Segundos para ms 1000 * msegs
MSEC_TO_TIMER(mseg) Ms para ms mseg
BPS_TO_TIMER (bps) Execuções por segundos para ms 1000/bps
BPM_TO_TIMER (bpm) Execuções por minutos para ms 60000/bpm

Devido à forma com que o Allegro gerencia os “timers”, devemos tomar uma série de medidas em nosso programa:

  1. As variáveis globais que serão alteradas dentro de um “timer” devem ser declaradas com a palavra reservada “volatile”. Volatile, se você não se lembra, faz com que o compilador saiba que determinada variavel “volatile” pode ser alterada por “algo” que não seja exatamente algum código no programa principal.
  2. Após a declaração de um “timer”, devemos usar a macro END_OF_FUNCTION(Nome_da_Funcao_Timer)”.
  3. Todas variáveis globais acessadas (lidas e/ou escritas) nos “timers” devem ser trancadas com a macro “LOCK_VARIABLE(Nome_De_Uma_Variavel)”.
  4. Todas as funções de “timer” devem ser trancadas com “LOCK_FUNCTION(Nome_da_Funcao_Timer)”.
  5. Os “timers” não devem acessar o disco nem outras funções do sistema.
  6. Deve-se fazer os “timers” o mais simples possível.

Exemplo básico:

[c]

volatile int x=0; //Variável glogal modificada em um timer

void Anda(void) { //Timer
x++;
}

END_OF FUNCTION(Anda); //Necessário após a declaração de um timer

int main(void) { //Nosso programa

install_timer(); //Opcional, porém mais legível
LOCK_FUNCTION(Anda); //Obrigatório trancar os timers usados
LOCK_VARIABLE(x); //Obrigatório trancar as variáveis globais usadas nos timers
install_int(Anda, BPS_TO_TIMER(10)); //"Anda" deve ser executado 10 vezes por segundos
while(!Fim_Jogo) {} //Lógica do jogo
return(1);
}
[/c]

Para finalizar, devo destacar que o Allegro permite apenas 16 “timers”. Como as funções de animações FLI, mouse, interface GUI, e música MIDI necessitam de “timers”, se utilizarmos alguns destes recursos, a quantidade de “timers” do usuário fica ainda menor.