Animações em tempo real com a VCL - Parte II

No post anterior, fizemos uma introdução ao conceito de “Double buffer” e realizamos o tratamento da mensagem WM_ERASEBKGND para ajudar a otimizar um formulário VCL com o intuito de exibir animações de qualidade nele. Neste post, iremos finalizar estas otimizações.

Usando o evento OnIdle

O objeto Application está presente em todos os programas GUI do Delphi e é utilizado para simplificar a interação com a API do Windows. De fato, o objeto Application encapsula todas as chamadas à API necessárias para inicializar, registrar e manipular janelas, além de publicar alguns eventos globais.

O evento TApplication.OnIdle é um desses eventos. Ele é disparado sempre que o programa fica ocioso, ou seja, sempre que não houver mais nenhuma instrução a ser executada, o evento OnIdle será chamado. Isso o torna especialmente útil quando queremos realizar uma tarefa em segundo plano sem utilizar threads, ou quando queremos que uma determinada função seja executada repetidamente sem afetar o desempenho do programa.

Dois passos são necessários para utilizarmos o evento OnIdle.

Primeiro declare um procedimento privado em seu formulário e implemente-o como o código a seguir:

procedure OnIdle(Sender: TObject; var Done: Boolean);
begin
 Done := false;
end;

Note que não importa o nome do que você der ao método. O que importa é que a lista de parâmetros seja idêntica à do exemplo para que a assinatura do método seja compatível com a definição do evento Application.OnIdle para que, dessa forma, possamos associar nosso método ao evento.

Para finalizar, ponha o trecho de código abaixo no OnCreate do formulário e estaremos prontos para realizar nosso processamento neste evento.

procedure TfrmCGScreen.FormCreate(Sender: TObject);
begin
 Application.OnIdle := OnIdle;
end;

O loop principal (main loop)

Iremos utilizar o OnIdle para nosso loop principal.

O loop principal de uma aplicação de computação gráfica pode assumir várias formas, mas todas elas implementam variações da mesma lógica:

1. INICIO
2. ENQUANTO(ESTADO  <>  SAIR)
  2.1. PROCESSA OS COMANDOS DO USUÁRIO;
  2.2. ATUALIZA DO ESTADO DA ANIMAÇÃO;
  2.3. DESENHA A CENA NO BUFFER;
  2.4. EXIBE A CENA NO MONITOR;
3. FIM;

Vamos criar então os métodos equivalentes. Declare na seção privada do formulário os métodos a seguir e pressione Ctl + Shit + C para que o Delphi crie a implementação de todos:


procedure ProcessInput;    {passo 2.1}
procedure UpdateAnimation; {passo 2.2}
procedure DrawToBuffer;    {2.3}
procedure Blit;            {2.4}

Agora implemente o método OnIdle para que a estrutura do nosso algoritmo fique completa.

procedure TForm1.OnIdle(Sender: TObject; var Done: Boolean);
begin
 ProcessInput;
 UpdateAnimation;
 DrawToBuffer;
 Blit;
end;

Se você compilar e executar o programa agora, você verá uma janela do Windows vazia. Nada muito empolgante até agora: temos a estrutura geral pronta, mas nada pra exibir.

Vamos criar um objeto to TBall (adicione o arquivo uBall.pas ao seu projeto) no centro da tela. Declare uma variável pública chamada Ball e modifique o evento OnCreate do formulário para:

procedure TForm1.FormCreate(Sender: TObject);
begin
 ReportMemoryLeaksOnShutdown := True;

 {teremos uma tela de 800 x 600 pixels}
 ClientWidth := 800;              
 ClientHeight := 600;

 {Criamos nosso bitmap de buffer}
 fOffScreen := TBitmap.Create; 

 {iremos trabalhar com cores de 24bit}
 fOffScreen.PixelFormat := pf24bit;

 {configura as dimensões do buffer de acordo com as dimensões do formulário}
 fOffScreen.Width := ClientWidth;
 fOffScreen.Height := ClientHeight;
 Ball := TBall.Create;
 Ball.Position:= Point(ClientWidth div 2, ClientHeight div 2);
 Ball.Color := clWhite;
 Ball.Diameter := 30;
 Application.OnIdle := OnIdle;
end;

Agora é preciso exibir o objeto. Vamos implementar os métodos DrawToBuffer e Blit:

procedure TForm1.DrawToBuffer;
var
 Canvas : TCanvas;
begin
 Canvas := fOffScreen.Canvas;

 {Primeiro, limpamos o buffer com um fundo preto...}
 Canvas.Brush.Color := clBlack;

 {Agora desenhamos a bola}
 Canvas.FillRect(fOffScreen.Canvas.ClipRect);
 Ball.Draw(Canvas);
end;

procedure TForm1.Blit;
begin
 BitBlt(Canvas.Handle, 0, 0, ClientWidth, ClientHeight,
        fOffScreen.Canvas.Handle, 0, 0, SRCCOPY);
end;

Com isto temos, finalmente, nosso framework funcionando. Tudo bem que ele não faz mais nada além de desenhar uma bola no meio da tela, mas o “trabalho sujo” de configuração do ambiente foi realizado.
Para finalizar este post, vamos movimentar um pouco a bola pela tela usando as setas do teclado.

Implemente o método ProccessInput usando a função GetKeyState como na listagem abaixo. Note que os botões + e - irão manipular a velocidade em que a bola se movimenta.

procedure TForm1.ProcessInput;
begin
 if GetKeyState(VK_UP) then
    Ball.Move(0,-1);

 if GetKeyState(VK_DOWN) then
    Ball.Move(0, 1);

 if GetKeyState(VK_LEFT) then
    Ball.Move(-1, 0);

 if GetKeyState(VK_RIGHT) then
    Ball.Move(1, 0);

 if GetKeyState(VK_SUBTRACT) then
    Ball.Speed := Ball.Speed - 0.1;

 if GetKeyState(VK_ADD) then
    Ball.Speed := Ball.Speed + 0.1;
end;

Compile o projeto e experimente um pouco. Note que a bola não ultrapassa os limites da tela e que a classe TBall é responsável por este tratamento.

No próximo post, iremos adicionar um fundo com animação iremos expandir a classe TBall para trabalhar com sprites animados.

Até o próximo post.

Downloads

Links

Links para as postagens que fazem parte do mini curso "Animações em Tempo real com a VCL"