Lazarus - animación simple usando el componente TImageFragment

En lugar del prólogo

En mi reciente artículo de Lazarus, al escribir un componente para la animación de sprites, describí el proceso de crear un componente TImageFragment simple que le permite mostrar un fragmento dado de una imagen.

Continuando con el tema seleccionado, en este artículo quiero mostrar lo fácil que es hacer animación de sprites en el entorno de desarrollo de Lazarus ( sitio oficial ) usando este componente.

Con este enfoque, los cuadros de animación individuales en diferentes proyecciones se colocan en la misma imagen, y el componente para mostrar el sprite muestra solo un fragmento seleccionado de esta imagen usando las propiedades OffsetX y OffsetY (desplazamiento de la esquina superior izquierda del fragmento de imagen horizontal y verticalmente).

Muchas de estas imágenes listas para usar se pueden encontrar en la Web, por ejemplo, aquí en este sitio .

Seleccione (y prepare) la imagen

Para mi ejemplo, elegí esta imagen:

- Muy expresivamente, este ave Fénix agita sus alas.

Como puede ver, cada fila contiene 4 cuadros para cada una de las 4 proyecciones. Al cambiar solo OffsetX , puede hacer que el pájaro agite sus alas, y para cambiar la proyección es suficiente con solo cambiar OffsetY . Esta separación de cuadros por líneas simplifica enormemente la programación de la animación.

El tamaño de esta imagen es 384x384, y el tamaño de cada cuadro es 96x96. Desafortunadamente, el uso directo de esta imagen nos perturbó con artefactos: algunos cuadros de la imagen se colocan de modo que sus bordes caigan en cuadros adyacentes, y durante la animación, los trazos amarillos parpadean en los bordes del sprite.

Para solucionar estos defectos, utilicé el editor gráfico gratuito multiplataforma GIMP ( sitio oficial ). Todo lo que tenía que hacer era eliminar los píxeles sobresalientes de las imágenes en los lugares donde caían en el marco adyacente.

El archivo corregido se ve así:



- A simple vista, las diferencias son invisibles, pero la segunda opción funciona sin artefactos.

Crea un nuevo proyecto

1. Cree un nuevo proyecto de tipo "Aplicación".

Por defecto, el IDE crea un proyecto llamado "proyecto1", que inmediatamente crea un módulo de programa llamado "unidad1", que describe una clase llamada "" TForm1 "y declara una instancia con el nombre" Form1 ".

En general, al crear nuevos objetos, el IDE les asigna nombres similares, que consisten en el nombre del tipo de objeto y el número de serie. Considero que es un buen estilo cambiar el nombre de todos esos objetos, dándoles nombres significativos que reflejen el rol o el propósito del objeto.

Por lo tanto, nuestro proyecto no se llamará "proyecto1", sino "Fénix", por el nombre del sprite seleccionado.

2. Guarde nuestro nuevo proyecto.

Es aconsejable guardar cada proyecto en un directorio separado con un nombre que coincida con el nombre del proyecto. Durante el proceso de guardado, especificamos el directorio para guardar (si es necesario, lo creamos allí mismo), luego el nombre del archivo del proyecto y el nombre del módulo del programa. Creé la carpeta "Phoenix" y guardé el archivo del proyecto allí ("Phoenix.lpi" en lugar del "project1.lpi" propuesto) y el archivo del módulo del programa ("UnitMain.pas" en lugar de la "unit1.pas" propuesta).

Matiz de mayúsculas y minúsculas
La versión de Lazarus para Windows lleva el nombre del archivo del módulo del programa a minúsculas: "unitmain.pas", pero el nombre del programa del módulo conserva el caso original de caracteres: "unit UnitMain;". Esto no sucede con el archivo del proyecto; el nombre del archivo conserva el caso original de los caracteres.

3. Cambie el nombre del formulario y cambie su título.

El formulario recién creado, llamado "Form1" (propiedad Name ), es una instancia de la clase "TForm1" y contiene el título "Form1" (propiedad Caption ). Cambie la propiedad Name del formulario a "FormMain", y el nombre de la clase cambiará a "TFormMain".

Cambie la propiedad Caption a "Phoenix" para que el título del proyecto se muestre en el título de la ventana.

4. Como resultado, obtuve el siguiente texto del 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, ejecute el proyecto (clave <F9>):



Pon el sprite en el formulario

Suponiendo que ya haya instalado el componente TImageFragment descrito en mi artículo anterior de Lazarus, escribimos un componente para la animación de sprites , seleccione la pestaña "Juego" en la paleta de componentes y agregue el componente "TImageFragment" al formulario.

Usando la propiedad Picture , cargue una imagen (una versión fija del ave Phoenix) en el componente. Además, también cambiamos las siguientes propiedades del nuevo objeto:

  • establecer las propiedades de alto y ancho en 96
  • establezca las propiedades Izquierda y Superior en 0 (conveniente para combinar con mis capturas de pantalla)
  • La propiedad de nombre se cambia del inconveniente "ImageFragment1" a un simple y comprensible "Sprite"

Si todo se hace correctamente, el componente mostrará el primer fotograma de la imagen:


El texto del módulo UnitMain sufrirá cambios menores:
- el módulo ImageFragment se agrega a la sección de usos

 uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ImageFragment; 

- aparecerá un nuevo objeto en la declaración de clase

  TFormMain = class(TForm) Sprite: TImageFragment; private public end; 


Añadir animación - aletas

1. Agregue un nuevo componente de la clase TTimer al formulario .

Este componente se encuentra en la pestaña "Sistema" de la paleta de componentes. Puede colocarlo en cualquier lugar conveniente del formulario, ya que no se muestra en una aplicación en ejecución.

2. Cambie el nombre del objeto agregado.

El nuevo objeto obtiene automáticamente el nombre "Timer1", pero cambiamos el nombre a "TimerLive". A menudo es conveniente dar nombres a los objetos en dos partes: la primera refleja la clase del objeto y la segunda refleja su propósito.

3. Cambie la propiedad Intervalo de 1000 a 100.

Deje que los cuadros de esta animación se reemplacen entre sí cada 100 milisegundos, es decir, 10 veces por segundo. En el futuro, esta propiedad se puede cambiar para ralentizar o acelerar la envergadura, a discreción del programador.

4. Agregue un controlador de eventos OnTimer.

La forma más fácil de hacer esto es hacer doble clic en el icono de un nuevo objeto TimerLive . Como resultado de esta acción, el propio IDE agregará un nuevo procedimiento a la declaración de clase de formulario, un enlace a este procedimiento a las propiedades del objeto, y el cuerpo del nuevo procedimiento se agregará a la sección de implementación (y el cursor se colocará dentro de este nuevo procedimiento, entre las palabras clave de inicio y fin ).

5. Agregue una línea de código al nuevo procedimiento.

  Sprite.OffsetX := (Sprite.OffsetX + 96) mod 384; 

Como resultado de estas acciones, la declaración de clase debería verse así:

  TFormMain = class(TForm) Sprite: TImageFragment; TimerLive: TTimer; procedure TimerLiveTimer(Sender: TObject); private public end; 

Y el nuevo procedimiento: el controlador de eventos OnTimer debería verse así:

 procedure TFormMain.TimerLiveTimer(Sender: TObject); begin Sprite.OffsetX := (Sprite.OffsetX + 96) mod 384; end; 

Después de compilar y ejecutar la aplicación, puedes ver al ave Fénix batiendo sus alas.

Esto sucede porque el controlador de eventos del temporizador cada 100 milisegundos cambia cíclicamente el desplazamiento del fragmento mostrado, y el cuadro seleccionado se desplaza horizontalmente, mostrando secuencialmente 4 cuadros de la línea superior de la imagen cargada. La operación de modificación , obteniendo el resto de la división, evita que el desplazamiento vaya más allá del tamaño de la imagen y, como resultado, el cuarto cuadro es nuevamente seguido por el primero.

Agrega un movimiento de sprite alrededor de la ventana

1. Agregue el módulo matemático a la sección de usos

 uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, ImageFragment, Math; 

2. Agregue una nueva variable y constante a la declaración de clase.

Para guardar el vector de mover el sprite por la ventana, agregue una variable de tipo TPoint

  private FVector: TPoint; 

En el mismo lugar, declaramos una constante para configurar el módulo de la velocidad de movimiento

  const Speed = 10; 

3. Agregue otro componente de la clase TTimer al formulario .

Les recuerdo: este componente se encuentra en la pestaña "Sistema" de la paleta de componentes.

El nuevo objeto vuelve a obtener automáticamente el nombre "Timer1", y le cambiamos el nombre, esta vez a "TimerMove". El propósito del segundo temporizador es controlar el movimiento del sprite. No vinculé ambos procesos (animación y movimiento) al mismo temporizador para que cada uno de los temporizadores se pueda configurar por separado, por ejemplo, para reducir la frecuencia de las oscilaciones de las alas sin disminuir el movimiento, y así sucesivamente.

4. Cambie la propiedad Intervalo de 1000 a 100.

Deje que este temporizador también dispare cada 100 milisegundos, es decir, 10 veces por segundo. En el futuro, esta propiedad también se puede cambiar para disminuir o acelerar la frecuencia de hacer que el sprite se mueva.

5. Agregue un controlador de eventos OnTimer .

Para variar, esta vez propongo hacer esto haciendo doble clic frente al evento OnTimer en la pestaña "Eventos" del nuevo objeto TimerMove . Como última vez, como resultado de esta acción, el propio IDE agregará un nuevo procedimiento a la declaración de clase de formulario, un enlace a este procedimiento a las propiedades del objeto, y el cuerpo del nuevo procedimiento se agregará a la sección de implementación (y el cursor se colocará dentro de este nuevo procedimiento, entre la clave palabras comienzan y terminan ).

6. Agregue dos líneas de código al nuevo procedimiento.

  Sprite.Left := Max(0, Min(Width - Sprite.Width, Sprite.Left + FVector.x)); Sprite.Top := Max(0, Min(Height - Sprite.Height, Sprite.Top + FVector.y)); 

El uso de las funciones Max () y Min () evita que el sprite salga del formulario (la ventana principal de la aplicación).
Es para usar estas funciones que conectamos el módulo matemático a la sección de usos .

7. Agregue un controlador de eventos OnKeyPress .

Seleccione el formulario (haga clic en el rectángulo gris del diseño de la ventana fuera de todos los componentes agregados) y en la pestaña Eventos encontramos el evento OnKeyPress . Al hacer doble clic en el valor vacío del controlador de eventos, creamos y asignamos un nuevo procedimiento: el controlador de eventos.

8. Agregue algunas líneas de código al nuevo procedimiento.

  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 de estas acciones, la declaración de clase debería verse así:

  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; 

Y los nuevos procedimientos: los controladores de eventos OnTimer y OnKeyPress deberían verse así:

 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; 

Después de compilar e iniciar la aplicación, puede mover el ave Fénix en la pantalla con las teclas "a", "w", "s", "d" y detenerlo con la barra espaciadora.

Usamos diferentes proyecciones del sprite.

Agregue el siguiente código al final del procedimiento 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; 

Cambiar la propiedad OffsetY dependiendo del vector de desplazamiento hace que la imagen gire en la dirección del movimiento.

Todo el texto del módulo principal de la unidad
 unit UnitMain; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, ImageFragment, Math; type { TFormMain } 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} { TFormMain } 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. 

En lugar de un epílogo

Este simple ejemplo no reclama altas calificaciones de velocidad o usabilidad. Si alguien, como en el artículo anterior , quiere decir en los comentarios que la animación debe hacerse mal, bienvenido, escriba su artículo. Y el tema de este artículo es cómo hacer animaciones en varias líneas de código, sin usar bibliotecas especiales, prácticamente "en la rodilla". Este método ha sido probado en la práctica, realmente funciona, así que antes de criticar y "menos", vuelva a leer de qué trata este artículo y por qué.

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


All Articles