Mostrando postagens com marcador delphi. Mostrar todas as postagens
Mostrando postagens com marcador delphi. Mostrar todas as postagens

YAMS - Um clone do Jogo Campo Minado Perdido no Tempo

Este jogo foi originalmente escrito em Delphi 7 no final de 2003 como um exercício de aprendizado da linguagem Object Pascal e da programação de jogos do Windows.

O código-fonte original foi publicado no bom e velho site Delphi Pages, que agora está, infelizmente, morto. Foi um momento de sorte incrível quando encontrei uma versão quase finalizada de seu código-fonte em um CD-ROM de backup empoeirado e agora estou disponibilizando-o novamente no Git Hub.

Ele abriu e foi compilado perfeitamente 21 anos depois, no Delphi 12. Cara, isso é simplesmente incrível!!! 😮

Essa é a cara dele rodando no Windows 11!

Espero que apreciem.


Links

Repositório: https://github.com/fabianosalles/yams

Página de relase: https://github.com/fabianosalles/yams/releases

Executáveis: x86 | x64


Criando um Game Completo - Parte 7


Bem vindo à sétima parte do nosso mini curso Criando um Game Completo, onde criamos uma versão do clássico Space Invaders compatível com Windows e Linux, utilizando aceleração de hardware para gráficos 2D, suporte a joysticks e uma tabela de scores online! Tudo isto compilando em Delphi e Lazarus.

Neste post iremos criar o menu inicial do game, definir a lógica de transição entre as telas e realizar os ajustes necessários na tela de gameplay para que o jogador possa voltar à tela inicial.

Criando um Game Completo - Parte 6.1

Bem vindo à parte 6.1 do nosso mini curso Criando um Game Completo, onde criamos uma versão do clássico Space Invaders compatível com Windows e Linux, utilizando aceleração de hardware para gráficos 2D, suporte a joysticks e uma tabela de scores online! Tudo isto compilando em Delphi e Lazarus.

Neste post, vamos complementar as melhorias introduzidas no texto anterior e criar um efeito de estrelas para deixar o plano de fundo do jogo mais interessante. A imagem ao lado mostra como ficará o visual das animações na cena do gameplay no final do processo.

Criando um Game Completo - Parte 6

Bem vindo à sexta parte do nosso mini curso Criando um Game Completo, onde criamos uma versão do clássico Space Invaders compatível com Windows e Linux, utilizando aceleração de hardware para gráficos 2D, suporte a joysticks e uma tabela de scores online! Tudo isto compilando em Delphi e Lazarus.

Já faz um tempo desde o último artigo da série, um ano, para ser exato e eu gostaria de começar pedindo desculpas por esta ausência tão longa. Estive envolvido com vários projetos simultâneos nestes últimos 12 meses, ministrando muitas aulas e trabalhando com sistemas embarcados, o que foi muito enriquecedor mas acabou me afastando um pouco aqui do blog. Mea culpa, mea culpa...

Bom, explicações dadas, vamos ao que interessa: nosso game!

Refactoring with Class Interceptors and Anonymous Methods

When it comes to database manipulation, there are few tools as convenient and easy to use as Delphi DataSets. Instanciate any TDataset, run a sql query, call the open method and bingo! We have a copy of the records (and a multitude of metadata) in memory, ready to be manipulated.

You may be very familiar with some variation of this code:

procedure Foo;
var
  qry: TSqlQuery;
begin
  qry := TSqlQuery.Create;
  try
    qry.CommandText := 'select some fields from some table';
    qry.Open;
    while not qry.Eof do
    begin
      { some proccessing oncurrent row  }
      qry.Next; 
   end;
   finally
     qry.Close;
     FreeAndNil(qry);
   end;
end;

Simple, isn't?

Yes, but... Well, image maintain a system where there are thousands of functions exactly the same as this, spreaded over hundreds or thousands of units and you will start to feel chills.

Refatoração com Class Interceptors e Métodos Anônimos

Quando assunto é manipulação de bancos de dados, há poucas ferramentas tão práticas e convenientes quanto os DataSets do Delphi. Instacie um dataset qualquer, informe uma consulta sql, chame o método open e bingo! temos uma cópia dos registros do banco (e uma infinidade de metadados) em memória, prontos para serem manipulados.

procedure Foo;
var
  qry: TSqlQuery;
begin
  qry := TSqlQuery.Create;
  try
    qry.CommandText := 'select some fields from some table';
    qry.Open;
    while not qry.Eof do
    begin
      //processa o registro atual..
      //e avança para o próximo
      qry.Next;
   end;
   finally
     qry.Close;
     FreeAndNil(qry);
   end;
end;

Prático e simples, não é?

Pois bem! Image dar manutenção em um sistema onde há milhares de funções exatamente iguais a esta, espalhadas por centenas de units e você começará sentir calafrios.

ASCII Art - Convertendo imagens para ASCII

Saída gerada pelo conversor
Um tópico um pouco esquecido, mas ainda interessante, é a criação de ASCII art a partir de imagens e fotos.

Na época em que os consoles de texto eram a mais comum, se não a única opção de interface disponível para os usuários de computador, as pessoas se valiam de muita criatividade (e um bocado de trabalho) para criar imagens usando somente os caracteres disponíveis na tabela ASCII. Muita coisa boa foi e ainda é criada com essa idéia, incluindo jogos e animações.

Depois de assistir a este vídeo, decidi implementar um pequeno conversor ASCII em pascal. O resultado da empreitada pode ser visto na imagem do rei do rock ao lado, que foi criada usando a versão do conversor criada para este artigo.


Animando o Console - Efeito Matrix Parte II

Saída da 2ª animação em modo texto. Bem mais interessante.
No último post criamos um pequeno programa que imprimia zeros e uns em posições aleatórias da tela, criando um efeito muito simples, mas nada parecido com o efeito do console do matrix que estamos querendo escrever.

O objetivo deste primeiro programa era se familiarizar com as funções da API do windows e começar a conhecer o ambiente e suas limitações.

O leitor mais atento poderá ter percebido, entretanto, vários problemas com o programa escrito. O primeiro deles é que não estamos realmente gerando os quadros (frames) de uma animação, estamos simplesmente jogando caracteres na tela da maneira mais displiscente e ineficiente se pode imaginar.

Animando o Console - Efeito Matrix

Saída da 1ª animação em modo texto. Bem vindo à Matrix! :)
Depois de ver o vídeo Armaggedon na postagem anterior, fiquei nostálgico e resolvi brincar um pouco com o console na tentativa de gerar algo graficamente interessante.

E por que alguém faria isto em pleno ano de 2014 com tantas opções e tantas tecnologias capazes de renderizar imagens super realistas em hardwares comuns e baratos? - eu até posso ouvir você perguntando.

Pelo prazer e pelo desafio, ora bolas! Sinceramente, precisa de mais que isto?

Então vamos começar nos familiarizando com o console dentro do Windows. Segundo a documentação da Microsoft

Consoles gerenciam inputs e outpus (I/O) para aplicações em modo caractere (aplicações que não providenciam sua própria interface gráfica).

Armaggedon – Animação em Modo Texto

Yuriy Kotsarenko, conhecido como LifePower, o autor da biblioteca Asphyre Sphinx, em parceiria com Humberto Andrade (aka Hab Soul) e Leon Moorrees (aka LPChip) escreveu um programa que gera animações em tempo real com efeitos 3D e trilha sonora para uma competição na área da computação gráfica.

Nada especial se não fosse por dois detalhes:

  1. tudo isso foi escrito em delphi e 
  2. tudo é gerado na janela de uma aplicação console! 
 Isso mesmo, uma aplicação console! É de cair o queixo a habilidade desses caras.

Confira o resultado no vídeo:



Ainda na dúvida?

Baixe o programa, descompacte-o e execute você mesmo. Em sistemas anteriores ao Windows 7 ainda é possível ver tudo em tela cheia pressionando At + Enter!

Gráficos Higth Tech "Made in Delphi"



Faz um tempo que venho acompanhando o blog de um norueguês chamado Rick, que fala sobre o andamento de um projeto de um jogo chamado Tower22, um jogo de horror pessoal desenvolvido do zero usando o Delphi 7.

Alguns Jogos criados em Delphi


Muita gente, talvez por falta de conhecimento, ou da escassez de títulos comerciais produzidos na ferramenta da CodeGear, duvida da capacidade do Delphi na produção e jogos e simulações de alto desempenho.
É verdade que o C++ impera na indústria, seja nos consoles, nos portáteis, nos celulares (aqui, dividindo o espaço com o Java 2 Mircro Edition, e nos desktops mas, isto se deve muito mais à onipresença de bons compiladores C/C++ nos dispositivos eletrônicos que à (inegável) qualidade técnica da liguagem. Mas, ora! O que o há no C++ que nós não temos em object-pascal? Temos suporte a sobrecarga de operadores, suporte a programação genérica, RTTI, Unicode, integração transparente com Win32 API, DirectX, OpenGL, SDL, acesso dierto ao hardware via inline assembly, compilação para Mac, Linux, GameBoy, XBox (via XNA e crhome) e, finalmente, com a versão 2010, teremos compilação nativa para chips de 64bits!
Todos os jogos dos vídeos foram feitos por hobistas utilizando delphi e/ou free pascal. Alguém ainda duvida?

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

No artigo anterior, criamos uma animação procedural, onde com um algoritmo simples, um padrão de cores foi gerado. Neste artigo vamos criar um efeito sobre uma imagem já existente utilizando um conceito simples de transparência.

Opacidade

Segundo o Wikipedia:
A opacidade é uma propriedade óptica da matéria. Um material é considerado opaco quando não permite a passagem da luz em quantidades apreciáveis. Geralmente, diz-se que um material é opaco quando bloqueia a passagem da luz visível. (http://pt.wikipedia.org/wiki/Opacidade)

Para nossos estudos, vamos definir opacidade como sendo o nível de transparência de uma imagem cujo valor será representado por um byte, o que nos dá uma faixa de 256 valores possíveis (0..255) onde 0 define uma imagem completamente transparente e 255 uma imagem completamente opaca.
Vamos criar um descendente de TBitmap onde adicionaremos esta propriedade.


TSprite = class(TBitmap)
private
   fOpacity: byte;
   procedure SetOpacity(const Value: byte);
published
   property Opacity: byte read FOpacity write SetOpacity; 
end;

Agora vamos instanciar nosso sprite na inicialização do programa (lembrando que estamos modificando o código do template cirado nos artigos anteriores)

procedure TForm1.FormCreate(Sender: TObject);
begin
   ReportMemoryLeaksOnShutdown := True;
   ClientWidth   := 800;               
   ClientHeight  := 600;               
   fOffScreen := TBitmap.Create;       
   fOffScreen.PixelFormat := pf24bit;  
   fOffScreen.Width  := ClientWidth;   
   fOffScreen.Height := ClientHeight;  
   fOffScreen.Canvas.Font.Color := clGray;
   fOffScreen.Canvas.Font.Size  := 9;

   sprite := TSprite.Create;
   sprite.LoadFromFile('img01.bmp');
   sprite.Opacity := MAXBYTE div 2;
   Application.OnIdle := OnIdle;
end;


Windows.AlphaBlend


Existe uma função na API do windows chamada AlphaBlend que serve para exibir imagens que possuem informações de transparência. Vamos utilizá-la para renderizar nosso sprite com 50% de opacidade sobre um background preto (note que já configuramos opacidade como MAXBYTE div 2).
A primeira coisa que precisamos para usar o Windows.AlphaBlend é configurar um registro do tipo TBlendFunction (Veja a descrição oficial deste struct no MSDN).

procedure TForm1.DrawToBuffer;
var
  Canvas : TCanvas;
  Blendf : TBlendFunction;
begin
  Canvas := fOffScreen.Canvas;
  Canvas.Brush.Color := clBlack;
  Canvas.FillRect(fOffScreen.Canvas.ClipRect);

  Blendf.BlendOp    := AC_SRC_OVER;
  Blendf.BlendFlags := 0;
  Blendf.SourceConstantAlpha := sprite.Opacity;
  Blendf.AlphaFormat := AC_SRC_OVER;
end;

O parâmetro BlendOp representa a opração de mesclagem a executar. Como queremos copiar nosso sprite “por cima” da imagem de fundo, setamos BlendOP com a constante AC_SRC_OVER.
O parâmatro BlendFlags deve ser sempre zero.
SourceConstantAlpha define um valor de transparência a ser aplicado na imagem de origem. Aqui, simplesmente passamos o valor da opacidade de nosso sprite.
Por fim, AlphaFormat deve ser AC_SRC_OVER para que o Windows não tente buscar informações de transparência nos pixels da imagem de origem, aplicando em vez disto, o valor de SourceConstantAlpha.
Pronto. Com nosso TBlendFunction configurado, podemos desenhar o sprite semi-transparente. Complemente o método DrawToBuffer conforme a listagem abaixo e execute o programa para ver o resultado.

procedure TForm1.DrawToBuffer;
var
   Canvas : TCanvas;
   Blendf : TBlendFunction;
begin
   Canvas := fOffScreen.Canvas;
   Canvas.Brush.Color := clBlack;
   Canvas.FillRect(fOffScreen.Canvas.ClipRect);

   Blendf.BlendOp    := AC_SRC_OVER;
   Blendf.BlendFlags := 0;
   Blendf.SourceConstantAlpha := sprite.Opacity;
   Blendf.AlphaFormat := AC_SRC_OVER;

   Windows.AlphaBlend(
      Canvas.Handle,  {manipulador da imagem de destino}
      fOffScreen.Width - sprite.Width, {coordenada X}
      0,                               {coordenada Y}
      sprite.Width,                    {largura da área de desenho}
      sprite.Height,                   {altura da área de desenho} 

      sprite.Canvas.Handle, {manipulador da imagem de origem}
      0,                    {coordenada X}
      0,                    {coordenada Y}
      sprite.Width,         {largura da imagem de origem}
      sprite.Height,        {altura da imagem de origem}

      Blendf                 {parâmetros de executação(TBlendFunction)}
   );

   Canvas.TextOut(10, 10, Format('Opacidade : %d', [sprite.Opacity]));
end;
Se tudo estiver certo, você vai ver uma imagem no canto esquerdo da tela de seu programa. A imagem parece muito escura porque metade a luminosidade foi misturada com o fundo negro. Altere o valor de opacidade do sprite para chegar a resultados diferentes.


Fade In e Fade Out

Uma aplicação muito comum da opacidade em imagens é a criação dos efeitos Fade In e Fade Out. Em um (fade in) uma imagem vai surgindo aos poucos até ficar completamente visível, no outro (fade out) a imagem vai se apagando até sumir completamente. Com o que já temos até aqui, é realmente muito fácil implementar estes efeitos: basta ir alterando o valor da propriedade Opacity ao longo do tempo. Vejamos como ficará o método UpdateAnimation.

procedure TForm1.UpdateAnimation;
begin
  if OpIncreasing then
     sprite.Opacity := sprite.Opacity + 1
  else
     sprite.Opacity := sprite.Opacity - 1;

  case sprite.Opacity of
    0   : OpIncreasing := true;
    255 : OpIncreasing := false;
  end;
end;

Execute o programa e veja os efeitos em funcionamento. Para conseguir novos efeitos, altere os valores dos incrementos e decrementos em cada iteração.
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"

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

No artigo anterior, configuramos um formulário padrão da VCL para que pudéssemos exibir animações em sua superfície. Criamos também uma bola, que podíamos manipular pelas setas do teclado e fazê-la se mover pela tela. Nesta parte, vamos falar um pouco de teoria e criar nossa primeira animação procedural.

Um pouco de teoria

O sistema ocular humano é limitado quanto ao espectro visível de luz e quanto à velocidade de interpretação das imagens. O cérebro, porém, é muito bom no trabalho de contornar estas limitações e nos fazer enxergar imagens corretas, nítidas e sem quebras. Mas este trabalho de interpretação está sujeito a falhas e são essas falhas de interpretação que causam as famosas ilusões de ótica. A animação por computador é um tipo de ilusão projetada para causar ao o observador a impressão de movimento.

Desde o surgimento da animação, o seu fundamento é o mesmo: exibir uma seqüência de imagens estáticas, ligeiramente diferentes entre si, numa a uma velocidade superior a 12 imagens por segundo. Quando isto acontece, o olho envia as imagens paradas ao cérebro, mas este acha que se trata de um objeto animado e une-as, criando os pedaços que faltam para que se tenha a impressão de se estar vendo um objeto animado. Muito embora, não pareça haver um consenso entre os cientistas sobre quantos quadros por segundo são necessários para que a “ilusão da animação” aconteça, 12 quadros parecem ser suficientes para que a maioria dos seres humanos visualize-os como uma animação.

A TV, o cinema, o computador, os desenhos animados e os jogos eletrônicos são todos construídos em cima desta idéia. O sistema PAL-M usado no Brasil usa uma taxa de 30fps (do inglês Frames per Second, literalmente quadros por segundo, é a unidade medida padrão usada na indústria e, portanto iremos utilizá-la de agora em diante), filmes em DVD de boa qualidade usam de 24 a 30fps e, na animação de jogos para computador, uma taxa ideal de 60fps vem se estabelecendo ao longo dos anos.

Quando digo “taxa ideal”, não estou afirmando que é necessário alcançar os 60fps ou que seja errado ultrapassá-los. Na realidade, quanto mais quadros por segundo você puder exibir, mais convincente e suave será sua animação, então, daqui pra frente, vamos escrever nossos programas para que alcancem a maior taxa de fps que conseguirmos.

A composição das cores


Como sabemos, tudo que enxergamos é luz. Os objetos que vemos são, na realidade, luz refletida e/ou emitida por suas superfícies. Sabemos também que a partir da mistura de umas poucas cores primárias, todas as outras cores podem ser obtidas. Pois bem, nos monitores de computador, todas as cores são formadas a partir da combinação de apenas três cores: o vermelho (red), o verde (green) e o azul (blue), o famoso padrão RGB.

Neste formato, utilizado exclusivamente por dispositivos emissores de luz, quando todos os três componentes RGB estão em 0% de sua intensidade obtém-se a cor preta e, no caso inverso, quando todos os três componentes estão em 100%, obtém-se a cor branca. Todas as outras cores são obtidas ao se combinar porções distintas de cada um destes componentes.

Imagens, bitmaps e Pixels


Como dito antes, toda imagem digital é um conjunto de pixels. Pra ser mais preciso, podemos defini-las como sendo uma matriz de pixels que é mapeada, pixel a pixel, para um dispositivo de saída (monitor, plotter, impressora, etc).

Nos primórdios da computação, quando os monitores eram monocromáticos, as imagens eram simplesmente uma seqüência de bits que dizia se um ponto numa determinada coordenada no monitor deveria estar acesa ou apagada. Um mapa de bits, um bitmap.

O Windows utiliza este conceito para representar tudo o que deve aparecer no monitor. Ele sempre mantém uma imagem do tipo bitmap em uma área especial da memória RAM que o sistema de vídeo usa para montar a imagem final na tela. A este espaço dá-se o nome de frame buffer.

O Delphi, através da classe TBitmap, oferece um excelente meio de manipular essas imagens, e o método BitBlit da API do Windows, nos fornece um modo rápido de copiar imagens para o frame buffer.

Um Pouco de Código (finalmente)

Para melhorar um pouco a organização do código escrito no post anterior, vamos criar uma interface IDrawable e fazer com que nosso objeto TBackground a implemente.

IDrawable = interface
  ['{3E41F055-58F6-4990-9C94-AA29FD4D7CF4}']
  function GetVisible: boolean;
  function GetRect : TRect;

  procedure SetVisible(const Value: boolean);
  procedure Draw(Canvas:TCanvas);

  property Rect: TRect read GetRect;
  property Visible: boolean read GetVisible write SetVisible;
end;

A interface é bem intuitiva e dispensa maiores explicações. Ela irá definir o “contrato” para todos os objetos renderizáveis no nosso programa. Agora segue nova definição de TBackground adaptado para implementar IDrawable.

TBackgroung = class(TInterfacedObject, IDrawable)
private
  fStep: byte;
  fSize: uCommon.TSize;
  fVisible: boolean;
  function GetVisible: boolean;
  function GetRect : TRect;
  procedure SetVisible(const Value: boolean);
public
  constructor Create(pWidth: integer=640; pHeight:integer=480);
  destructor Free;
published
  {IDrawable}
  property Rect: TRect read GetRect;
  property Visible: boolean read GetVisible write SetVisible;

  {TBackgroung}
  property Size : uCommon.TSize read fSize;
  property Step : byte read fStep write fStep;
end;

Além das propriedades e métodos da interface, criamos duas outras, Size e Step, que irão nos auxiliar no controle da renderização e no controle da animação. Vamos criar uma série de quadros vermelhos que irão mudar de cor de acordo com o valor da propriedade Step. Para isto, vamos implementar o método Draw.


procedure TBackgroung.Draw(Canvas: TCanvas);
const
  SquareW    = 40; {largura de cada bloco}
  SquareH    = 40; {altura de cada bloco}
  SquaresC   = 640 div SquareW + 1; {colunas}
  SquaresR   = 480 div SquareH + 1; {linhas}
var
  col, row, i : integer;
  r : TRect;
begin
  if fVisible then begin
  for col:=0 to SquaresC-1 do
      for row :=0 to SquaresR-1 do begin
          r.Left   := Col * SquareW;
          r.Top    := Row * SquareH;
          r.Right  := r.Left + SquareW;
          r.Bottom := r.Top + SquareH;

          Canvas.Brush.Color := col * row + Step;
         Canvas.FillRect(r);
      end;
  end;
end;


O que fazemos aqui é dividir a tela em linhas e colunas de acordo com as contantes SquareW (a largura de cada quadrado) e SquareH (altura de cada quadrado), atribuir a cada um destas “células” uma cor diferente, que calculamos com base na linha, na coluna e no valor da propriedade Step para finalmente, desenharmos uma a uma as “células” no Canvas.

Só nos resta agora “animar” a cena alterando a propriedade Step após cada renderização. Faremos isto no nosso loop principal através do método UpdateAnimation.

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

E agora, a implementação de UpdateAnimation.

procedure TForm1.UpdateAnimation;
begin
  BackGnd.Step := BackGnd.Step + 1;
end;

Pronto! Nosso background animado está concluído. Execute o código para ver o padrão vermelho mudando de tom e experimente brincar com as constantes do método Draw para chegar a outros resultados. Observe que a classe TBall também foi alterada para implementar a interface IDrawable e que uma outra interface (IMoveable) foi criada para definir os objetos capazes de se mover pela tela.

Downloads

Links

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

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"

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

O Delphi facilita muito a construção de interfaces gráficas. Arrastando componentes da barra de ferramentas e alterando suas propriedades pelo “object inspector”, é muito fácil construir uma interface consistente para a maioria das aplicações.

Esta abordagem, porém, não pode ser utilizada para construir uma superfície de desenho adequada para a exibição de animações que exijam uma taxa de atualização aceitável (pelo menos 20 quadros por segundo). Para isso, a solução mais óbvia seria utilizar uma API ou framework projetado especificamente para este fim (como OpenGL ou DirectX).

 Existem, porém, dois problemas a ser vencidos para adotar um framework destes. O primeiro problema é que, caso não haja um hardware gráfico moderno disponível na máquina onde a aplicação irá ser executada, nenhuma aceleração de será provida (leia-se: todo o processamento gráfico será feito por software). O segundo problema é a curva de aprendizado, consideravelmente longa, que o programador terá de vencer para conseguir aproveitar os recursos que elas possam oferecer.

Então, qual biblioteca utilizar? Não dá pra fazer isso com a própria VCL?

Sim. Dá sim. Mas algumas otimizações precisam ser feitas. E é sobre estas otimizações que vamos falar.

Double Buffer ou Backbuffer

O TCanvas representa uma superfície de desenho que pode ser entendida e tratada como um array de pixels ou um buffer de pixels. O Windows mapeia este buffer para a memória de vídeo e então a placa de vídeo trata de interpretar esta informação e convertê-la em uma imagem visível no monitor.

Se desenharmos direto num TCanvas de um objeto visível, o desenho será imediatamente atualizado na tela. Até aí, tudo bem. Desde que sejam desenhados um ou dois objetos por vez, tudo irá ser atualizado rápido o suficiente para que a transição não seja facilmente percebida.

Ponha um TTimer num form, configure a propriedade interval para 20, carregue uma imagem qualquer num TImage e ponha o seguinte código no evento OnTimer:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Canvas.Brush.Color := clWhite;
  Canvas.FillRect(Canvas.ClipRect);
  Canvas.Draw(X, Y, Image1.Picture.Graphic);
  Inc(X);
  Inc(Y);
  if X >= ClientWidth - Image1.Width then
     X := 0;
  if Y >= ClientHeight - Image1.Height then
     Y := 0;
end;

Ao executar este programa, você verá sua imagem sendo redesenha aproximadamente 50 vezes por segundo (1000/20=50), o suficiente para causar a impressão de movimento, mas a imagem fica piscando constantemente e o resultado é realmente muito feio.

A idéia geral do buffer duplo (Double buffer) é ter uma segunda área de desenho que fique sempre invisível. Daí, em vez de desenhar direto na tela, todos os desenhos seriam gerados neste buffer e, somente depois de pronto, copiaríamos o desenho para a área visível da tela, numa única operação.

A primeira impressão que essa idéia passa é de que, como estaremos percorrendo um caminho maior, as coisas ficarão mais lentas. Mas o fato é que, quando desenhamos num TCanvas visível, a operação demora mais porque, além do tempo gasto com o desenho em si, o programa tem que esperar a memória de vídeo ser atualizada para que o as mudanças apareçam na tela e o programa possa seguir. Quando desenhamos na memória (num TCanvas não visível) este atraso não existe, logo, a operação é concluída muito mais rápido. Além disso, quando vamos exibir a imagem na tela, ela já está pronta e somente uma operação de desenho será realizada nesta “área lenta”, o que contribui para que a animação tenha uma aparência mais agradável.
Vejamos como implementar esta idéia em Delphi.

Primeiro, declare uma variável privada do tipo TBitmap para ser o nosso “back buffer” e trate a inicialização e destruição deste objeto:

private
  { Private declarations }
  fOffScreen: TBitmap;

(...)

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(fOffScreen);
end;

(...)

procedure TForm1.FormCreate(Sender: TObject);
begin
  X := 0;
  Y := 0;
  fOffScreen := TBitmap.Create;
  fOffScreen.PixelFormat := pf24bit;
  fOffScreen.Width := ClientWidth;
  fOffScreen.Height := ClientHeight;
end;

Agora vamos desenhar na memória e, quando concluído o desenho, vamos transferi-lo para a “área visível” da tela.

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  fOffScreen.Canvas.Brush.Color := clWhite;
  fOffScreen.Canvas.FillRect(fOffScreen.Canvas.ClipRect);
  fOffScreen.Canvas.Draw(X, Y, Image1.Picture.Graphic);

  BitBlt(Canvas.Handle, 0, 0,
         ClientWidth,
         ClientHeight,
         fOffScreen.Canvas.Handle,
         0, 0, SRCCOPY);
  Inc(X);
  Inc(Y);
  if X >= ClientWidth - Image1.Width then
     X := 0;
  if Y >= ClientHeight - Image1.Height then
     Y := 0;
end;

Execute este programa e veja que o problema da imagem piscando (conhecido como flickering) foi resolvido.

Eliminando interferências do Windows

Outra otimização mais sutil, mas não menos importante, é o tratamento adequado da mensagem WM_ERASEBKGND que o Windows envia ao programa quando ele acha que alguma área da janela deve ser atualizada.

O Delphi, por padrão, em resposta a esta mensagem irá invalidar toda a área cliente da janela, forçando um redesenho completo. Este comportamento faz sentido quando deixamos que o Delphi cuide sozinho do desenho e da atualização da janela, mas no nosso caso, queremos ter controle completo sobre o modo que a janela será exibida.

Vamos criar uma procedure que irá realizar o tratamento desta mensagem. Declare o seguinte procedimento :

procedure WMEraseBg(var Msg: TWMEraseBkgnd); message WM_ERASEBKGND;

E em sua implementação diga ao Windows, que você mesmo irá tratar do redesenho da janela, setando a mensagem MSG para 0:

procedure TfrmCGScreen.WMEraseBg(var Msg: TWMEraseBkgnd);
begin
  Msg.Result := 0;
end;

Um template para animações

Com estas duas otimizações implementadas, já dá pra fazer pequenas animações executarem de maneira eficiente. Mas ainda há espaço para outras otimizações (que trataremos no próximo post), como executar o programa em full screen, substituir o TTimer por funções mais precisas como QueryPerformaceCounter e utilizar o evento OnIdle do TApplication para realizar os desenhos da animação.

Neste link, você pode baixar os fontes de um projeto que implementa todas estas otimizações e utilizá-lo como template para seus próprios projetos. Faça o download do arquivo, abra-o no Delphi, clique com um botão direito em cima do form e escolha a opção “Add to Repository”. Pronto. Este template estará disponível no “Object Repository”!

Para usar o template, clique no menu File > New > Other, escolha o formulário adicionado anteriormente, marque a opção “inherit” e sobrescreva o método DrawScene para criar suas animações.

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"

Sistemas de partículas

Segundo a dinâmica das partículas, um sistema de partículas é o conjunto de leis físicas que regem um conjunto de n partículas partindo de um centro de massa ou emissor. Muitos fenômenos podem ser modelados como sistemas de partículas. A chuva (cada gota é uma partícula e o emissor é o conjunto de nuvens de onde as gotas vêm), a neve (cada floco uma partícula e o emissor, o ponto de condensação/cristalização do vapor de água) o fogo (cada chama é uma partícula e o emissor é o material sendo queimado), todos estes podem ser descritos como tal.

Um sistema simples utilizando vetores bidimensionais para calcular a o deslocamento das partículas pode ser facilmente modelado no Delphi e exibido num TCanvas padrão. Neste sistema (veja os links abaixo) é possível manipular a força do vento, a força da gravidade e o centro de massa de maneira interativa e ver em tempo real o resultado na tela.
Este sistema de exemplo se distancia da realidade física por não calcular desvios de rota resultantes de colisões entre partículas e implementar somente duas grandezas vetoriais (a gravidade e o vento) quando no mundo real o número de grandezas envolvidas num sistema de partículas é muito maior. Além disso, cada partícula percorre uma trajetória linear, o que nem sempre é condizente com o sistema que se está tentando simular.

Mesmo com todas essas omissões, é possível conseguir efeitos especiais atraentes e leves para serem usados em jogos. Este exemplo faz uso das rotinas gráficas da API do Windows, portanto, não requer nenhum componente ou biblioteca especial para compliar.

Para controlar a gravidade, utilize as teclas "+" e "-" do teclado numérico. Para controlar a direção e a força do vento, use as setas direcionais.

Evoé...


Código fonte
Executável