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 objetoApplication
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 oOnIdle
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.