Em vez do prefácio
No meu recente artigo do
Lazarus - escrevendo um componente para animação de sprite, descrevi o processo de criação de um componente
TImageFragment simples que permite exibir um determinado fragmento de uma imagem.
Continuando o tópico selecionado, neste artigo, quero mostrar como é fácil fazer animações de sprites no ambiente de desenvolvimento do
Lazarus (
site oficial ) usando esse componente.
Com essa abordagem, os quadros de animação individuais em diferentes projeções são colocados na mesma imagem, e o componente para exibir o sprite mostra apenas um fragmento selecionado dessa imagem usando as propriedades
OffsetX e
OffsetY (deslocamento do canto superior esquerdo do fragmento de imagem horizontal e verticalmente).
Muitas dessas imagens prontas podem ser encontradas na Web - por exemplo,
aqui neste site .
Selecione (e prepare) a imagem
Para o meu exemplo, escolhi esta imagem:

- muito expressivamente, este pássaro Phoenix bate as asas.
Como você pode ver, cada linha contém 4 quadros para cada uma das 4 projeções. Ao alterar apenas o
OffsetX , você pode fazer o pássaro bater as asas e, para alterar a projeção, basta alterar o
OffsetY . Essa separação de quadros por linhas simplifica bastante a programação da animação.
O tamanho desta imagem é 384x384 e o tamanho de cada quadro é 96x96. Infelizmente, o uso direto dessa imagem nos incomoda com artefatos: alguns quadros da imagem são colocados para que suas bordas caiam em quadros adjacentes e, durante a animação, traços amarelos piscam nas bordas do sprite.
Para corrigir esses defeitos, usei o editor gráfico gratuito de plataforma cruzada
GIMP (
site oficial ). Tudo o que precisava ser feito era remover os pixels salientes das imagens nos locais onde elas caíam no quadro adjacente.
O arquivo corrigido fica assim:

- A olho nu, as diferenças são invisíveis, mas a segunda opção funciona sem artefatos.
Crie um novo projeto
1. Crie um novo projeto do tipo "Aplicativo".
Por padrão, o IDE cria um projeto chamado "projeto1", que cria imediatamente um módulo de programa chamado "unidade1", que descreve uma classe chamada "" TForm1 "e declara uma instância com o nome" Form1 ".
Em geral, ao criar novos objetos, o IDE atribui nomes semelhantes, consistindo no nome do tipo de objeto e número de série. Considero um bom estilo renomear todos esses objetos, dando-lhes nomes significativos que refletem o papel ou a finalidade do objeto.
Portanto, nosso projeto não será chamado de "projeto1", mas "Phoenix" - de acordo com o nome do sprite selecionado.
2. Salve nosso novo projeto.
É aconselhável salvar cada projeto em um diretório separado com um nome que corresponda ao nome do projeto. Durante o processo de salvamento, especificamos o diretório para salvamento (se necessário, criamos ali), depois o nome do arquivo do projeto e o nome do arquivo do módulo do programa. Criei a pasta "Phoenix" e salvei o arquivo do projeto ("Phoenix.lpi" em vez do "project1.lpi" proposto) e o arquivo do módulo do programa ("UnitMain.pas" em vez do "unit1.pas" proposto).
Nuance de maiúsculas e minúsculasA versão do Lazarus para Windows leva o nome do arquivo do módulo do programa para letras minúsculas: “unitmain.pas”, mas o nome do programa do módulo mantém a caixa original dos caracteres: “unit UnitMain;”. Isso não acontece com o arquivo do projeto; o nome do arquivo preserva o caso original dos caracteres.
3. Renomeie o formulário e altere seu título.
O formulário recém-criado, chamado “Form1” (propriedade
Nome ), é uma instância da classe “TForm1” e contém o título “Form1” (propriedade
Caption ). Altere a propriedade
Name do formulário para "FormMain" e o nome da classe será alterado para "TFormMain".
Altere a propriedade
Caption para "Phoenix" para que o título do projeto seja exibido no título da janela.
4. Como resultado, recebi o seguinte texto do módulo unitmain.pas:
unit UnitMain; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs; type TFormMain = class(TForm) private public end; var FormMain: TFormMain; implementation {$R *.lfm} end.
5. Compile, execute o projeto (chave <F9>):

Coloque o sprite no formulário
Supondo que você já tenha o componente
TImageFragment instalado , descrito no meu artigo anterior do
Lazarus - escrevemos um componente para animação de sprite , selecione a guia "Jogo" na paleta de componentes e adicione o componente "TImageFragment" ao formulário.
Usando a propriedade
Picture , carregue uma imagem (uma versão fixa do pássaro Phoenix) no componente. Além disso, também alteramos as seguintes propriedades do novo objeto:
- defina as propriedades Altura e Largura como 96
- defina as propriedades Left e Top como 0 (conveniente para combinar com minhas capturas de tela)
- A propriedade Name é alterada do inconveniente "ImageFragment1" para um simples e compreensível "Sprite"
Se tudo for feito corretamente, o componente mostrará o primeiro quadro da imagem:

O texto do módulo
UnitMain sofrerá pequenas alterações:
- o módulo
ImageFragment é adicionado à seção
usos uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ImageFragment;
- um novo objeto aparecerá na declaração de classe
TFormMain = class(TForm) Sprite: TImageFragment; private public end;
Adicionar animação - flaps de asas
1. Adicione um novo componente da classe
TTimer ao
formulário .
Este componente está localizado na guia "Sistema" da paleta de componentes. Você pode colocá-lo em qualquer local conveniente no formulário, pois ele não é exibido em um aplicativo em execução.
2. Renomeie o objeto adicionado.
O novo objeto recebe automaticamente o nome "Timer1", mas o renomeamos para "TimerLive". Muitas vezes, é conveniente atribuir nomes aos objetos, consistindo em duas partes: a primeira reflete a classe do objeto e a segunda reflete seu objetivo.
3. Altere a propriedade
Interval de 1000 para 100.
Permita que os quadros desta animação se substituam a cada 100 milissegundos, ou seja, 10 vezes por segundo. No futuro, essa propriedade poderá ser alterada para diminuir a velocidade ou acelerar a envergadura - a critério do programador.
4. Adicione um manipulador de eventos OnTimer.
A maneira mais fácil de fazer isso é clicar duas vezes no ícone de um novo objeto
TimerLive . Como resultado dessa ação, o próprio IDE adicionará um novo procedimento à declaração de classe de formulário, um link para esse procedimento nas propriedades do objeto e o corpo do novo procedimento será adicionado à seção de
implementação (e o cursor será colocado dentro deste novo procedimento, entre as
palavras-chave de início e
fim ).
5. Adicione uma linha de código ao novo procedimento.
Sprite.OffsetX := (Sprite.OffsetX + 96) mod 384;
Como resultado dessas ações, a declaração de classe deve ser algo como isto:
TFormMain = class(TForm) Sprite: TImageFragment; TimerLive: TTimer; procedure TimerLiveTimer(Sender: TObject); private public end;
E o novo procedimento - o
manipulador de eventos
OnTimer deve ser algo como isto:
procedure TFormMain.TimerLiveTimer(Sender: TObject); begin Sprite.OffsetX := (Sprite.OffsetX + 96) mod 384; end;
Após compilar e executar o aplicativo, você pode assistir o pássaro Phoenix batendo suas asas.
Isso acontece porque o manipulador de eventos do timer a cada 100 milissegundos muda ciclicamente o deslocamento do fragmento exibido e o quadro selecionado é deslocado horizontalmente, exibindo sequencialmente 4 quadros da linha superior da imagem carregada. A operação
mod - obtendo o restante da divisão - impede que o deslocamento ultrapasse o tamanho da imagem e, como resultado, o quarto quadro é novamente seguido pelo primeiro.
Adicione um movimento de sprite pela janela
1. Adicione o módulo
Math à seção
usos uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, ImageFragment, Math;
2. Adicione uma nova variável e constante à declaração de classe.
Para salvar o vetor de mover o sprite pela janela, adicione uma variável do tipo
TPoint private FVector: TPoint;
No mesmo local, declaramos uma constante para definir o módulo de velocidade
const Speed = 10;
3. Adicione outro componente da classe
TTimer ao
formulário .
Lembro que: este componente está localizado na guia "Sistema" da paleta de componentes.
O novo objeto novamente recebe automaticamente o nome "Timer1", e o renomeamos - desta vez para "TimerMove". O objetivo do segundo temporizador é controlar o movimento do sprite. Eu não vinculei os dois processos (animação e movimento) ao mesmo cronômetro, para que cada um dos cronômetros pudesse ser definido separadamente - por exemplo, para diminuir a frequência dos movimentos das asas sem desacelerar o movimento e assim por diante.
4. Altere a propriedade
Interval de 1000 para 100.
Deixe esse timer também disparar a cada 100 milissegundos, ou seja, 10 vezes por segundo. No futuro, essa propriedade também poderá ser alterada para diminuir ou acelerar a frequência de renderização do fato do sprite em movimento.
5. Adicione um
manipulador de eventos
OnTimer .
Para variar, desta vez, proponho fazer isso clicando duas vezes em frente ao evento
OnTimer na guia "Events" do novo objeto
TimerMove . Na última vez, como resultado dessa ação, o próprio IDE adicionará um novo procedimento à declaração da classe de formulário, um link para este procedimento nas propriedades do objeto e o corpo do novo procedimento será adicionado à seção de
implementação (e o cursor será colocado dentro deste novo procedimento, entre a chave palavras
começam e
terminam ).
6. Adicione duas linhas de código ao novo procedimento.
Sprite.Left := Max(0, Min(Width - Sprite.Width, Sprite.Left + FVector.x)); Sprite.Top := Max(0, Min(Height - Sprite.Height, Sprite.Top + FVector.y));
O uso das funções Max () e Min () impede que o sprite saia do formulário (a janela principal do aplicativo).
É para usar essas funções que conectamos o módulo
Math à seção de
usos .
7. Adicione um
manipulador de eventos
OnKeyPress .
Selecione o formulário (clique no retângulo cinza do layout da janela, fora de todos os componentes adicionados) e, na guia Eventos, encontramos o evento
OnKeyPress . Ao clicar duas vezes no valor vazio do manipulador de eventos, criamos e atribuímos um novo procedimento - o manipulador de eventos.
8. Adicione algumas linhas de código ao novo procedimento.
if Key = 'a' then FVector := TPoint.Create(-Speed, 0) else if Key = 'd' then FVector := TPoint.Create(Speed, 0) else if Key = 'w' then FVector := TPoint.Create(0, -Speed) else if Key = 's' then FVector := TPoint.Create(0, Speed) else if Key = ' ' then FVector := TPoint.Create(0, 0);
Como resultado dessas ações, a declaração de classe deve ser algo como isto:
TFormMain = class(TForm) Sprite: TImageFragment; TimerMove: TTimer; TimerLive: TTimer; procedure FormKeyPress(Sender: TObject; var Key: char); procedure TimerLiveTimer(Sender: TObject); procedure TimerMoveTimer(Sender: TObject); private FVector: TPoint; const Speed = 10; public end;
E os novos procedimentos - os manipuladores de eventos
OnTimer e
OnKeyPress devem ter a seguinte aparência:
procedure TFormMain.TimerMoveTimer(Sender: TObject); begin Sprite.Left := Max(0, Min(Width - Sprite.Width, Sprite.Left + FVector.x)); Sprite.Top := Max(0, Min(Height - Sprite.Height, Sprite.Top + FVector.y)); end; procedure TFormMain.FormKeyPress(Sender: TObject; var Key: char); begin if Key = 'a' then FVector := TPoint.Create(-Speed, 0) else if Key = 'd' then FVector := TPoint.Create(Speed, 0) else if Key = 'w' then FVector := TPoint.Create(0, -Speed) else if Key = 's' then FVector := TPoint.Create(0, Speed) else if Key = ' ' then FVector := TPoint.Create(0, 0); end;
Após compilar e executar o aplicativo, você pode mover o pássaro Phoenix pela tela usando as teclas "a", "w", "s", "d" e pará-lo com a barra de espaço.
Usamos diferentes projeções do sprite
Adicione o seguinte código ao final do procedimento
TFormMain.FormKeyPress if FVector.x < 0 then Sprite.OffsetY := 96 else if FVector.x > 0 then Sprite.OffsetY := 192 else if FVector.y < 0 then Sprite.OffsetY := 288 else Sprite.OffsetY := 0;
Alterar a propriedade
OffsetY , dependendo do vetor de deslocamento, faz com que a imagem gire na direção do movimento.
Todo o texto do módulo UnitMain unit UnitMain; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, ImageFragment, Math; type TFormMain = class(TForm) Sprite: TImageFragment; TimerMove: TTimer; TimerLive: TTimer; procedure FormKeyPress(Sender: TObject; var Key: char); procedure TimerLiveTimer(Sender: TObject); procedure TimerMoveTimer(Sender: TObject); private FVector: TPoint; const Speed = 10; public end; var FormMain: TFormMain; implementation {$R *.lfm} procedure TFormMain.TimerLiveTimer(Sender: TObject); begin Sprite.OffsetX := (Sprite.OffsetX + 96) mod 384; end; procedure TFormMain.TimerMoveTimer(Sender: TObject); begin Sprite.Left := Max(0, Min(Width - Sprite.Width, Sprite.Left + FVector.x)); Sprite.Top := Max(0, Min(Height - Sprite.Height, Sprite.Top + FVector.y)); end; procedure TFormMain.FormKeyPress(Sender: TObject; var Key: char); begin if Key = 'a' then FVector := TPoint.Create(-Speed, 0) else if Key = 'd' then FVector := TPoint.Create(Speed, 0) else if Key = 'w' then FVector := TPoint.Create(0, -Speed) else if Key = 's' then FVector := TPoint.Create(0, Speed) else if Key = ' ' then FVector := TPoint.Create(0, 0); if FVector.x < 0 then Sprite.OffsetY := 96 else if FVector.x > 0 then Sprite.OffsetY := 192 else if FVector.y < 0 then Sprite.OffsetY := 288 else Sprite.OffsetY := 0; end; end.
Em vez de um posfácio
Este exemplo simples não reivindica classificações altas de velocidade ou usabilidade. Se alguém, como no
artigo anterior , quiser dizer nos comentários que a animação precisa ser feita de forma errada - bem-vindo, escreva seu artigo. E o tópico deste artigo é como fazer animação em várias linhas de código, sem usar nenhuma biblioteca especial, praticamente "no joelho". Esse método foi testado na prática e realmente funciona. Portanto, antes de criticar e “menos”, leia novamente o que é este artigo e por quê.