Создание Анимации в Delphi

В этом примере показано, как, объеденив классы Delphi 5 с функциями Win32 GDI, можно добиться анимации упрощенного избражения эльфа.

Исходные тексты можно взять здесь.


unit MainFrm;  
interface  
uses  
SysUtils, WinTypes, WinProcs, Messages, Classes,  
Graphics, Controls, Forms, Dialogs, Menus, Stdctrls;  
{$R SPRITES.RES } {Привязка растровых изображений к 
исполняемому файлу.}  
type  
TSprite = class  
private  
FWidth: integer;  
FHeight: integer;  
FLeft: integer;  
FTop: integer;  
FAndImage, FOrImage: TBitMap;  
public  
property Top: Integer read FTop write FTop;  
property Left: Integer read FLeft write FLeft;  
property Width: Integer read FWidth write FWidth;  
property Height: Integer read FHeight write FHeight;  
constructor Create;  
destructor Destroy; override;  
end;  
  
TMainForm = class(TForm)  
procedure FormCreate(Sender: TObject);  
procedure FormPaint(Sender: TObject);  
procedure FormDestroy(Sender: TObject);  
private  
BackGnd1, BackGnd2: TBitMap;  
Sprite: TSprite;  
GoLeft, GoRight,GoUp,GoDown: boolean;  
procedure MyIdleEvent(Sender: TObject;  
var Done: Boolean);  
procedure DrawSprite;  
end;  
  
const  
BackGround = 'BACK2.BMP';  
var  
  
MainForm: TMainForm;  
  
implementation  
  
{$R *.DFM}  
constructor TSprite.Create;  
begin  
inherited Create;  
{ Создание растров для хранения изображений эльфа, 
которые будут использованы при выполнении операции 
AND/OR (И/ИЛИ) для содания анимации }  
FAndImage := TBitMap.Create;  
FAndImage.LoadFromResourceName(hInstance, 'AND');  
  
FOrImage := TBitMap.Create;  
FOrImage.LoadFromResourceName(hInstance, 'OR');  
  
Left := 0;  
Top := 0;  
Height := FAndImage.Height;  
Width := FAndImage.Width;  
end;  
  
destructor TSprite.Destroy;  
begin  
FAndImage.Free;  
FOrImage.Free;  
inherited Destroy;  
end;  
  
  
procedure TMainForm.FormCreate(Sender: TObject);  
begin  
// Создание исходного фонового изображения  
BackGnd1 := TBitMap.Create;  
with BackGnd1 do  
begin  
LoadFromResourceName(hInstance, 'BACK');  
Parent := nil;  
SetBounds(0, 0, Width, Height);  
end;  
  
// Создание копии фонового изображения  
BackGnd2 := TBitMap.Create;  
BackGnd2.Assign(BackGnd1);  
  
// Создание изображения эльфа  
Sprite := TSprite.Create;  
  
// Инициализация переменных направления  
GoRight := true;  
GoDown := true;  
GoLeft := false;  
GoUp := false;  
  
{ Установка события приложения OnIdle равным значению 
MyIdleEvent, с которого начнется движение эльфа }  
Application.OnIdle := MyIdleEvent;  
// Установка высоты и ширины области клиента формы  
ClientWidth := BackGnd1.Width;  
ClientHeight := BackGnd1.Height;  
end;  
  
procedure TMainForm.FormDestroy(Sender: TObject);  
begin  
// Освобождение всех объектов, созданных в конструкторе  
формы FormCreate()  
BackGnd1.Free;  
BackGnd2.Free;  
Sprite.Free;  
end;  
  
procedure TMainForm.MyIdleEvent(Sender: TObject;  
var Done: Boolean);  
begin  
DrawSprite;  
{ Разрешение вызова события OnIdle даже при отсутствии 
сообщений в очереди сообщений приложения }  
Done := False;  
end;  
  
procedure TMainForm.DrawSprite;  
var  
OldBounds: TRect;  
begin  
  
// Сохранение границ эльфа в объекте OldBounds  
with OldBounds do  
begin  
Left := Sprite.Left;  
Top := Sprite.Top;  
Right := Sprite.Width;  
Bottom := Sprite.Height;  
end;  
  
{ Теперь изменяем границы эльфа, чтобы он двигался в одном 
направлении, или изменяем направление при 
соприкосновении с границами формы }  
with Sprite do  
begin  
if GoLeft then  
if Left > 0 then  
Left := Left - 1  
else begin  
GoLeft := false;  
GoRight := true;  
end;  
  
if GoDown then  
if (Top + Height) < self.ClientHeight then  
Top := Top + 1  
else begin  
GoDown := false;  
GoUp := true;  
end;  
  
if GoUp then  
if Top > 0 then  
Top := Top - 1  
else begin  
GoUp := false;  
GoDown := true;  
end;  
  
if GoRight then  
if (Left + Width) < self.ClientWidth then  
Left := Left + 1  
else begin  
GoRight := false;  
GoLeft := true;  
end;  
end;  
{ Стираем исходное изображение эльфа на фоне BackGnd2 
путем копирования прямоугольника из фона BackGnd1 }  
with OldBounds do  
BitBlt(BackGnd2.Canvas.Handle, Left, Top, Right, Bottom,  
BackGnd1.Canvas.Handle, Left, Top, SrcCopy);  
{ Теперь рисуем эльфа на "внеэкранном" растре, тем самым 
избавлясь от мерцания }  
with Sprite do  
begin  
{ Создадим черное пятно с силуэтом эльфа с помощью 
операции логического И, выполненной над растрами 
FAndImage и BackGnd2 }  
BitBlt(BackGnd2.Canvas.Handle, Left, Top, Width, Height,  
FAndImage.Canvas.Handle, 0, 0, SrcAnd);  
// Выполним заливку черного пятна исходными цветами эльфа  
BitBlt(BackGnd2.Canvas.Handle, Left, Top, Width, Height,  
FOrImage.Canvas.Handle, 0, 0, SrcPaint);  
end;  
{ Копируем эльфа в его новой позиции на канву формы. 
При этом используется прямоугольник, который немного 
больше, чем нужно для фигуры эльфа. Тем самым мы 
добиваемся эффективного стирания эльфа путем его 
перезаписи, после чего рисуем нового эльфа в новой 
позиции с помощью доного вызова функции BitBlt }  
with OldBounds do  
BitBlt(Canvas.Handle, Left-2, Top-2, Right+2, Bottom+2,  
BackGnd2.Canvas.Handle, Left-2, Top-2, SrcCopy);  
end;  
procedure TMainForm.FormPaint(Sender: TObject);  
begin  
// Рисуем фоновой изображение при закрашивании формы  
BitBlt(Canvas.Handle, 0, 0, ClientWidth, ClientHeight,  
BackGnd1.Canvas.Handle, 0, 0, SrcCopy);  
end;  
  
end.  

Как это работает. Анимационный проект состоит из фонового изображения и нарисованного на нем эльфа в виде летающего блюдца, которое перемещается в пределах области клиента фона. Фон представлен растровым изображением разбросанных по небу звезд (рис 1).

Эльф составлен из двух растров размером 64x32. О них речь пойдет ниже, а пока рассмотрим, что происходит в программе. В приведенном модуле определяется класс TSprite, который содержит поля, предназначенные для хранения позиций эльфа на изображении фона, и два объекта типа TBitmap для хранения растровых изображений эльфа. Конструктор TSprite.Create создает оба экземпляра класса TBitmap и загружает их реальными растрами. Оба растровых изображения эльфа и фоновый растр содержатся в файле ресурсов, который привязывается к проекту путем включения в основной модуль следующей инструкции: { $R SPRITES.RES }.

После загрузки растра устанавливаются границы изображения эльфа. Деструктор TSprite.Destroy освобождает оба экземпляра растра. Главная форма содержит два объекта типа TBitmap, объект TSprite и индикаторы напрвлений, задающие линию движения эльфа. Кроме того, в главной форме определены два метода: MyIdleEvent(), служащий обработчиком событий Application.OnIdle, и DrawSprite(), предназначенный для рисования изображения эльфа.

Обработчик событий FormCreate() создает оба экземпляра класса TBitmap и загружает каждый одним и тем же растровым изображением (зачем - разберемся чуть ниже). Затем создается экземпляр класса TSprite, устанавливаются значения индикаторов направлений и обработчику событий Application.OnIdle назначается метод MyIdleEvent(). Наконец, обработчик событий формы FormCreate() изменяет размеры формы в соответствии с размерами фонового изображения.

Метод FormPaint() выполняет рисование на канве фона BackGnd1.

Метод FormDestroy() освобождает экземпляры классов TBitmap и TSprite.

Метод MyIdleEvent() вызывает метод DrawSprite(), который перемещает и рисует эльфа на существующем фоне.

Метод MyIdleEvent() вызывается, когда приложение находится в состоянии ожидания, т.е. когда пользователь не выполняет никаких действий, на которые приложению следовало бы отреагировать.

Метод DrawSprite() изменяет расположение эльфа на изображении фона. Для этого требуется выполнить немало инструкций - ведь сначала нужно стереть старое изображение эльфа, а затем нарисовать его на новом месте, сохраняя цвет фона вокруг реального изображения эльфа. Кроме того, метод DrawSprite() должен выполнить эти действия без мерцания. Для достижения поставленных целей процесс рисования выполняется на "внеэкранном" растре BackGnd2. Растры BackGnd2 и BackGnd1 являются точными копиями фонового изображения, однако BackGnd1 никогда не модифицируется (поэтому его можно назвать чистой копией фона). По завершении рисования модифицированная область растра BackGnd2 копируется на канву формы. Это позволяет за одно обращение к функции BitBlt() выполнить как стирание на канве формы, так и рисование эльфа в новой позиции. Какие же опреции выполняются с растром BackGnd2?

Во-первых, из BackGnd1 в BackGnd2 копируется прямоугольный участок, превышающий по размерам область, занимаемую самим эльфом. Тем самым гарантируется стирание изображения эльфа с растра BackGnd2. После этого растр FAndImage копируется в BackGnd2 на его новой позиции с помощью поразрядной опреции AND (логическое И). Это приводит к созданию черного пятна с силуэтом эльфа, но с сохранением цветов в области растра BackGnd2, окружающей черный силуэт. Растр FAndImage показан на (рис 2).

На рис 2 эльф представлен черными пикселами, а изображение вокруг эльфа состоит из белых пикселей. Черный цвет имеет значение, равное 0, а белый - 1. В табл. 1 и 2 приведены результаты выполнения опреции AND с белым и черным цветами.

Табл. 1 "Опреция AND с черным цветом"Фон Значение Цвет

BackGnd2 1001 Некоторый цвет
FAndImage 0000 Черный
Результат 1001 Черный
Табл. 2 "Операция AND с белым цветом"Фон Значение Цвет

BackGnd2 1001 Некоторый цвет
FAndImage 1111 Белый
Результат 1001 Некоторый цвет
Эти таблицы показывают, как выполнение операции логического И приводит к зачернению области, занимаемой эльфом на растре BackGnd2. В табл. 1 столбец "Значение" представляет цвет пискселя. Если пиксель на растре BackGnd2 содержит некоторый произвольный цвет, то объединение этого цвета с черным при использовании оператора AND заставит этот пиксель полностью почернеть. Аналогичная операция, выполненная над тем же цветом и абсолютно белым "коллегой" никак не отразится наисходном цвете, как видно в табл. 2. А поскольку цвет фона, на котором находится эльф в растре FAndImage, был белым, то пиксели на растре BackGnd2 копируются без изменения своих цветов. После копирования растра FAndImage в объект BackGnd2 растр FOrImage должен быть скопирован в то же самое место растра BackGnd2, чтобы заполнить черное пятно, созданное объединением растра FAndImage с реальными цветами эльфа. Растр FOrImage таже имеет прямоугольник, окружающий реальное изображение эльфа. И вновь мы сталкиваемся с задачей получения цветов эльфа для растра BackGnd2 и одновременным сохранением цветов этого растра в области, окружающей эльфа. Это достигается объединением растров FOrImage и BackGnd2 с использованием оператора OR (лигическое ИЛИ).

Обратите внимание на то, что область, окружающая изображение эльфа, окрашена в черный цвет. В табл. 3 показаны результаты выполнения операции ИЛИ с растрами FOrImage и BackGnd2. Из табл. 3 следует, что если растр BackGnd2 содержит произвольный цвет, то после операции лигического сложения с черным цветом останется тот же цвет растра BackGnd2.

Табл. 3 "Операция OR с черным цветом"Фон Значение Цвет

BackGnd2 1001 Некоторый цвет
FOrImage 0000 Черный
Результат 1001 Некоторый цвет
Напомним, что все рисование выполняется на "внеэкранном" растре. По завершении рисования достаточно только одного обращения к функции BitBlt(), чтобы стереть и скопировать изображение эльфа. В описанном способе создания анимации нет ничего необычного. Вы можете сами расширить функциональные возможности класса, связанные с переме.
Теги:
анимация, специальные эффекты
Добавлено: 07 Августа 2018 08:16:36 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...