而不是前言
在我最近的
Lazarus文章中
-编写用于sprite动画的组件,我描述了创建简单的
TImageFragment组件的过程,该组件允许您显示给定的图像片段。
继续选择的主题,在本文中,我想展示使用此组件在
Lazarus开发环境(
官方网站 )中制作精灵动画的容易程度。
使用这种方法,将不同投影中的各个动画帧放置在同一图像上,并且用于显示子画面的组件使用
OffsetX和
OffsetY属性(图像片段左上角的水平和垂直偏移)仅显示该图像的一个选定片段。
这些现成的图像很多都可以在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
- 将属性Left和Top设置为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本身将在表单类声明中添加一个新过程,将该过程链接到对象属性,并将新过程的主体添加到
实现部分(并将光标放置在新过程中的
begin和
end关键字之间 )。
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;
新过程
-OnTimer和
OnKeyPress事件处理程序应如下所示:
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 = 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} 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而就”。 该方法已经过实践测试,确实有效,因此在批评和“减”之前,请重新阅读本文的内容以及原因。