Большой архив статей, книг, документации по программированию, вебдизайну, компьютерной графике, сетям, операционным системам и многому другому
 
<Добавить в Избранное>    <Сделать стартовой>    <Реклама на сайте>    <Контакты>
  Главная Документация Программы Обои   Экспорт RSS E-Books
 
 

   Программирование -> Delphi / Pascal -> Программирование анимации в Delphi


Программирование анимации в 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).


Рис 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.

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

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

Фон
Значение
Цвет
BackGnd2
1001 Некоторый цвет
FAndImage 0000 Черный
Результат
0000
Черный

Табл. 2 "Операция AND с белым цветом"

Фон
Значение
Цвет
BackGnd2
1001 Некоторый цвет
FAndImage 1111 Белый
Результат
1001
Некоторый цвет

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

   	    					       Рис 3.

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

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

Фон
Значение
Цвет
BackGnd2
1001 Некоторый цвет
FOrImage 0000 Черный
Результат
1001
Некоторый цвет

Напомним, что все рисование выполняется на "внеэкранном" растре. По завершении рисования достаточно только одного обращения к функции BitBlt(), чтобы стереть и скопировать изображение эльфа. В описанном способе создания анимации нет ничего необычного. Вы можете сами расширить функциональные возможности класса, связанные с перемещением и рисованием на родительской канве.



 

 
Интересное в сети
 
10 новых программ
CodeLobster PHP Edition 3.7.2
WinToFlash 0.7.0008
Free Video to Flash Converter 4.7.24
Total Commander v7.55
aTunes 2.0.1
Process Explorer v12.04
Backup42 v3.0
Predator 2.0.1
FastStone Image Viewer 4.1
Process Lasso 3.70.4
FastStone Image Viewer 4.0
Xion Audio Player 1.0.125
Notepad GNU v.2.2.8.7.7
K-Lite Codec Pack 5.3.0 Full


Наши сервисы
Рассылка новостей. Подпишитесь на рассылку сейчас и вы всегда будете в курсе последних событий в мире информационных технологий.
Новостные информеры. Поставьте наши информеры к себе и у вас на сайте появится дополнительный постоянно обновляемый раздел.
Добавление статей. Если вы являетесь автором статьи или обзора на тему ИТ присылайте материал нам, мы с удовольствием опубликуем его у себя на сайте.
Реклама на сайте. Размещая рекламу у нас, вы получите новых посетителей, которые могут стать вашими клиентами.
 
Это интересно
 

Copyright © CompDoc.Ru
При цитировании и перепечатке ссылка на www.compdoc.ru обязательна. Карта сайта.