Lazarus - einfache Animation mit der TImageFragment-Komponente

Anstelle des Vorworts

In meinem kürzlich erschienenen Lazarus- Artikel - Schreiben einer Komponente für Sprite-Animationen - habe ich den Prozess zum Erstellen einer einfachen TImageFragment- Komponente beschrieben, mit der Sie ein bestimmtes Fragment eines Bildes anzeigen können.

In diesem Artikel möchte ich das ausgewählte Thema fortsetzen und zeigen, wie einfach es ist, mithilfe dieser Komponente Animationen von Sprites in der Lazarus- Entwicklungsumgebung ( offizielle Website ) zu erstellen.

Bei diesem Ansatz werden einzelne Animationsrahmen in verschiedenen Projektionen auf demselben Bild platziert, und die Komponente zum Anzeigen des Sprites zeigt nur ein ausgewähltes Fragment dieses Bildes unter Verwendung der Eigenschaften OffsetX und OffsetY (Versatz der oberen linken Ecke des Bildfragments horizontal und vertikal).

Viele dieser vorgefertigten Bilder finden Sie im Web - zum Beispiel hier auf dieser Website .

Wählen Sie das Bild aus (und bereiten Sie es vor)

Für mein Beispiel habe ich dieses Bild ausgewählt:

- sehr ausdrucksstark schlägt dieser Phönixvogel mit den Flügeln.

Wie Sie sehen können, enthält jede Zeile 4 Frames für jede der 4 Projektionen. Wenn Sie nur OffsetX ändern , können Sie den Vogel mit den Flügeln schlagen lassen. Um die Projektion zu ändern, reicht es aus, nur OffsetY zu ändern. Diese Trennung von Frames durch Linien vereinfacht die Programmierung von Animationen erheblich.

Die Größe dieses Bildes beträgt 384 x 384 und die Größe jedes Rahmens beträgt 96 x 96. Leider hat uns die direkte Verwendung dieses Bildes mit Artefakten verärgert: Einige Frames des Bildes sind so platziert, dass ihre Kanten auf benachbarte Frames fallen, und während der Animation blinken gelbe Striche an den Rändern des Sprites.

Um diese Mängel zu beheben, habe ich den kostenlosen plattformübergreifenden grafischen Editor GIMP ( offizielle Seite ) verwendet. Alles, was getan werden musste, war, die hervorstehenden Pixel der Bilder an den Stellen zu entfernen, an denen sie auf den angrenzenden Rahmen fielen.

Die korrigierte Datei sieht folgendermaßen aus:



- Mit bloßem Auge sind die Unterschiede unsichtbar, aber die zweite Option funktioniert ohne Artefakte.

Erstellen Sie ein neues Projekt

1. Erstellen Sie ein neues Projekt vom Typ „Anwendung“.

Standardmäßig erstellt die IDE ein Projekt mit dem Namen "project1", das sofort ein Programmmodul mit dem Namen "unit1" erstellt, eine Klasse mit dem Namen "TForm1" beschreibt und eine Instanz mit dem Namen "Form1" deklariert.

Im Allgemeinen weist die IDE beim Erstellen neuer Objekte ihnen ähnliche Namen zu, die aus dem Namen des Objekttyps und der Seriennummer bestehen. Ich halte es für einen guten Stil, alle diese Objekte umzubenennen und ihnen aussagekräftige Namen zu geben, die die Rolle oder den Zweck des Objekts widerspiegeln.

Unser Projekt heißt also nicht "Projekt1", sondern "Phoenix" - entsprechend dem Namen des ausgewählten Sprites.

2. Speichern Sie unser neues Projekt.

Es ist ratsam, jedes Projekt in einem separaten Verzeichnis mit einem Namen zu speichern, der dem Namen des Projekts entspricht. Beim Speichern geben wir das zu speichernde Verzeichnis an (ggf. erstellen wir es genau dort), dann den Namen der Projektdatei und den Dateinamen des Programmmoduls. Ich habe den Ordner „Phoenix“ erstellt und dort die Projektdatei („Phoenix.lpi“ anstelle der vorgeschlagenen „project1.lpi“) und die Programmmoduldatei („UnitMain.pas“ anstelle der vorgeschlagenen „unit1.pas“) gespeichert.

Charakterfall Nuance
Die Lazarus-Version für Windows führt den Dateinamen des Programmmoduls in Kleinbuchstaben: "unitmain.pas", aber der Programmname des Moduls behält die ursprüngliche Groß- und Kleinschreibung der Zeichen bei: "unit UnitMain;". Dies ist bei der Projektdatei nicht der Fall, da der Dateiname den ursprünglichen Fall von Zeichen beibehält.

3. Benennen Sie das Formular um und ändern Sie den Titel.

Das neu erstellte Formular mit dem Namen "Form1" ( Name- Eigenschaft) ist eine Instanz der Klasse "TForm1" und enthält den Titel "Form1" ( Caption- Eigenschaft). Ändern Sie die Name- Eigenschaft des Formulars in "FormMain", und der Klassenname ändert sich in "TFormMain".

Ändern Sie die Caption- Eigenschaft in "Phoenix", sodass der Projekttitel im Fenstertitel angezeigt wird.

4. Als Ergebnis habe ich den folgenden Text des Moduls unitmain.pas erhalten:

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. Kompilieren Sie das Projekt, führen Sie es aus (Taste <F9>):



Setzen Sie das Sprite auf das Formular

Angenommen, Sie haben bereits die TImageFragment- Komponente installiert , die in meinem vorherigen Lazarus- Artikel beschrieben wurde. Wir schreiben eine Komponente für die Sprite-Animation , wählen die Registerkarte "Spiel" in der Komponentenpalette aus und fügen dem Formular die Komponente "TImageFragment" hinzu.

Laden Sie mit der Picture- Eigenschaft ein Bild (eine feste Version des Phoenix-Vogels) in die Komponente. Darüber hinaus ändern wir auch die folgenden Eigenschaften des neuen Objekts:

  • Setzen Sie die Eigenschaften Höhe und Breite auf 96
  • Setzen Sie die Eigenschaften Links und Oben auf 0 (praktisch für den Abgleich mit meinen Screenshots).
  • Die Namenseigenschaft wird von "ImageFragment1" in ein einfaches und verständliches "Sprite" geändert.

Wenn alles richtig gemacht wurde, zeigt die Komponente den ersten Frame des Bildes:


Der Text des UnitMain- Moduls wird geringfügig geändert:
- Das ImageFragment- Modul wird dem Verwendungsabschnitt hinzugefügt

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

- In der Klassendeklaration wird ein neues Objekt angezeigt

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


Animation hinzufügen - Flügelklappen

1. Fügen Sie dem Formular eine neue Komponente der TTimer- Klasse hinzu.

Diese Komponente befindet sich auf der Registerkarte „System“ der Komponentenpalette. Sie können es an einer beliebigen Stelle im Formular platzieren, da es in einer laufenden Anwendung nicht angezeigt wird.

2. Benennen Sie das hinzugefügte Objekt um.

Das neue Objekt erhält automatisch den Namen "Timer1", aber wir benennen es in "TimerLive" um. Es ist oft zweckmäßig, Objekten solche Namen zu geben, die aus zwei Teilen bestehen: Der erste spiegelt die Klasse des Objekts wider und der zweite spiegelt seinen Zweck wider.

3. Ändern Sie die Interval- Eigenschaft von 1000 in 100.

Lassen Sie die Frames dieser Animation alle 100 Millisekunden, dh 10 Mal pro Sekunde, einander ersetzen. In Zukunft kann diese Eigenschaft geändert werden, um die Spannweite zu verlangsamen oder zu beschleunigen - nach Ermessen des Programmierers.

4. Fügen Sie einen OnTimer-Ereignishandler hinzu.

Der einfachste Weg, dies zu tun, besteht darin, auf das Symbol des neuen TimerLive- Objekts zu doppelklicken . Als Ergebnis dieser Aktion fügt die IDE selbst der Formularklassendeklaration eine neue Prozedur hinzu, eine Verknüpfung zu dieser Prozedur mit den Objekteigenschaften, und der Hauptteil der neuen Prozedur wird dem Implementierungsabschnitt hinzugefügt (und der Cursor wird innerhalb dieser neuen Prozedur zwischen den Schlüsselwörtern begin und end platziert ).

5. Fügen Sie der neuen Prozedur eine Codezeile hinzu.

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

Als Ergebnis dieser Aktionen sollte die Klassendeklaration ungefähr so ​​aussehen:

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

Und die neue Prozedur - der OnTimer- Ereignishandler sollte ungefähr so aussehen:

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

Nach dem Kompilieren und Ausführen der Anwendung können Sie beobachten, wie der Phoenix-Vogel mit den Flügeln schlägt.

Dies liegt daran, dass der Timer-Ereignishandler alle 100 Millisekunden zyklisch den Versatz des angezeigten Fragments ändert und der ausgewählte Frame horizontal verschoben wird und nacheinander 4 Frames der obersten Zeile des geladenen Bildes anzeigt. Die Mod- Operation - den Rest der Teilung erhalten - verhindert, dass der Versatz über die Bildgröße hinausgeht, und infolgedessen folgt auf den 4. Frame erneut der 1. Frame.

Fügen Sie eine Sprite-Bewegung um das Fenster hinzu

1. Fügen Sie das Mathematikmodul zum Verwendungsabschnitt hinzu

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

2. Fügen Sie der Klassendeklaration eine neue Variable und Konstante hinzu.

Fügen Sie eine Variable vom Typ TPoint hinzu, um den Vektor zum Verschieben des Sprites im Fenster zu speichern

  private FVector: TPoint; 

An derselben Stelle deklarieren wir eine Konstante zur Definition des Geschwindigkeitsmoduls

  const Speed = 10; 

3. Fügen Sie dem Formular eine weitere Komponente der TTimer- Klasse hinzu.

Ich erinnere Sie daran: Diese Komponente befindet sich auf der Registerkarte „System“ der Komponentenpalette.

Das neue Objekt erhält automatisch wieder den Namen "Timer1" und wir benennen ihn um - diesmal in "TimerMove". Der Zweck des zweiten Timers besteht darin, die Bewegung des Sprites zu steuern. Ich habe nicht beide Prozesse (Animation und Bewegung) an denselben Timer gebunden, sodass jeder Timer separat eingestellt werden konnte - zum Beispiel, um die Häufigkeit von Flügelschwüngen zu verlangsamen, ohne die Bewegung zu verlangsamen, und so weiter.

4. Ändern Sie die Interval- Eigenschaft von 1000 in 100.

Lassen Sie diesen Timer auch alle 100 Millisekunden, dh 10 Mal pro Sekunde, auslösen. In Zukunft kann diese Eigenschaft auch geändert werden, um die Häufigkeit des Renderns der Bewegung des Sprites zu verlangsamen oder zu beschleunigen.

5. Fügen Sie einen OnTimer- Ereignishandler hinzu.

Zur Abwechslung schlage ich diesmal vor, dies durch Doppelklicken gegenüber dem OnTimer- Ereignis auf der Registerkarte "Ereignisse" des neuen TimerMove- Objekts zu tun . Als letztes Ergebnis dieser Aktion fügt die IDE selbst der Formularklassendeklaration eine neue Prozedur hinzu, eine Verknüpfung zu dieser Prozedur mit den Objekteigenschaften, und der Hauptteil der neuen Prozedur wird dem Implementierungsabschnitt hinzugefügt (und der Cursor wird innerhalb dieser neuen Prozedur zwischen den Schlüsseln platziert Wörter beginnen und enden ).

6. Fügen Sie der neuen Prozedur zwei Codezeilen hinzu.

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

Die Verwendung der Funktionen Max () und Min () verhindert, dass das Sprite das Formular (das Hauptanwendungsfenster) verlässt.
Für die Verwendung dieser Funktionen haben wir das Math- Modul mit dem Verwendungsabschnitt verbunden .

7. Fügen Sie einen OnKeyPress- Ereignishandler hinzu.

Wählen Sie das Formular aus (klicken Sie auf das graue Rechteck des Fensterlayouts außerhalb aller hinzugefügten Komponenten) und auf der Registerkarte Ereignisse finden Sie das OnKeyPress- Ereignis. Durch Doppelklick auf den leeren Wert des Ereignishandlers erstellen wir eine neue Prozedur und weisen sie zu - den Ereignishandler.

8. Fügen Sie der neuen Prozedur einige Codezeilen hinzu.

  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); 

Als Ergebnis dieser Aktionen sollte die Klassendeklaration ungefähr so ​​aussehen:

  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; 

Und die neuen Verfahren - OnTimer- und OnKeyPress- Ereignishandler sollten ungefähr so aussehen:

 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; 

Nach dem Kompilieren und Ausführen der Anwendung können Sie den Phoenix-Vogel mit den Tasten „a“, „w“, „s“, „d“ über den Bildschirm bewegen und mit der Leertaste anhalten.

Wir verwenden verschiedene Projektionen des Sprites

Fügen Sie den folgenden Code am Ende der Prozedur TFormMain.FormKeyPress hinzu

  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; 

Durch Ändern der OffsetY- Eigenschaft in Abhängigkeit vom Verschiebungsvektor wird das Bild in Bewegungsrichtung gedreht.

Alle UnitMain-Modultext
 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. 

Anstelle eines Nachwortes

Dieses einfache Beispiel beansprucht keine hohen Bewertungen für Geschwindigkeit oder Benutzerfreundlichkeit. Wenn jemand, wie im vorherigen Artikel , in den Kommentaren mitteilen möchte, dass die Animation falsch ausgeführt werden muss - begrüßen Sie, schreiben Sie Ihren Artikel. In diesem Artikel geht es darum, wie Sie Animationen in mehreren Codezeilen erstellen können, ohne spezielle Bibliotheken zu verwenden, praktisch „auf dem Knie“. Diese Methode wurde in der Praxis getestet und funktioniert wirklich. Bevor Sie kritisieren und „minus“, lesen Sie bitte noch einmal, worum es in diesem Artikel geht und warum.

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


All Articles