Lazarus-使用TImageFragment组件的简单动画

而不是前言

在我最近的Lazarus文章中-编写用于sprite动画的组件,我描述了创建简单的TImageFragment组件的过程,该组件允许您显示给定的图像片段。

继续选择的主题,在本文中,我想展示使用此组件在Lazarus开发环境( 官方网站 )中制作精灵动画的容易程度。

使用这种方法,将不同投影中的各个动画帧放置在同一图像上,并且用于显示子画面的组件使用OffsetXOffsetY属性(图像片段左上角的水平和垂直偏移)仅显示该图像的一个选定片段。

这些现成的图像很多都可以在Web上找到,例如, 在此站点上

选择(并准备)图像

对于我的示例,我选择了以下图像:

-这只凤凰鸟非常有力地拍打着翅膀。

如您所见,每行包含4个投影的4个框架。 通过仅更改OffsetX ,您可以使鸟拍打其翅膀,并且要更改投影就足以更改OffsetY 。 逐行的帧分隔大大简化了动画的编程。

该图像的尺寸为384x384,每帧的尺寸为96x96。 不幸的是,直接使用此图像会使人为失真:图像的某些帧被放置为使其边缘落在相邻的帧上,并且在动画过程中,黄色笔触在子画面的边缘闪烁。

为了修复这些缺陷,我使用了免费的跨平台图形编辑器GIMP官方网站 )。 所有要做的就是除去图像掉落在相邻帧上的地方的突出像素。

更正后的文件如下所示:



-用肉眼看不见差异,但是第二个选项可以在没有伪影的情况下工作。

创建一个新项目

1.创建一个类型为“应用程序”的新项目。

默认情况下,IDE创建一个名为“ project1”的项目,该项目立即创建一个名为“ unit1”的程序模块,该模块描述一个名为“ TForm1”的类,并声明一个名称为“ Form1”的实例。

通常,在创建新对象时,IDE会为它们分配相似的名称,包括对象类型的名称和序列号。 我认为重命名所有此类对象是一种好方法,为它们赋予有意义的名称以反映对象的作用或目的。

因此,根据所选精灵的名称,我们的项目将不会被称为“ project1”,而将被称为“ Phoenix”。

2.保存我们的新项目。

建议将每个项目保存在一个单独的目录中,并使用与该项目名称匹配的名称。 在保存过程中,我们指定要保存的目录(如有必要,我们在此处创建),然后指定项目文件的名称和程序模块的文件名。 我创建了“ Phoenix”文件夹,并将项目文件(“ Phoenix.lpi”而不是建议的“ project1.lpi”)和程序模块文件(“ UnitMain.pas”而不是建议的“ unit1.pas”)保存在那里。

字符大小写细微差别
Windows的Lazarus版本使程序模块的文件名小写:“ unitmain.pas”,但是模块的程序名保留字符的原始大小写:“ unit UnitMain;”。 项目文件不会发生这种情况;文件名保留了字符的原始大小写。

3.重命名表格并更改其标题。

新创建的表单称为“ Form1”( Name属性),是“ TForm1”类的实例,并包含标题“ Form1”( Caption属性)。 将窗体的名称属性更改为“ FormMain”,并且类名称将更改为“ TFormMain”。

标题属性更改为“凤凰”,以便在窗口标题中显示项目标题。

4.结果,我得到了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.编译,运行项目(键<F9>):



将精灵放在表格上

假设您已经安装了TImageFragment组件(如我之前的Lazarus文章所述), 我们为Sprite动画编写了一个组件,请在组件面板上选择“游戏”选项卡,然后将“ TImageFragment”组件添加到表单中。

使用“ 图片”属性,将图像(凤凰鸟的固定版本)加载到组件中。 此外,我们还更改了新对象的以下属性:

  • 将“ 高度”和“ 宽度”属性设置为96
  • 将属性LeftTop设置为0(方便与我的屏幕截图匹配)
  • 名称属性从不方便的“ ImageFragment1”更改为简单易懂的“ Sprite”

如果一切正确,则组件将显示图像的第一帧:


UnitMain模块的文本将进行较小的更改:
-ImageFragment模块添加到了uses部分

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

-一个新的对象将出现在类声明中

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


添加动画-机翼襟翼

1.将TTimer类的新组件添加到表单

该组件位于组件面板的“系统”选项卡上。 您可以将其放置在表单上的任何方便位置,因为它不会显示在正在运行的应用程序中。

2.重命名添加的对象。

新对象自动获得名称“ Timer1”,但我们将其重命名为“ TimerLive”。 给对象起这样的名字通常很方便,它由两部分组成:第一部分反映对象的类,第二部分反映对象的目的。

3.将“ 间隔”属性从1000更改为100。

让此动画的帧每100毫秒(即每秒10次)相互替换。 将来,可以更改此属性,以减慢或加快翼展-由程序员决定。

4.添加一个OnTimer事件处理程序。

最简单的方法是双击新的TimerLive对象的图标。 作为此操作的结果,IDE本身将在表单类声明中添加一个新过程,将该过程链接到对象属性,并将新过程的主体添加到实现部分(并将光标放置在新过程中的beginend关键字之间 )。

5.在新过程中添加一行代码。

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

这些操作的结果是,类声明应类似于以下内容:

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

新过程-OnTimer事件处理程序应如下所示:

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

编译并运行该应用程序后,您可以观看Phoenix鸟拍打翅膀。

发生这种情况是因为计时器事件处理程序每​​100毫秒周期性地更改所显示片段的偏移量,并且所选帧会水平移动,从而顺序显示已加载图像顶行的4帧。 进行除法运算的余数-进行运算可防止偏移量超出图像大小,因此,第4帧后跟第1帧。

在窗口周围添加精灵运动

1.将数学模块添加到使用部分

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

2.将新变量和常量添加到类声明中。

要保存在窗口中移动精灵的矢量,请添加TPoint类型的变量

  private FVector: TPoint; 

在同一位置,我们声明一个常量,用于定义速度模块

  const Speed = 10; 

3.将TTimer类的另一个组件添加到表单中

我提醒您:此组件位于组件面板的“系统”选项卡上。

新对象再次自动获得名称“ Timer1”,我们将其重命名-这次是“ TimerMove”。 第二个计时器的目的是控制子画面的移动。 我没有将两个过程(动画和运动)都绑定到同一计时器,因此可以分别设置每个计时器-例如,在不减慢机翼运动速度的情况下减慢机翼摆动的频率,等等。

4.将“ 间隔”属性从1000更改为100。

让此计时器也每100毫秒(即每秒10次)触发。 将来,还可以更改此属性,以减慢或加快渲染精灵移动事实的频率。

5.添加一个OnTimer事件处理程序

对于更改,这次我建议通过在新的TimerMove对象的“事件”选项卡上的OnTimer事件上双击来完成此操作。 上次,由于此操作,IDE本身将在表单类声明中添加一个新过程,将该过程链接到对象属性,并将新过程的主体添加到实现部分(并将光标放置在该新过程内部,在键之间)单词开头结尾 )。

6.在新过程中添加两行代码。

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

使用Max()和Min()函数可防止Sprite退出表单(主应用程序窗口)。
为了使用这些功能,我们将Math模块连接到了uses部分。

7.添加一个OnKeyPress事件处理程序

选择表单(单击所有添加的组件之外的窗口布局的灰色矩形),然后在“事件”选项卡上找到OnKeyPress事件。 通过双击事件处理程序的空值,我们创建并分配一个新过程-事件处理程序。

8.在新过程中添加几行代码。

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

这些操作的结果是,类声明应类似于以下内容:

  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; 

新过程-OnTimerOnKeyPress事件处理程序应如下所示:

 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; 

编译并运行该应用程序后,您可以使用“ a”,“ w”,“ s”,“ d”键在屏幕上移动Phoenix鸟,并使用空格键将其停止。

我们使用精灵的不同投影

将以下代码添加到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; 

根据位移矢量更改OffsetY属性会导致图像沿移动方向旋转。

所有单元主模块文本
 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. 

而不是后记

这个简单的例子并没有要求很高的速度或可用性。 如果有人(如上一篇文章中所述)想要在评论中告诉您动画需要做错-欢迎,写您的文章。 本文的主题是如何用几行代码制作动画,而无需使用任何特殊的库,实际上是“一on而就”。 该方法已经过实践测试,确实有效,因此在批评和“减”之前,请重新阅读本文的内容以及原因。

Source: https://habr.com/ru/post/zh-CN439732/


All Articles