Animações Gráficas com Linguagens de Programação

É bem provável que o primeiro contato com a informática e computação gráfica, hoje, venham dos jogos de videogame. Do primitivo PC XT até chegar as modernas estações SILICON hoje, houve um grande trabalho em todas as etapas da evolução da informática para se obter o máximo com o mínimo de evolução que se obtinha em termos gráficos. Desde os tempos dos jogos de ATARI ninguém ficou esperando muito para que algo melhor surgisse e botaram a mão na massa.

Existe quem tenha tentado fazer animações usando caracteres ASCII, usando um padrão de formatação de textos chamado ANSI. Para quem é do tempo das BBS, deve se lembrar disso. Um exemplo desse tipo de animação pode ser visto em http://www.asciimation.co.nz/.

Entretanto, um outro tipo de segmento na indústria de software, diferentemente dos “artistas” que faziam esse tipo de animação, estava interessado em usar animações em seus programas: o segmento de jogos em computador.

O desafio de se fazer animações no computador, com qualidade, sempre foi um dos mais importantes objetivos para o que hoje se chama de indústria de entretenimento eletrônico. Maior ainda para os programadores de plantão, que geralmente “estufam o peito” e fazem muitas coisas “no braço”. Hoje, jogos como Final Fantasy X (http://na.square-enix.com/games/FFX/) não são nada sem a avançada técnica da animação que o jogo possui.

Bem, sem toda a pretensão de se fazer um Final Fantasy X no seu computador apenas lendo este artigo, mostraremos como fazer uma animação extremamente simples usando a linguagem C e uma biblioteca gráfica disponível na internet.

Linguagem C e Allegro

Para executar a nossa “proeza” de fazer uma animação gráfica, usaremos a linguagem C e sua estrutura. Recomendamos o compilador GCC, disponível em versões para quem usa Linux (http://www.gnu.org/software/gcc/gcc.html), DOS através do DJGPP (http://www.delorie.com), ou Windows através do MingW (http://www.mingw.org) ou Cygwin (http://sourceware.cygnus.com/cygwin/).

O responsável pela “mágica” da animação e quem vai facilitar a nossa vida na programação é a biblioteca gráfica Allegro (http://alleg.sourceforge.net/).

A Allegro é uma biblioteca multimídia desenvolvida para a programação de jogos. Para quem estiver usando DJGPP, basta descompactar o Allegro dentro do diretório do DJGPP com os diretórios padrão e entrar no diretório “…allegro”. Digite “make”. A biblioteca irá se compilar automaticamente. Então digite “make install” para instalar a biblioteca.

A respeito do texto, apesar de utilizarmos a linguagem C como referencial, ele se destina a programadores de outras linguagens com outras bibliotecas gráficas, bastando para adaptar as técnicas apresentadas, observar o código fonte disponível aqui no site e os respectivos comentários a cada função.

Um ponto que nos motivou a utilizar a biblioteca Allegro é que ela é multiplataforma e possui versões para Windows, Macintosh, Linux e outros ambientes, podendo ser utilizada por inúmeros compiladores. Por isso, entendemos que a abrangência desse texto seria maior do que se utilizássemos Delphi ou outra linguagem/ferramenta.

Para aprender a instalar a biblioteca gráfica Allegro em diferentes plataformas, sugerimos que você leia os arquivos “*.txt”, no diretório “…allegrodocsbuild” e veja o arquivo mais apropriado a sua necessidade (DJGPP.txt, MSVC.txt, UNIX.txt, etc…).

O que é uma animação?

Animação é fazer com que coisas dêem a impressão de movimento. No computador, assemelha-se a produção de um desenho animado, onde várias figuras, levemente diferentes, serão sobrepostas a uma velocidade que você não perceba que isto está acontecendo, dando assim a impressão de movimento. Uma animação será tão mais perfeita quanto mais quadros houverem entre os estados inicial e final da animação.

Para a criação de uma animação usando uma linguagem de programação, temos duas formas a seguir, para criarmos nossos “quadros”:

  1. Criar os quadros todos na memória, utilizando-se de rotinas da biblioteca gráfica, como linhas e círculos (para saber mais sobre funções gráficas básicas de alegro, veja o nosso tutorial de allegro na seção Textos e Artigos);
  2. Utilizar imagens criadas/capturadas em outros programas, como Corel Photopaint e Adobe PhotoShop.

Cada uma dessas técnicas tem vantagens e desvantagens. No primeiro caso, conseguiremos uma boa “economia” de memória, porém, criar imagens complexas pode ser uma tarefa realmente complicada. No segundo caso, o quadros criados externamente são inseridos facilmente com as rotinas da biblioteca, entretanto, a alteração dessas imagens se torna realmente difícil por meio do código do programa, de modo que essas alterações pareçam tranparentes.

Utilizando-se estas duas técnicas, uma complementando a outra, podemos elaborar cenários complexos e flexíveis conhecendo apenas algumas poucas funções básicas da biblioteca gráfica, como veremos adiante.

EXEMPLO 1: O programa a seguir demonstra, através de comandos gráficos simples, uma animação de um círculo na tela.

[c]
#include <conio.h> /* Necessário para a função getch() */
#include <allegro.h> /* Cabeçalho da biblioteca */
int main()
{
/* Variável inteira. Importante para o laço for */
int x;
/* Inicializa a biblioteca gráfica */
allegro_init();
/* Autodetecta a placa de vídeo e usa resolução de 640×480 */
set_gfx_mode(GFX_AUTODETECT,640,480,640,480);
/* instala timer. Não é importante agora, serve aqui a função rest() abaixo funcionar */
install_timer();
/* desenha o primeiro círculo , na posição 0,100, com raio 100*/
circlefill (screen, 0, 100, 100, 15);
/* loop para animação através da tela */
for (x = 1; x < 320; x++) {
/* faz pequena pausa para se enxergar a animação*/
rest (10);
/* apaga circulo anterior */
circlefill (screen, x – 1, 100, 100, 0);
/* desenha novo circulo em nova posição */
circlefill (screen, x, 100, 100, 15);
}
/* Aguarda uma tecla ser presionada */
getch();
/* Termina a biblioteca Allegro*/
allegro_exit();
/* termina main() de forma padrão */
return 0;
}
/* Macro do Allegro para que o programa seja compilado corretamente em plataformas diferentes do DOS */
END_OF_MAIN();
[/c]

Flickering

Ao executar o programa, percebemos que existe uma tremulação no círculo. Se não ocorreu nada, experimente aumentar os valores do raio em “circlefill” (é o penúltimo parâmetro) para 150 ou mesmo 200. Se ainda assim nada aconteceu, parabéns! Você tem um supercomputador! Mas nós, meros mortais, teremos esse “problema”. O que aconteceu é o chamado efeito de flickering (tremulação, em inglês).

O efeito flickering acontece porque o desenho do círculo deve ser feito ao mesmo tempo que o monitor atualiza a tela do monitor. Quando desenhamos na memória de vídeo diretamente, como fizemos no exemplo, há uma falta de sincronia entre o que está sendo desenhado e o que se está desenhando na memória de vídeo naquele momento. É exatamente o mesmo efeito que acontece quando tentamos filmar uma televisão em uma câmera de vídeo. Ao ver o filme, percebemos uma tremulação na imagem da televisão filmada. É a falta de sincronia entre as imagens exibidas e as que estão sendo desenhadas no filme.

Double Buffering

Para solucionar o problema, existem várias técnicas de programação. A mais simples e comum consiste na técnica do double buffering. Nessa técnica criamos um buffer onde desenhamos toda a tela antes de enviarmos a tela ao vídeo. Desta forma a tela é atualizada inteiramente e de uma só vez, eliminando o problema do flickering.

O ponto forte dessa técnica é o fato de ser facilmente implementada e funcionar muito bem na grande maioria dos casos, servindo também como ponto de partida para técnicas mais avançadas, como o triple buffering, dirty rectangles, entre outros.

EXEMPLO 2 – ([Download não encontrado.]) O exemplo 2 você poderá baixar na área de download deste site. Consiste no mesmo programa do exemplo 1, com a adição da técnica de double buffering. Adicionamos também, uma animação de uma seqüência de figuras criadas externamente exemplificando assim todas as técnicas e recursos citados acima. Leia atentamente os comentários no código do programa, pois o código mostra o que faz cada função.

É claro que este artigo não esgota de maneira alguma o que se pode falar sobre animação gráfica. Mas conseguimos explicar um pouco a respeito de um dos maiores problemas ao tentar se fazer uma animação gráfica com linguagens de programação. O programa apresentado aqui pode parecer trivial, mas é o fundamento para outras coisas mais interessantes, como programação de jogos. É claro que você pode estar vendo apenas uma bola andando na tela. Eu, ao contrário, vejo a “estrela da morte” se preparando para entrar em ação. Use você também a sua imaginação. O próximo grande jogo/animação da temporada pode ser o seu…

Até a próxima.

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.