Lazarus - escrevendo um componente para animação de sprites

Em vez do prefácio

Na escola de Odessa, os alunos da 8ª série nas aulas de ciência da computação usam o ambiente de desenvolvimento multiplataforma gratuito do Lazarus ( site oficial ), que se parece muito com o amado por muitos Delphi, usando a versão do Object Pascal chamada Free Pascal e simplificando bastante o processo de entrada na programação.

Mas as crianças não estão interessadas em escrever um programa para calcular a gravidade usando a fórmula F = mg, que ainda é incompreensível para elas. Quase todas as crianças que eu tentei ensinar programação querem escrever um jogo desde a primeira lição. Felizmente, o Lazarus é ótimo para escrever jogos simples.

É verdade que, para criar sprites animados, eu precisava de um componente que exibisse um fragmento arbitrário da imagem (que descreve várias projeções diferentes do mesmo personagem em diferentes fases do movimento), mas não existe esse componente na entrega padrão. Acabou sendo muito fácil escrever você mesmo, e quero falar sobre essa tecnologia neste artigo.

Para exibir conteúdo gráfico divertido em vez de um conjunto comercial seco de componentes padrão no Lazarus (como no Delphi), existem 3 componentes na guia Adicional:
- TImage (exibindo uma imagem de um arquivo arbitrário);
- TShape (exibição de uma das várias primitivas gráficas predefinidas);
- TPaintBox (exibindo uma tela na qual você pode desenhar programaticamente).

A coisa mais espetacular para um aluno é carregar um pequeno sprite no TImage e escrever um programa para movê-lo pela tela - de acordo com os eventos do mouse / teclado, automaticamente em um loop ou automaticamente por um evento de um temporizador.

Assim que isso começa a funcionar, o aluno tem a seguinte pergunta legítima: é possível fazer o personagem se mexer? E é possível fazer com que ele não nos olhe constantemente, mas se vire na direção que coincide com a direção do movimento?

Na Web, você pode encontrar um grande número de imagens prontas para uso no desenvolvimento de jogos. E muitos personagens são pré-projetados em várias projeções e vários quadros de animação (como, por exemplo, aqui neste site ).

Aqui está um exemplo de imagem em que os sprites são organizados na forma de uma tabela, na qual cada linha corresponde a uma certa projeção e cada coluna corresponde a uma certa fase de animação:


Por que tantas fotos?
Para exibir um sprite, basta colocar na tela um componente simples que não exibe toda a imagem, mas apenas um fragmento; e, alterando o deslocamento do fragmento selecionado horizontal e verticalmente, você pode fazer o personagem girar em direções diferentes e fazer movimentos cíclicos (por exemplo, bater asas ou pisar com as pernas). Essa técnica é frequentemente usada no desenvolvimento da Web: mesmo conjuntos simples de ícones para gráficos de negócios são geralmente colocados em um arquivo e exibidos em locais diferentes em páginas com diferentes compensações, dando a impressão de imagens diferentes.

Infelizmente, o componente TImage, que faz parte da distribuição padrão do Lazarus (e Delphi), não permite mostrar um fragmento arbitrário de uma imagem: alterando suas propriedades, podemos forçá-lo a mostrar apenas a imagem inteira, o canto superior esquerdo ou sua parte central. Para exibir um fragmento arbitrário da imagem definida pelo deslocamento e dimensões ao longo dos dois eixos, você precisa de algum outro componente. Mas, como se viu, fazer você mesmo em Lázaro não é nada difícil!

Crie um novo componente

Como uma instrução para criar componentes, usei o guia oficial .

Tudo é escrito lá em detalhes suficientes; a duplicação não faz sentido. Eu só vou insistir em alguns pontos.

1. O Assistente de Projeto padrão não nos permite criar um pacote e, de alguma forma, obter acesso ao editor, selecione "Novo Projeto" (na versão russa - "Novo Projeto")


e depois "Aplicativo" (na versão russa - "Aplicativo"):


2. Seguindo as instruções, no menu "Pacote" (na versão russa - "Pacote"), selecione o item superior "Novo pacote ..." (na versão russa - "Novo pacote ..."), selecione o nome e o caminho do arquivo para salvar. Chamei meu novo pacote de "Jogo" e o coloquei em uma pasta separada com o mesmo nome:


Criei uma pasta Lazarus / Cmp separada com a expectativa de que eu possa ter vários pacotes diferentes com componentes e já criei a pasta “Game” nesta pasta.

Se tudo for feito corretamente, uma janela de um novo pacote (até agora vazio) deve aparecer na tela.

3. Prosseguindo novamente de acordo com as instruções, para criar um novo componente na janela do pacote, clique no botão Adicionar (na versão russa - "Adicionar") e selecione "Novo componente" na lista suspensa (na versão russa - "Novo componente"):


Especificamos TCustomImage como a classe ancestral - essa classe é realmente usada para implementar o componente TImage, mas difere dela por não conter propriedades publicadas e nos permite determinar o conjunto de propriedades que estarão disponíveis no designer para o nosso componente.

O que são propriedades publicadas?
Para quem não sabe disso, vou esclarecer que publicada é uma seção da classe (como pública) que descreve propriedades novas ou simplesmente indica propriedades herdadas que devem estar disponíveis no editor de propriedades visuais no estágio de desenvolvimento do programa. As aulas intermediárias não declaram nada nesta seção, deixando a oportunidade para o programador trazer à tona o que achar melhor. Portanto, a classe TImage não adiciona nenhuma funcionalidade, mas coloca apenas um número de propriedades herdadas do pai TCustomImage na seção publicada. Como precisamos ocultar algumas dessas propriedades, também herdaremos nosso novo componente do TCustomImage e exibiremos apenas o que não contradiz a lógica do nosso componente publicado.

Ícone (ícone) para o componente
Seria um bom estilo desenhar um ícone pessoal para cada novo componente, mas como nossa tarefa é mostrar como é simples, deixaremos esse campo vazio, o que levará ao ícone padrão usado no Lazarus / Delphi para todos os componentes caseiros na barra de ferramentas. .
A propósito, as instruções mencionadas acima contêm uma seção separada sobre a criação de ícones para componentes - é para aqueles que não estão satisfeitos com o ícone "padrão".

Após preencher todos os campos, clique no botão "Criar novo componente" (na versão russa - "Criar um novo componente").

Adicione código ao novo componente.

Imediatamente após a criação de um novo componente, seu código-fonte é mais ou menos assim:

unit ImageFragment; {$mode objfpc}{$H+} interface uses Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs; type TImageFragment = class(TCustomImage) private protected public published end; procedure Register; implementation procedure Register; begin RegisterComponents('Game', [TImageFragment]); end; end. 

Como esperado, a declaração de classe está completamente vazia e não há implementação. Tudo isso é a função de registro de componentes na guia "Jogo".

Precisamos adicionar várias propriedades publicadas herdadas, criar duas próprias e redefinir uma função virtual. Vamos começar!

0. Na seção de importação, precisamos de dois módulos adicionais: ExtCtrls e LCLProc - adicione-os à seção de usos:

 uses Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls, LCLProc; 

1. Adicione uma lista de propriedades publicada que seja completamente semelhante ao componente TImage, exceto algumas propriedades que permitem alterar a escala e a posição da imagem:

  published property AntialiasingMode; property Align; property Anchors; //property AutoSize; property BorderSpacing; //property Center; //property KeepOriginXWhenClipped; //property KeepOriginYWhenClipped; property Constraints; property DragCursor; property DragMode; property Enabled; property OnChangeBounds; property OnClick; property OnDblClick; property OnDragDrop; property OnDragOver; property OnEndDrag; property OnMouseDown; property OnMouseEnter; property OnMouseLeave; property OnMouseMove; property OnMouseUp; property OnMouseWheel; property OnMouseWheelDown; property OnMouseWheelUp; property OnPaint; property OnPictureChanged; property OnPaintBackground; property OnResize; property OnStartDrag; property ParentShowHint; property Picture; property PopupMenu; //property Proportional; property ShowHint; //property Stretch; //property StretchOutEnabled; //property StretchInEnabled; property Transparent; property Visible; end; 

Por uma questão de persuasão, não excluí, mas comentei as propriedades que estão no componente TImage, mas que interferem no nosso novo componente TImageFragment.

2. Adicione duas novas propriedades à declaração de classe para definir as compensações de imagem horizontal e vertical:

  private FOffsetX: Integer; FOffsetY: Integer; procedure SetOffsetX(AValue: Integer); procedure SetOffsetY(AValue: Integer); published property OffsetX: Integer read FOffsetX write SetOffsetX default 0; property OffsetY: Integer read FOffsetY write SetOffsetY default 0; 

e não se esqueça de adicionar dois procedimentos declarados à implementação da classe:
 implementation procedure TImageFragment.SetOffsetX(AValue: Integer); begin if FOffsetX = AValue then exit; FOffsetX := AValue; PictureChanged(Self); end; procedure TImageFragment.SetOffsetY(AValue: Integer); begin if FOffsetY = AValue then exit; FOffsetY := AValue; PictureChanged(Self); end; 

3. Redefinimos a função virtual DestRect:

  public function DestRect: TRect; override; 

e adicione sua implementação à implementação da classe:

 function TImageFragment.DestRect: TRect; begin Result := inherited DestRect(); if (FOffsetX <> 0) or (FOffsetY <> 0) then LCLProc.OffsetRect(Result, -FOffsetX, -FOffsetY); end; 

Compile o pacote e reconstrua o Lazarus

1. Na janela do pacote, clique no botão "Compilar" (na versão russa - "Compilar"). Se tudo for feito corretamente, uma mensagem verde sobre compilação bem-sucedida aparecerá na janela da mensagem; caso contrário, a mensagem será amarela ou vermelha.

2. Na mesma janela, clique no botão "Usar" (na versão russa - "Usar") e selecione o segundo item "Instalar" no menu suspenso (na versão russa - "Instalar"). O programa oferecerá a reconstrução e a reinicialização do IDE - concordamos:



3. Após o reinício, uma nova guia "Jogo" aparecerá na barra de ferramentas e nela - um ícone para o nosso novo componente.

Em vez de um posfácio

No próximo artigo de Lazarus - uma animação simples usando o componente TImageFragment, falei sobre como usar esse componente - em 5 minutos, crie uma janela na qual o personagem animado se moverá em direções diferentes e girará na direção do movimento.

Se o tópico for interessante para os leitores, posso complementar esta série com um artigo sobre como, depois de passar um pouco mais de tempo, você pode criar, por exemplo, um campo de futebol com alguns jogadores de futebol controlados por teclado.

E se houver tempo e desejo suficientes - tentarei escrever algoritmos de controle de caracteres diferentes (por exemplo, jogadores de futebol) e organizar competições entre eles!

Source: https://habr.com/ru/post/pt438828/


All Articles