A animação consiste na disposição temporal de uma sequencia de imagens no intuito de causar a ilusão do movimento. Além do processo tradicional na qual os “quadros” são desenhados e coloridos manualmente, existem ainda o stop motion  (registro de objetos físicos) e computação gráfica  entre outros. Veja o filme “Animando” onde Marcos Magalhães utiliza o desenho tradicional e o stop motion para animar o próprio animador:

 

 

Alguns softwares foram elaborados especificamente para a produção e publicação final de animações destinadas aos suportes e meios audiovisuais como o vídeo digital, cinema e TV.
O Toon Boom, por exemplo, oferece ferramentas de desenho vetorial, timeline e suporte para a sincronização de áudio e criação de personagens no estilo cartoon:

 

 

O Processing é um ambiente  de programação  de uso geral cujos recursos incentivam a produção de gráficos, tipografia, interatividade e mesmo animações 2D e 3D.
Em seu ambiente de desenvolvimento não existe uma interface gráfica semelhante à timeline do Toon Boom ou Adobe Flash. Apesar desta “limitação”, veremos que existem funções especiais como o  setup() e draw() que possibilitam o controle do fluxo de execução de um programa, gerando repetições necessárias para a produção de animações ou movimentos diversos.

 

As funções setup() e draw()

Como vimos em tutoriais anteriores, o posicionamento espacial de elementos geométricos, imagens ou textos foi feito por linhas de programação consecutivas. Por exemplo, se desejamos desenhar um quadrado em quatro posições diferentes, devemos repetir a função rect() , variando os parametros x ou y em cada posição desejada:

size (200,200);
background(0);
rect (20,20,10,10); 
rect (40,20,10,10);
rect (60,20,10,10);
rect (80,20,10,10); 

E este seria o resultado visual deste script:

 

Execução sequencial de desenhos

 

Ao executarmos o código, notamos que não houve uma animação. Apesar de variarmos a posição horizontal (20,40,60 e 80) em cada função, conseguimos apenas o desenho do objeto em posições diferentes e ao mesmo tempo! Para conseguirmos a ilusão de movimento precisamos desenhar cada quadrado em instantes diferentes ou em frames diferentes.

Neste caso, a função draw()  torna-se útil, pois ela funciona como uma espécie de looping dentro do qual uma ou mais instruções são repetidas indefinidamente a cada segundo.

No Processing, execute  o código:

int px;

void setup() {
    size (200,200);
    background(0); 
   px=0;
}

void draw()  {
  background(0);
  px++;
  rect (px,20,10,10); 

Apesar de ainda não apresentarmos  um tópico específico sobre funções, basta saber no momento que a escrita de funções segue algumas regras, por exemplo em

void setup( ) {

}

o termo void significa que a função não gera (retorna) um valor específico, como o resultado de uma operação matemática, por exemplo.  O termo setup é o nome da função que deve ser seguido de ( ) , assim como na   função background( ). A diferença aqui é que  os sinais { } determinam o escopo da função, ou seja, quando a função é executada todo o código inserido neste trecho será também executado em bloco.

Antes de explicarmos  o funcionamento deste script, verifique abaixo o resultado:

 


Obs: o sketch (applet gerado no Processing)   foi hospedado no site OpenProcessing e incluído (embed) aqui no blog. Se não conseguir visualizar a animação, faça o download do Java plugin , reinicialize o browser e recarregue a página para recomeçar.

A estrutura inicial do código acima pode ser resumida assim:

void setup( ) {

}

void draw ( ) {

}

Este arranjo está presente na maioria dos sketchs programados no Processing e representam duas funções distintas. Primeiro, todo  o código aninhado dentro das chaves  { }  da função setup( )  são executados somente uma única vez. Então, as linhas

size (200,200);
background(0);
px=0; 

não precisam entrar no looping (repetição), pois são funções iniciais que preparam os atributos gerais do  sketch como dimensão, cor de fundo e inicialização de variáveis.

Já as linhas

  background(0);
  px++;
  rect (px,20,10,10); 

acontecem dentro do escopo   { } da função draw( ), isto é, as três linhas são executadas em bloco, infinitamente. Vamos entender melhor como isso ocorre.

 

A intenção original é gerar o movimento horizontal do quadrado, portanto devemos aumentar os valores da coordenada x (lembre-se que o eixo horizontal corresponde à coordenada x e o vertical à coordenada y). Veja o código:

rect (px,20,10,10);

Perceba que no parâmetro referente à coordenada x utilizamos a variável px! Para que o valor da variável px aumente, devemos incrementá-la constantemente e isto é feito pela linha:

px++;

Como vimos no tutorial sobre expressões aritméticas, escrever px++ equivale à expressão px=px+1. Como esta expressão também está dentro do looping da função draw() , então a variável px será incrementada infinitamente – veja que o quadrado segue para fora da área de desenho, pois sua coordenada x recebe o valor armazenado na variável  px  e que aumenta constantemente.

Finalmente, note que a criação da variável px (declaração) foi escrita fora do escopo das funções setup() ou draw(). Se, por exemplo,  esta variável fosse criada dentro da função setup(), provavelmente o sistema acusaria algum erro, pois a função draw () não “enxergaria”  a variável px  que , neste caso,  é funcional apenas para o escopo de setup( ).

Vamos comentar o código para ficar mais legível:

 

int px;  //declaramos uma variável px do tipo inteiro (integer)                      

void setup() {               // função setup – só é executada uma vez
    size (200,200);      // determina as dimensões do sketch
    background(0);    // determina a cor de fundo do sketch (preto)
   px=0;                         // inicializa a variável px com um valor (zero) posição inicial do quadrado
}

void draw()  {                   // função draw – looping, executada infinitamente
  background(0);            // aplicamos novamente a função background(0)
  px++;                               // incrementamos o valor de px (soma uma unidade a cada repetição)
  rect (px,20,10,10);    //desenhamos um retangulo ( coordenada x=valor atual de px,
// coordenada y valor fixo=20, altura e largura=10) 

 

A ilusão de animação é criada pelo incremento da variável px que substitui a coordenada x na função rect(px,20,10,10). Mas, existe outro truque aqui! Veja que na função draw () existe ainda a chamada de background(0). Se não usamos este recurso não perceberíamos a animação, pois cada quadrado desenhado ficaria permanentemente fixo em sua posição. Para que o efeito de movimento seja completo, devemos desenhar um quadrado, limpar o desenho com background (0) e tornar a desenhar  a mesma figura em outra posição. Este processo está fundamentado na própria síntese da imagem em  movimento, desde a origem do cinema e a exploração de um fenômeno chamado de  persistência retiniana.

No exemplo do código anterior, existe o apagamento total da imagem antes da mesma ser redesenhada pelo looping. Podemos utilizar uma espécie de “suavização” do efeito como nas animações geniais do artista William Kentridge  :

 

 

Nesta animação realizada pelo processo tradicional do desenho,  Kentdridge escreve e apaga em cada quadro, gerando um clima fantasmático  pela sobreposição de camadas  de memórias.

No  meio digital , este efeito pode ser simulado pela modulação da opacidade dos gráficos e imagens. No Processing  não contamos com uma ferramenta de “apagamento” semelhante à borracha do Photoshop. O rastro da animação pode ser produzido pela obstrução gradual dos desenhos, bastando gerar uma sobreposição contínua  de camadas transparentes. Veja o código e o resultado:
int px=0;

void setup( ) {
  background(0);
  size(200,200,P2D);
  smooth();
  noStroke();
  px=0;
}

void draw( ) {
  px++;
  fill(0,5);
  rect(0,0,width,height);
  fill(255,0,0);
  rect(px,100,20,20);
  fill(0,255,0);
  rect(100,px,20,20);
}

(obs: recarregue a página para recomeçar a animação)

 

Neste novo exemplo fizemos algumas modificações. Note que na função draw() não utilizamos mais a função background(0). Para a apagar os quadros, desenhamos um retângulo com a cor de preenchimento igual ao do background, porém a transparência do mesmo é de apenas 5. Veja o código:

 

fill(0,5);                                   // preenchimento preto  e transparência 5
rect(0,0,width,height);   // desenha o  retângulo  com as mesmas dimensões do sketch

 

Logo após, mudamos a cor para vermelho  com fill(255,0,0) para desenhar o primeiro retângulo animado e verde com fill (0,255,0) para o segundo retângulo que se desloca verticalmente (note que  também colocamos a variável px no parametro referente à coordenada y em rect(100,px,10,10) ).

Igual ao exemplo anterior, background(0) poderia ter sido utilizado para uma animação simples. Como planejamos o efeito do “rastro”, optamos pela  de acumulação das  transparências dos quadros que se sobrepõem continuamente  sobre as figuras coloridas desenhadas também no mesmo looping.

Um outro detalhe foi acrescentado  na função size (200,200,P2D). Existe um terceiro parâmetro para a função size() além das dimensões largura e altura:

size (width,height,MODE);

O elemento MODE determina o modo como o Processing  executa os gráficos, podendo assumir os seguintes valores:

JAVA2D (default)
P2D
P3D
OPENGL

Neste exemplo utilizamos o P2D para ganhos na velocidade de processamento (veja maiores detalhes).

 

Alterando a Velocidade

A velocidade em que o quadros (frames) são redesenhados pode ser alterada pela função frameRate( ). Por exemplos, podemos dispor esta função logo no início do sketch, dentro do escopo de setup() :

 

void setup() {

    size (200,200);
    background(0); 
   frameRate(12); // desaceleramos a animação para 12 quadros por segundo
}


O valor default para frameRate()  é de 60 quadros por segundo. O fato  de aumentarmos esse valor não significa  que a animação  seja executada muito mais rápido, pois o fator considera também o poder de processamento da máquina onde o programa está sendo executado. A alteração do valor indica apenas a taxa máxima de quadros que o programa deve tentar desenhar  por unidade de tempo (segundos).

De outra forma, podemos produzir a ilusão de velocidade alterando o código e fazendo com que valores maiores ou menores sejam adicionados à variável responsável pelo posicionamento no espaço das coordenadas. Veja a alteração do exemplo anterior:

 

int px;
int py; 

void setup( ) {
  background(0);
  size(200,200,P2D);
  smooth();
  noStroke();
  px=0;    // determina valor inicial para px
  py=0;
   // determina valor inicial para py
}

void draw( ) {
  px+=2;    // incrementa px com duas unidades
  py+=4;  
 // incrementa py com quatro unidades
  fill(0,5);
  rect(0,0,width,height);
  fill(255,0,0);
  rect(px,100,20,20); // px é utilizada na atualização da posição horizontal
  fill(0,255,0);
  rect(100,py,20,20);  //py é utilizada na atualização da posição vertical
}

Neste exemplo criamos duas variáveis (px e py)  para o controle das velocidades individuais dos quadrados.  Veja o código:

 

px+=2;  // equivale a px=px+2
 py+=4;  // equivale a py=py+4

 

Aqui as variáveis são incrementadas com valores diferentes, logo a velocidade horizontal parece ser menor que a velocidade vertical do quadrado verde. Experimente este código no Processing e teste o resultado.

Finalizando este tópico lembramos que é importante o estudo do uso de variáveis e expressões aritméticas  - examine os tutoriais anteriores. No próximo veremos como tornar as animações menos lineares com o uso das funções randômicas.

Tagged with: