Lazarus - Schreiben einer Komponente für Sprite-Animationen

Anstelle des Vorworts

An der Schule in Odessa verwenden Schüler der 8. Klasse im Informatikunterricht die kostenlose plattformübergreifende Entwicklungsumgebung Lazarus ( offizielle Website ), die der von vielen Delphi geliebten Version sehr ähnlich sieht und aussieht. Sie verwenden die Version von Object Pascal namens Free Pascal und vereinfachen den Einstieg in die Programmierung erheblich.

Kinder sind jedoch nicht daran interessiert, ein Programm zur Berechnung der Schwerkraft mit der Formel F = mg zu schreiben, das ihnen noch nicht klar ist. Fast alle Kinder, denen ich das Programmieren beibringen wollte, möchten ab der ersten Lektion ein Spiel schreiben. Glücklicherweise eignet sich Lazarus hervorragend zum Schreiben einfacher Spiele.

Um animierte Sprites zu erstellen, brauchte ich zwar eine Komponente, die ein beliebiges Fragment des Bildes anzeigt (das mehrere verschiedene Projektionen desselben Charakters in verschiedenen Bewegungsphasen darstellt), aber es gibt keine solche Komponente in der Standardauslieferung. Es stellte sich als sehr einfach heraus, es selbst zu schreiben, und ich möchte in diesem Artikel über diese Technologie sprechen.

Um in Lazarus (wie in Delphi) unterhaltsame Grafikinhalte anstelle eines trockenen Business-Satzes von Standardkomponenten anzuzeigen, befinden sich auf der Registerkarte Zusätzliche drei Komponenten:
- TImage (Anzeigen eines Bildes aus einer beliebigen Datei);
- TShape (Anzeige eines von mehreren vordefinierten grafischen Grundelementen);
- TPaintBox (Anzeige einer Leinwand, auf der Sie programmgesteuert zeichnen können).

Das Spektakulärste für einen Schüler ist, ein kleines Sprite in TImage zu laden und ein Programm zu schreiben, um es auf dem Bildschirm zu bewegen - entsprechend Maus- / Tastaturereignissen, automatisch in einer Schleife oder automatisch durch ein Ereignis von einem Timer.

Sobald dies zu funktionieren beginnt, hat der Schüler die folgende berechtigte Frage: Ist es möglich, den Charakter in Bewegung zu setzen? Und ist es möglich, dass er uns nicht ständig ansieht, sondern sich in die Richtung dreht, die mit der Bewegungsrichtung übereinstimmt?

Im Web finden Sie eine große Anzahl vorgefertigter Bilder zur Verwendung in der Spieleentwicklung. Und viele Charaktere sind in mehreren Projektionen und mehreren Animationsrahmen vorgefertigt (wie zum Beispiel hier auf dieser Site ).

Hier ist ein Beispiel für ein Bild, in dem die Sprites in Form einer Tabelle angeordnet sind, in der jede Zeile einer bestimmten Projektion und jede Spalte einer bestimmten Animationsphase entspricht:


Warum so viele Bilder?
Um ein solches Sprite anzuzeigen, reicht es aus, eine einfache Komponente auf dem Bildschirm zu platzieren, die nicht das gesamte Bild, sondern nur ein Fragment davon anzeigt. Wenn Sie dann die Verschiebung des ausgewählten Fragments horizontal und vertikal ändern, können Sie den Charakter in verschiedene Richtungen drehen und zyklische Bewegungen ausführen (z. B. Flügel schlagen oder Schritte mit Beinen). Diese Technik wird häufig in der Webentwicklung verwendet: Selbst einfache Sätze von Symbolen für Geschäftsgrafiken werden häufig in einer Datei platziert und an verschiedenen Stellen auf Seiten mit unterschiedlichen Offsets angezeigt, wodurch der Eindruck unterschiedlicher Bilder entsteht.

Leider erlaubt die TImage-Komponente, die Teil der Standardverteilung von Lazarus (und Delphi) ist, nicht, ein beliebiges Fragment eines Bildes anzuzeigen: Wenn wir seine Eigenschaften ändern, können wir es zwingen, nur das gesamte Bild, die obere linke Ecke oder seinen zentralen Teil anzuzeigen. Um ein beliebiges Fragment des Bildes anzuzeigen, das durch den Versatz und die Bemaßungen entlang beider Achsen definiert ist, benötigen Sie eine andere Komponente. Aber wie sich herausstellte, ist es überhaupt nicht schwierig, es in Lazarus selbst zu tun!

Erstellen Sie eine neue Komponente

Als Anleitung zum Erstellen von Komponenten habe ich den offiziellen Leitfaden verwendet .

Dort ist alles ausreichend detailliert geschrieben, eine Vervielfältigung macht keinen Sinn. Ich werde nur auf einige Punkte eingehen.

1. Der Standard-Projektassistent bietet uns nicht an, ein Paket zu erstellen und irgendwie auf den Editor zuzugreifen. Wählen Sie „Neues Projekt“ (in der russischen Version - „Neues Projekt“).


und dann "Bewerbung" (in der russischen Version - "Bewerbung"):


2. Wenn Sie gemäß den Anweisungen weiter handeln, wählen Sie im Menü „Paket“ (in der russischen Version - „Paket“) den oberen Punkt „Neues Paket ...“ (in der russischen Version - „Neues Paket ...“) aus und wählen Sie den Dateinamen und den Pfad aus zu speichern. Ich habe mein neues Paket "Spiel" genannt und es in einem separaten Ordner mit demselben Namen abgelegt:


Ich habe einen separaten Lazarus / Cmp-Ordner mit der Erwartung erstellt, dass ich möglicherweise mehrere verschiedene Pakete mit Komponenten habe, und bereits den Ordner „Game“ in diesem Ordner erstellt.

Wenn alles richtig gemacht wurde, sollte ein Fenster eines neuen (bisher leeren) Pakets auf dem Bildschirm erscheinen.

3. Fahren Sie gemäß den Anweisungen erneut fort, um eine neue Komponente im Paketfenster zu erstellen, klicken Sie auf die Schaltfläche Hinzufügen (in der russischen Version - „Hinzufügen“) und wählen Sie „Neue Komponente“ in der Dropdown-Liste (in der russischen Version - „Neue Komponente“):


Wir geben TCustomImage als Vorgängerklasse an. Diese Klasse wird tatsächlich zum Implementieren der TImage-Komponente verwendet, unterscheidet sich jedoch darin, dass sie keine veröffentlichten Eigenschaften enthält und es uns ermöglicht, die Eigenschaften festzulegen, die im Designer für unsere Komponente verfügbar sind.

Was sind veröffentlichte Eigenschaften?
Für diejenigen, die dies nicht wissen, werde ich klarstellen, dass veröffentlicht ein Abschnitt der Klasse (wie public) ist, der neue beschreibt oder einfach geerbte Eigenschaften angibt, die in der Programmentwicklungsphase im visuellen Eigenschafteneditor verfügbar sein sollten. Fortgeschrittene Klassen deklarieren in diesem Abschnitt nichts, so dass der Programmierer die Möglichkeit hat, das herauszubringen, was er für richtig hält. Die TImage-Klasse fügt also keine Funktionalität hinzu, sondern platziert nur eine Reihe von Eigenschaften, die vom übergeordneten TCustomImage-Element geerbt wurden, im veröffentlichten Abschnitt. Wir müssen einige dieser Eigenschaften ausblenden, damit wir auch unsere neue Komponente von TCustomImage erben und nur das anzeigen, was der Logik unserer Komponente in veröffentlicht nicht widerspricht.

Symbol (Symbol) für die Komponente
Es wäre ein guter Stil, für jede neue Komponente ein persönliches Symbol zu zeichnen. Da wir jedoch zeigen möchten, wie einfach es ist, lassen wir dieses Feld leer, was zu dem in Lazarus / Delphi verwendeten Standardsymbol für alle selbst erstellten Komponenten in der Symbolleiste führt .
Übrigens enthält die oben erwähnte Anweisung einen separaten Abschnitt zum Erstellen von Symbolen für Komponenten - dies ist für diejenigen, die mit dem "Standard" -Symbol nicht zufrieden sind.

Nachdem Sie alle Felder ausgefüllt haben, klicken Sie auf die Schaltfläche „Neue Komponente erstellen“ (in der russischen Version - „Neue Komponente erstellen“).

Fügen Sie der neuen Komponente Code hinzu.

Unmittelbar nach dem Erstellen einer neuen Komponente sieht der Quellcode folgendermaßen aus:

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. 

Wie erwartet ist die Klassendeklaration vollständig leer und es gibt überhaupt keine Implementierung. Alles, was ist, ist die Komponentenregistrierungsfunktion auf der Registerkarte "Spiel".

Wir müssen mehrere geerbte veröffentlichte Eigenschaften hinzufügen, zwei eigene erstellen und eine virtuelle Funktion neu definieren. Fangen wir an!

0. Im Importabschnitt benötigen wir zwei zusätzliche Module: ExtCtrls und LCLProc - fügen Sie sie dem Verwendungsabschnitt hinzu:

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

1. Fügen Sie eine veröffentlichte Eigenschaftenliste hinzu, die der TImage-Komponente vollständig ähnlich ist, mit Ausnahme einiger Eigenschaften, mit denen Sie den Maßstab und die Position des Bildes ändern können:

  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; 

Aus Gründen der Überzeugungskraft habe ich die Eigenschaften der TImage-Komponente nicht gelöscht, sondern auskommentiert, sie werden jedoch unsere neue TImageFragment-Komponente beeinträchtigen.

2. Fügen Sie der Klassendeklaration zwei neue Eigenschaften hinzu, um die horizontalen und vertikalen Bildversätze festzulegen:

  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; 

und vergessen Sie nicht, der Implementierung der Klasse zwei deklarierte Prozeduren hinzuzufügen:
 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. Wir definieren die virtuelle Funktion DestRect neu:

  public function DestRect: TRect; override; 

und fügen Sie seine Implementierung zur Implementierung der Klasse hinzu:

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

Kompilieren Sie das Paket und erstellen Sie Lazarus neu

1. Klicken Sie im Paketfenster auf die Schaltfläche "Kompilieren" (in der russischen Version - "Kompilieren"). Wenn alles richtig gemacht wurde, wird im Nachrichtenfenster eine grüne Meldung über die erfolgreiche Kompilierung angezeigt. Andernfalls wird die Meldung gelb oder rot angezeigt.

2. Klicken Sie im selben Fenster auf die Schaltfläche "Verwenden" (in der russischen Version - "Verwenden") und wählen Sie den zweiten Punkt "Installieren" im Dropdown-Menü (in der russischen Version - "Installieren"). Das Programm bietet an, die IDE neu zu erstellen und neu zu starten - wir sind uns einig:



3. Nach dem Neustart wird in der Symbolleiste eine neue Registerkarte „Spiel“ angezeigt - ein Symbol für unsere neue Komponente.

Anstelle eines Nachwortes

Im nächsten Artikel von Lazarus - einer einfachen Animation mit der TImageFragment-Komponente - habe ich in 5 Minuten über die Verwendung einer solchen Komponente gesprochen. Erstellen Sie ein Fenster, in dem sich die animierte Figur in verschiedene Richtungen bewegt und in Bewegungsrichtung dreht.

Wenn das Thema für die Leser interessant ist, kann ich diese Reihe durch einen Artikel ergänzen, in dem erläutert wird, wie Sie nach etwas mehr Zeit beispielsweise ein Fußballfeld mit einigen tastaturgesteuerten Fußballspielern erstellen können.

Und wenn genügend Zeit und Lust vorhanden sind, werde ich versuchen, verschiedene Algorithmen zur Charaktersteuerung (z. B. Fußballspieler) zu schreiben und Wettbewerbe zwischen ihnen zu arrangieren!

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


All Articles