VR
Virtual Reality On-line   DirectX
Новости   |     Журнал    |    Хаkер   |     Магазин   |   Проекты
[   Вход    ]
[Kарта сайтa]
[ Download  ]
[  Конкурс  ]
[  Анекдоты ]
[  Ссылки   ]
[  Реклама  ]
[ Почтальон ]
[ О проекте ]






TopList
Delphi+DirectX7,8.
Простейший приём анимации
:

Сегодня я познакомлю тебя с простейшим примером анимации с использованием DirectDraw. Для этого примера я нарисовал вертолёт в нескольких положениях. Точнее сказать, я его не рисовал, а стыбрил, потому что рисовать я не умею. А изюменкой этой статьи будет то, что я покажу тебе как самому сделать искусственое отсечение картинок, не попадающих в пределы экрана.
Logo
Рис 1. Фон

На рисунке 1 показана картинка, которая состоит из трёх вертолётов. Мы загрузим её и будем последовательно менять картинку. Это создаст впечатление летящего вертолёта с вращающимися лопостями.

Если последовательно выводить эти картинки вертолёта, то будет создаваться впечатление, что лопасти крутятся и вертолёт летит. Для большей реалистичности мы будем двигать картинку по экрану, но об этом немного позже.

Чтобы не сильно мучится, загрузи пример в конце статьи, а я здесь только объясню, что происходит. Сегодняшний пример основан на примере из предыдущей статьи, где мы учились выводить на экран прозрачные картинки.

В самом начале я объявил одну запись TVertol в разделе type :

type
  TVertol=record
   vLeft:Integer;
   vTop:Integer;
   vState:Integer;
   vWaitTime:Integer;
  end;
Logo

Она состоит из четырёх свойств:

  1. vLeft - будет указывать на левую позицию вертолёта.
  2. vTop - будет указывать на верхнюю позицию вертолёта.
  3. vState - указывает на состояние вертолёта, а именно на ту картинку, которая должна быть выведена сейчас на экран.
  4. vWaihtTime - эта задержка между появлениями вертолёта на экране.

В разделе private объекта TForm1 я объявил ещё две переменные и одну процедуру:

  FVertolImage      : IDirectDrawSurface7;
  Vertol1           : TVertol;
  procedure InitVertol;

FVertolImage - это поверхность, которая будет хранить картинку с вертолётом.

Vertol1 - это переменная типа структуры TVertol. Она будет хранить в себе состояние нашего пилотируемого аппарата.

InitVertol - в этой процедуре будет происходить инициализация картинки вертолёта. А именно, здесь я буду загружать картинку и выставлять значения по умолчанию. Вот как она выглядит:

procedure TForm1.InitVertol;
begin
 //Загружаю картинку
 FVertolImage := DDLoadBitmap(FDirectDraw, PChar('vertol.bmp'), 0, 0);
 //Выставляю цвет прозрачности.
 DDSetColorKey (FVertolImage, RGB(255, 0, 255));

 Vertol1.vLeft:=-200;//Левая позиция = -200
 Vertol1.vTop:=30;   //Высота = 30
 Vertol1.vState:=0;  //Состояние =0 
 Vertol1.vWaitTime:=100; //Задержка перед вылетом = 100.
end;

Эту процедуру я вызываю самой последней в обработчике события OnCreate.

После этого я создал обработчик события OnIdle. Это событие вызывается, когда приложение свободно и ему нечего делать. Для этого я выделил компонент TApplicationEvents и на закладке Events дважды щёлкнул по этой строке. В нём я написал следующее:

 procedure TForm1.ApplicationEvents1Idle(Sender: TObject;
   var Done: Boolean);
 begin
  if FActive <> True then exit;
  FormPaint(nil);
  Done := False;
 end;

В первой строке происходит проверка, является ли приложение сейчас активным. Если нет, то выход. Если приложение актино, то произвести прорисовку FormPaint(nil) и переменной done присвойть значение false. Если ты не сделаешь последнее действие, то процедура, больше не будет вызываться.

Переменная FActive - это простая булева переменная (boolean). Я присваиваю ей true по событию OnActivate и OnRestore:

procedure TForm1.ApplicationEvents1Activate(Sender: TObject);
begin
 FActive:=true;
end;

procedure TForm1.ApplicationEvents1Restore(Sender: TObject);
begin
 WindowState := wsMaximized;
 FActive := True;
end;

А по событию OnDeactivate присваиваю false:

procedure TForm1.ApplicationEvents1Deactivate(Sender: TObject);
begin
 Application.Minimize;
 FActive := false;
end;

Всё. Теперь изменения в процедуре рисования:

procedure TForm1.FormPaint(Sender: TObject);
var
 hRet : HRESULT;
 bltfx : TDDBLTFX;
 x1,x2:Integer;
 dstR, srcR:TRect;
begin
 ZeroMemory(@bltfx, SizeOf(bltfx));
 bltfx.dwSize := sizeof(bltfx);
 bltfx.dwFillColor := 0;

 hRet := FBackGround.Blt(nil, nil, nil, DDBLT_COLORFILL or DDBLT_WAIT, 
                        @bltfx);
 if hRet = DDERR_SURFACELOST then
  begin
   FPrimarySurface._Restore;
   FImageSurface := DDLoadBitmap(FDirectDraw, '1.bmp', 0, 0);
   FTransparentImage := DDLoadBitmap(FDirectDraw, 'bart.bmp', 0, 0);
   DDSetColorKey (FTransparentImage, RGB(255, 0, 255));
   FVertolImage := DDLoadBitmap(FDirectDraw, PChar('vertol.bmp'), 0, 0);
   DDSetColorKey (FVertolImage, RGB(255, 0, 255));
  end;

 FBackGround.BltFast (175, 75, FImageSurface, nil, DDBLTFAST_WAIT);

 //Если свойство vWaitTime меньше 1 то рисовать
 //Иначе уменьшить это свойство на 1 (это будет ниже,
 //после слова else.
 if Vertol1.vWaitTime<1 then
  begin
   //Увеличить левую позицию вертолёта на 2.
   Vertol1.vLeft:=Vertol1.vLeft+2;
   //Увеличить значение состояния вертолёта
   //Если состояние 0, то выведится первая картинка,
   //если состояние 1, то выведится вторая картинка,
   //если состояние 2, то выведится треться картинка вертоля
   inc(Vertol1.vState);
   //Если состояние больше 2, то присвоить 0.
   //Это потому что состояний три (три картинки вертолёта).
   if Vertol1.vState>2 then Vertol1.vState:=0;

   //Если вертолёт не виден полностью, то обрезать его
   if Vertol1.vLeft<0 then X1:=Abs(Vertol1.vLeft)
   else X1:=0;
   if Vertol1.vLeft+200>800 then X2:=800-Vertol1.vLeft
   else X2:=200;
   //Создаю область источника
   srcR:=Rect(X1, Vertol1.vState*80, X2, Vertol1.vState*80+80);

   //Если вертолёт не виден полностью, то обрезать его
   if Vertol1.vLeft<0 then X1:=0
   else X1:=Vertol1.vLeft;
   if Vertol1.vLeft+200>799 then X2:=799
   else X2:=Vertol1.vLeft+200;
   //Создаю область приёмника
   dstR:=Rect(X1 ,Vertol1.vTop,X2,Vertol1.vTop+80);

   //Вывожу на экран вертолёт.
   FBackGround.Blt(@dstR, FVertolImage, @srcR, DDBLT_KEYSRC, nil);

   //Если вертоль уже улетел, то установить левую позицию в -200
   //И установить задержку в 200.
   if Vertol1.vLeft>800 then
    begin
     Vertol1.vWaitTime:=200;
     Vertol1.vLeft:=-200;
    end;
  end
 else
  Vertol1.vWaitTime:=Vertol1.vWaitTime-1;

 FBackGround.BltFast(mouseX-20, mouseY-47, FTransparentImage, nil,
                  DDBLTFAST_WAIT or DDBLTFAST_SRCCOLORKEY);
 FPrimarySurface.Flip(nil, DDFLIP_WAIT);
end;

Обрати внимание, что вертолёт вылетает чётко из-за экрана. Напомню, недостаток прошлого примера - если картинка не помещается на экран, то она не выводится полностью. В этом примере я проверяю, если вертолёт не помещается полностью, то вывожу только то, что помещается. Для этого служит следующий код:

   //Если вертолёт не виден полностью, то обрезать его
   if Vertol1.vLeft<0 then X1:=Abs(Vertol1.vLeft)
   else X1:=0;
   if Vertol1.vLeft+200>800 then X2:=800-Vertol1.vLeft
   else X2:=200;
   //Создаю область источника
   srcR:=Rect(X1, Vertol1.vState*80, X2, Vertol1.vState*80+80);

   //Если вертолёт не виден полностью, то обрезать его
   if Vertol1.vLeft<0 then X1:=0
   else X1:=Vertol1.vLeft;
   if Vertol1.vLeft+200>799 then X2:=799
   else X2:=Vertol1.vLeft+200;
   //Создаю область приёмника
   dstR:=Rect(X1 ,Vertol1.vTop,X2,Vertol1.vTop+80);

Попробую объяснить, как происходит обрезание. Если левая позиция вертолёта -50, то он не помещается полность. Ширина картинки вертолёта 200 пикселов. Это значит, что первые 50 пикселов будут за пределами экрана. Для обрезания нужна следующая проверка:

 if Vertol1.vLeft<0 then X1:=Abs(Vertol1.vLeft)
 else X1:=0;

Если левая позиция меньше 0, то в переменную X1 записать абсолютное значение переменной Vertol1.vLeft (абсолютное значение - значит без знака). Результат - в X1 будет число 50.

Далее идёт следующая проверка:

 if Vertol1.vLeft+200>800 then X2:=800-Vertol1.vLeft
 else X2:=200;

Если левая позиция + ширина картинки (200) больше 800 (ширины экрана), то обрезать правую позицию картинки. У нас это значение меньше, значит выполнится X2:=200. Результат в X2 будет 200.
Logo
Рис 2. Вертоль

После этого я создаю структуру, которая показывает область копирования из источника: srcR:=Rect(X1, Vertol1.vState*80, X2, Vertol1.vState*80+80). Результат, в srcR будет (50, 0, 200, 80). Это при условии, что Vertol1.vState равно нулю. Если равно 1, то будет выведена вторая картинка, потому что SrcR будет равно (50, 80, 200, 160). Это значит, что нам надо скопировать картинку, начиная с 50 позиции слева, нулевой сверху и по 200 справа и 80-ю снизу. Посмотри на рисунок 2. На нём закрашена красным цветом та область, которая не будет копироваться.

После этого, я точно так же вычисляю область - куда нужно копировать.

 //Следующая проверка присвоит X1 значение 0, потому что
 //Vertol1.vLeft меньше 0 (мы же выбрали значение 50)
   if Vertol1.vLeft<0 then X1:=0
   else X1:=Vertol1.vLeft;
 //Следующая проверка присвоит X2 значение -50+200=150.
   if Vertol1.vLeft+200>799 then X2:=799
   else X2:=Vertol1.vLeft+200;
 //Следующая строка создаст запись dstR с параметрами (0,0,150,80).
   dstR:=Rect(X1 ,Vertol1.vTop,X2,Vertol1.vTop+80);
Logo
Рис 3. Вертоль

Запись - (0,0,150,80) означает, что вертоль будет нарисован начиная с нулевой левой позиции. Посмотри на рисунок 3. Ты увидишь, как это будет выглядить.

Как видишь, на результате картинка выведена обрезанной в левую позицию экрана. Как только вертоль вылетит на экран полностью, он будет просто копироватся без всяких отсечений.

Вот так мы сделали искусственное отсечение.

На сегодня всё. Мы и так хорошо поработали.

 Исходники примера забирай здесь

Для примера нужны новые заголовочные файлы, в которых уже прописана поддержка интерфейсов DirectX7. Я эти файлы разместил в разделе "Скачать".


Copyright©: Horrific aka Флёнов Михаил
Design by FMk group©