четверг, 20 января 2011 г.

опять по программированию


DirectX for C++ Builder  
Там же смотреть DOWNLOADS
Главная страница
Инструкции


-=-
Direct3d 
 
http://www.cyberforum.ru/cpp-builder/thread94238.html 
 
begin
_bitmap:=TBitMap.Create;  _back:=TBitMap.Create;
_bitmap.Width:=85;_bitmap.Height:=68;
_rec1:=Bounds(0,0,85,68);
_bitmap.Canvas.CopyRect(_Rec1,(Sender as TImage).canvas,_Rec1);
_bitmap.TransparentColor:= _bitmap.canvas.pixels[1, 1];
_bitmap.Transparent := True;
_back.LoadFromResourceName(HInstance, 'background');
(Sender as TImage).Canvas.CopyMode:=cmsrcand;
(Sender as TImage).Canvas.CopyRect(_Rec1,_back.canvas,_Rec1);
(Sender as TImage).Transparent:=true;
(Sender as TImage).Picture.Bitmap.TransparentColor:=_bitmap.TransparentColor;
(Sender as TImage).Canvas.CopyRect(_Rec1,_bitmap.canvas,_Rec1);
 
 

-=-

Image1.Transparent   := True;
    Image1.Picture.Bitmap.TransparentMode:= tmFixed;

    Image1.Picture.Bitmap.Width            := Image1.ClientWidth;
    Image1.Picture.Bitmap.Height           := Image1.ClientHeight;
    Image1.Picture.Bitmap.PixelFormat      := pf32bit;
    Image1.Picture.Bitmap.Transparent      := True;
    Image1.Picture.Bitmap.TransparentColor := clWhite;
    Form1.DoubleBuffered := True;



OpenGL Tutorials http://nehe.gamedev.net/

C++ Builder DirectX
http://www.clootie.ru/cbuilder/index.html



Bitmap Functions
http://msdn.microsoft.com/en-us/library/dd183385%28v=VS.85%29.aspx

ROPCODES:
http://msdn.microsoft.com/en-us/library/aa928169.aspx
DWORD MAKEROP4(
  DWORD fore, 
  DWORD back
);

http://forums.randi.org/archive/index.php/t-57654.html
Example using TransparentBlt. I paint the background black, and use white as my transparent color:

case WM_PAINT: {

hdc = BeginPaint(hWnd, &ps);
HBITMAP foo = LoadBitmap (hInst, MAKEINTRESOURCE (IDB_BITMAP1));
HDC memDC = CreateCompatibleDC (hdc);

int save = SaveDC (memDC);

int savedc = SaveDC (hdc);
SelectObject (hdc, GetStockObject (BLACK_BRUSH));
Rectangle (hdc, 0, 0, 500, 500);
SelectObject (memDC, foo);

// BitBlt (hdc, 100, 120, 48, 48, memDC, 0, 0, SRCCOPY);

TransparentBlt (hdc, 100, 120, 48, 48, memDC, 0, 0, 48, 48, RGB (255, 255, 255));
RestoreDC (memDC, save);
RestoreDC (hdc, savedc);
EndPaint(hWnd, &ps);
DeleteObject (foo);
}
break;

Спрайты:
http://reinerstileset.4players.de/vehiclesE.html
http://www.animationfactory.com/en/
ссылки - http://plg.lrn.ru/index.php?sub=links&sort=2
http://www.panelmonkey.org/category.php?id=2



Способы анимации спрайтов (очень полезная ссылка):
http://delphiforfun.org/programs/Delphi_Techniques/animation.htm

Прога:
http://gmakers.ru/index.php?board=51.0


http://www.compdoc.ru/prog/builder/builder/7.shtml
http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/Graphics_TCopyMode.html 


http://docwiki.embarcadero.com/CodeExamples/en/BrushCopy_%28C%2B%2B%29



    TheImage->Canvas->CopyMode = cmSrcInvert;
  TheImage->Canvas->Draw(0, 0, OverlayImage.get());
  TheImage->Canvas->CopyMode = cmSrcCopy;
  if (ShowText == true) PaintAsText(TheImage.get(),  PaintTRect);
  Canvas->CopyMode = cmSrcCopy;
  Canvas->Draw(0, 0, TheImage.get());
 http://delphiworld.narod.ru/base/sprites_work.html
Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Работа со спрайтами


Содержание
  • Введение
  • Спрайты c готовой маской
  • Cпрайты c программной маской Transparent
  • Использование TImageList
  • Использование Direct X
  • Список ссылок
Введение
Для начала нужно разобраться, что же такое спрайт. Вот такое описание я нашел в книге Андрэ Ла Мота:
" Знаете, есть такой газированный напиток... Снова шучу. На самом деле спрайты - это маленькие объектики, которые находятся на игровом поле и могут двигаться. Этот термин прижился с легкой руки программистов фирмы Atari и Apple в середине 70-х годов. Спрайты - это персонажи в играх для ПК, которые могут без труда перемещаться по экрану, изменять цвет и размер "
И так, спрайт это персонаж игры. Не углубляясь в дебри программирования, могу сказать что спрайт это массив из цветов - для простаты представим его как BMP файл или TBitmap, тем более что, этот формат поддерживаемый windows и не содержащий компрессии.
Что нам нужно от спрайта - заставить его появляться на экране и образовывать анимацию. Анимация это не только смена координаты спрайта, но и изменение самой картинки. Следовательно спрайт может иметь не одно изображение, а несколько. Смена их и приводит к анимации.
Как я уже говорил спрайт это матрица. При вписывании в кравдрат ( прямоугольник ) сложного объекта, например волшебника из рисунка ниже, остается свободное пространство. Его заполняют цветом, которого нет в изображении самого объекта. При простом копировании этой матрицы ( или для простоты BMP или TBitmap ) на экран выводится и волшебник и фон под ним. Но нам это не всегда, подчеркну не всегда, нужно. Если спрайт выводится на фон, то он затирает все квадратную область.
Как я уже говорил спрайт это матрица. При вписывании в кравдрат ( прямоугольник ) сложного объекта, например волшебника из рисунка ниже, остается свободное пространство. Его заполняют цветом, которого нет в изображении самого объекта. При простом копировании этой матрицы ( или для простоты BMP или TBitmap ) на экран выводится и волшебник и фон под ним. Но нам это не всегда, подчеркну не всегда, нужно. Если спрайт выводится на фон, то он затирает все квадратную область.


















Не правда ли есть разница, и довольно заметная. При выводе на экран использовался один и тот же рисунок, но все зависит от способа выведения спрайта.
1-й способ ( маг в белом квадрате ) основан на простом копировании одной области памяти в другую.
2-й способ ( маг на фоне ) то же копирование, но интеллектуальное. Копирование происходит по следующему алгоритму: Если цвет копируемого элементы матрицы ( область памяти ) соответствует значению цвета Transparent Color, то копирования не происходит, переходим к следующему элементу.
3-й способ так же основан на копирование области памяти, но с применением логических операций - маски.
Спрайты c готовой маской
Способов вывести спрайт на поверхность экрана много. Рассмотрим один из них. Это способ, когда отдельно рисуется спрайт и отдельно маска. Для этого нам понадобится сам спрайт, его маска и буфер.


















Спрайт


















Маска спрайта
И спрайт и маска должны иметь одинаковый размер, в данном примере 50x50. Для чего нужна маска? Она нужна для того, чтобы при выводе спрайта не затиралось изображение, которое находится под ним. Маску можно заготовить отдельно в BMP файле - более быстрый способ, а можно рассчитать программно.Спрайт и маску помещаем в TBitmap.
Wizard := Tbitmap.Create;
Wizard.Loadfromfile('spr1.bmp'); // Bitmap для спрайта
WizardMask := Tbitmap.Create;
WizardMask.Loadfromfile('spr2.bmp'); // Bitmap для маски
Ну вот, у нас есть спрайт, маска и нам это вывести его на экран. Для этого существует функция Win32Api:
BitBlt (param_1,X1,Y1,dX1,dY1,param_2,X2,Y2,param_3);
  • Param_1 - Handle на поверхность куда выводить.
  • X1,Y1 - Смещение от начала координат.
  • dX1,dY1 - Размер выводимого изображения.
  • Param_2 - Handle откуда брать.
  • X2,Y2 - Размер выводимого изображения.
  • Param_3 - Параметры копирования.
Для нашего случая:

BitBlt(Buffer.Canvas.Handle,X,Y,50,50, WizardMask.Canvas.Handle,0,0,SrcPaint); 
BitBlt(Buffer.Canvas.Handle,X,Y,50,50, Wizard.Canvas.Handle,0,0,SrcAnd); 
  • SrcPaint - Копировать только белое.
  • SrcAnd - Копировать все кроме белого.
Сначала выводим маску с параметром SrcPaint, а затем в тоже место ( координаты X,Y) сам спрайт с параметром SrcAnd.
Осталось рассмотреть зачем же нужен буфер. При выводе одного спрайта вы не почувствуете мелькания изображения, но когда их будет 100-200 это будет заметно. По этому все спрайты копируются в буфер - это Tbitmap размером с экран или окно, короче изменяемой области. Вот как окончательно будет выглядеть фрагмент программы :
...
var
  Wizard, WizardMask, Buffer: Tbitmap;
  X, Y: integer;
begin
  ...
  Wizard := Tbitmap.Create;
  Wizard.Loadfromfile('spr1.bmp');
  WizardMask := Tbitmap.Create;
  WizardMask.Loadfromfile('spr2.bmp');
  // Копируем маску в буфер BitBlt(Buffer.Canvas.Handle,X,Y,50,50,
  Buffer := Tbitmap.Create;
  WizardMask.Canvas.Handle, 0, 0, SrcPaint);
  // Копируем спрайт в буфер
  BitBlt(Buffer.Canvas.Handle, X, Y, 50, 50, Wizard.Canvas.Handle, 0, 0, SrcAnd);
  ...
  // Перемещаем буфер на форму BitBlt(Form1.Canvas.Handle,0,0,320,240,
  // Buffer.Canvas.Handle,0,0,SrcCopy);
Флаг SrcCopy означает копирование без изменения, аналогичен простому перемещению одного участка памяти в другой.
Не нужно думать, что готовая маска это прошлое компьютерных игр. В любом случае, маска создается, только иногда это делается программно, а иногда заготавливается в виде отдельного файла. Какой вариант лучше, нужно смотреть по конкретному примеру.
Я не буду расписывать все параметры BitBlt, если интересно смотрите сами в Delphi Help. Ну вот и все. Напоследок картина творчества.


















Спрайт
Cпрайты c программной маской - Transparent
Другой метод вывода спрайтов - методом программной маски. Этот способ, немного медленнее, но не требует возни с изготовлением масок. Это не значит, что маски вообще нет. Маска присутствует и создается в памяти.
Для счастливых обладателей Windows NT подойдет способ, который используется в самой ОС. Это функция MaskBlt. Судя по ее названию, она позволяет выводить растры используя битовые маски.
Привиду пример на спрайтах из игры Эпоха Империй I. Наша задача, как и во всех предыдущих примерах, вывести спрайт с Transparent Color (по русски плохо звучит). В игре он черный.


















Начальный вариант спрайта


















Это уже полученная маска


















Вызвали MaskBLT


















MaskBlt + BitBlt
var
  Sprite, Mask: TBitmap;
begin
  Sprite := TBitmap.Create;
  Sprite.LoadFromFile('G0100219.bmp');
  Mask := TBitmap.Create;
  Mask.LoadFromFile('G0100219.bmp');
  Mask.Mask(clBlack); // Создание маски
  // Преобразование в маску, после этого получится Bitmap, представленный
  // на Рис 2
  MaskBlt(Form1.Canvas.Handle, 10, 10, Sprite.Width, Sprite.Height,
    Sprite.Canvas.Handle, 0, 0, Mask.MaskHandle, 0, 0, SRCPAINT);
  // После вызова этой функции, экран выглядит как на рисунке 3.
  BitBlt(Form1.Canvas.Handle, 10, 10, Sprite.Width, Sprite.Height,
    Sprite.Canvas.Handle, 0, 0, SRCPAINT);
end;
С Windows NT все понятно, но как быть в других ОС? ( Хотя возможно, эта функция появится(-лась) в Windows 2000 и Windows Me). Использовать библиотеки сторонних разработчиков. Если они поставляются с исходным кодом, то вы можете перенести необходимые вам процедуры в собственный модуль.
Я нашел самую быструю библиотеку для работы с графикой - Media Library Component Version 1.93. В примере используется только часть ее. Нам понадобится только одна процедура:
DrawBitmapTransparent(param_1,X,Y,param_2,param_3); 
  • param_1 - Canvas, куда копировать
  • X,Y - Смещение
  • param_2 - TBitmap, что копировать.
  • param_3 - TColor, цвет Transparent - этот цвет не будет копироваться
Применение только данной библиотеки не принципиально. Практически в любом наборе VCL компонентов от сторониих производителей есть процедуры или функции для вывода Bitmap с использованием цвета прозрачности. Такие процедуры есть в библиотеке RXLib, LMD Tools, Cool Control и многих других.
Для нашего примера: DrawBitmapTransparent(Buffer.Canvas,WizardX,WizardY,Wizard,clRed); Спрайт должен выглядеть так:


















Небольшое замечание по поводу Transparent. Цвет надо выбирать такой, которого нет на самом спрайте, иначе неизбежны "дырки" в изображении. Лучше всего такой : #00FF00 - ярко зеленый, но можно использовать черный или белый.
В предыдущей главе "Работа спрайта c готовой маской" я подвесил передвижение спрайта на таймер:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  ... // тело цикла
end.
Да cпособ хорош, но не так быстродейственен. Есть еще пара вариантов :
1. Создать поток TThread - в примере разобран именно он.
2. "Подвесить" на IDL
Рассмотрим сначала второй способ т.к. он наименее прогрессивен:) Пишем такую процедуру:
procedure TForm1.Tic(Sender: TObject; var Done: Boolean);
begin
  ...
  // Сюда заносим, что надо исполнять.
  ...
  Done := false;
end;
.... и еще немного:
procedure TForm1.FormCreate(Sender: TObject);
 begin
  ...
  Application.OnIdle := Tic;
end;
Способ быстрее в 1-2 раз чем таймер, но не лишен недостатков. Не буду объяснять почему. Первый способ самый оптимальный для игры, как самой сложной так и простой. Реализуется он с помощью потоков. В игре их можно создать несколько - один для обработки графики, другой для AI, третий для музыки и т.д. У каждого потока свой приоритет, но высший только у одного. При работе с несколькими потоками не забывайте их "прибивать" при выходе из программы.
Сначала заводим новый класс:
TGameRead = class(TThread) // класс для таймера игры
protected
  procedure Execute; override; // Запуск
  procedure Tic; // Один тик программы
end;
// Потом переменную :

var
   ...
  T1: TGameRead;
  ...

// Описываем процедуры класса :

procedure TGameRead.execute;
 begin
  repeat
    synchronize(Tic);
  until
    Terminated
end;

procedure TGameRead.Tic;
 begin
  ...
  // Тут пишем все как в TTimer - OnTime
  ...
end;

// В событии Form1.Create инициализируем поток, и задаем приоритет.
// Расписывать все приоритеты не буду, читайте Delphi Help
// ...и не забываем убрать за собой:

...
T1 := TGameRead.Create(false); // Создаем поток
T1.Priority := TpHighest; // Ставим приоритет
...

procedure TForm1.FormDestroy(Sender: TObject);
 begin
  T1.Suspend; // Приостановим
  T1.Free; // и прибьем
end;

// Ну вот и все. Ах да, вас наверное заинтересовала строчка FPS.
// Так это тоже самое, что выдает Quake на запрос "showframerate"
// или что-то такого плана - количество кадров в секунду.
// Делается это так : заводится переменная:

var
   G: integer;
  ...

// При каждом вызове потока Tic, она увеличивается на единицу:

procedure TGameRead.Tic;
 begin
  ...
  Inc(G); // Увеличиваем значение G
end;

// Создаем таймер с интервалом 1000 - это 1 секунда, и в событии
// OnTime выводим значение G в метку. В значении G будет количество
// вызовов процедуры DoSome за 1 секунду:

procedure TForm1.Timer1Timer(Sender: TObject);
 begin
  label1.caption := 'FPS :' + IntToStr(G);
  G := 0; // Обнуляем G
end;
На моем средненьком Pentium AMD 233 c Intel 740 8M - выдает 90-100 кадров в секунду, при окне 360X360. Для начала неплохо!
P.S. У вас может возникнуть вопрос - почему передвижение спрайта за мышкой. Ответ: наименьшие затраты на писанину тест программы, при неплохом разнообразии движения.


















Использование внешних процедур для Transparent вывода спрайтов, хорошо но есть несколько минусов данного способа:
во первых эти процедуры не слишком оптимизированы - их основное предназначение вывод простеньких элементов приложения, таких как иконок, картинок кнопок и т.д. Хотя это не относится к некоторым библиотекам, код которых на 90% состоит из ассемблера.
во вторых хранить выводимое изображение нужно в bmp файле, хотя подойдет и любой другой, не применяющий компрессию с потерей ( Jpeg) . Если картинок более 1-й, а при нормальной анимации их набирается порядка 150-200 на один юнит, то сложно получать именно нужный участок файла.
Приведу пример
В bmp файле содержатся 8 картинок - 64x64 пикселя. Нужно получить доступ к 6-й картинке ( на рисунке помечена розовым квадратом)- ее координаты будут 128,64


















Чтобы получить следующий кадр анимации, нужно снова ко номеру кадра считать координаты : Не совсем удобно. Все эти проблемы можно решить используя TImageList.
Использование TImageList
Используя этот компонент можно не думать о координатах картинки, цвете прозрачности - он решает сразу две проблемы.
Разберем что нужно сделать, для вывода спрайта с использованием TImageList. Во первых нужно загрузить набор спрайтов TImageList, для этого лучше всего использовать команду:
TImageList.AddMasked(Image: TBitmap; MaskColor: TColor): Integer;
Первый параметр - это Bitmap, второй Transparent Color - цвет прозрачности. Если Вам не нужно использовать цвет прозрачности, то нужно использовать процедуру Add. После загрузки всех картинок, можно приступать к их выводу на экран. Для этого существует процедура:
procedure TImageList.Draw(Canvas: TCanvas; X, Y, Index: Integer);
Первый параметр Canvas на который будет произведена отрисовка, второй и третий координаты для вывода X и Y а четвертый индекс или порядковый номер выводимого изображения.
Для примера:
ImageList1.Draw(Canvas,0,0,6); 
Тот же самое, но с использованием BitBlt:
BitBlt(Canvas.Handle,0,0,64,64,Bitmap_Mask.Canvas.Handle,128,64,SrcPaint); - маска 
BitBlt(Canvas.Handle,0,0,64,64,Bitmap.Canvas.Handle,128,64,SrcAnd; - спрайт 

ImageList1.Draw(Canvas,0,0,6); 
Тот же самое, но с использованием BitBlt:
BitBlt(Canvas.Handle,0,0,64,64,Bitmap_Mask.Canvas.Handle,128,64,SrcPaint); - маска 
BitBlt(Canvas.Handle,0,0,64,64,Bitmap.Canvas.Handle,128,64,SrcAnd; - спрайт 
Думаю пояснять нет нужды, что использовать TImageList лучше, и проще. Пример работы с TImageList описан в файле. Там показана анимация персонажа из игры WarCraft и Warlord III. Я так и не разобрался как работает механизм отрисовки в TImageList. Мои раскопки привели к такой функции :
function ImageList_Draw(ImageList: HImageList; Index: Integer; Dest: HDC;
  X, Y: Integer; Style: UINT): Bool; stdcall;
и
function ImageList_DrawEx(ImageList: HImageList; Index: Integer; Dest: HDC;
  X, Y, DX, DY: Integer; Bk, Fg: TColorRef;
  Style: Cardinal): Bool; stdcall; 
HImageList - Handle на TImageList.
Так как вызывается экспортируемая процедура, находящаяся в библиотеке Comctl32.dll то остается не понятным, какие алгоритмы используются при выводе изображения. Могу только сказать, что при добавлении нового изображения, добавляется как изображение так и маска.
Я заинтересовался данным вопросом и продолжал копать стандартные библиотеки Windows и компоненты. Возможно информация по данным вопросам содержится во многочисленных SDK, выпускаемых Microsoft. В компоненте TFastDIB я наткнулся на процедуру Draw:
procedure TFastDIB.MaskDraw(fDC,x,y:Integer;c:TFColor); 
begin 
  TransBlt(fDC,x,y,Width,Height,hDC,0,0,Width,Height,PDWord(@c)^); 
end; 
Естественно меня заинтересовала процедура TransBlt и вот что я нашел:
function TransBlt(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11:DWord):BOOL; stdcall; 
... 
function CreateDIB; external 'gdi32.dll' name 'CreateDIBSection'; 
function TransBlt; external 'msimg32.dll' name 'TransparentBlt'; 
function AlphaBlt; external 'msimg32.dll' name 'AlphaBlend'; 
Мне захотелось посмотреть, а что еще может библиотека 'msimg32.dll' и вот полный список:
AlphaBlend 
GradientFill 
TransparentBlt 
DllInitialize 
vSetDdrawflag 
Все, хватит, а то некоторые читатели и так ничего не поняли. Но для интересующихся скажу - не все процедуры и функции описаны в Delphi, многое не документировано.
Использование DirectX
Чем плохи рассмотренные выше методы вывода спрайтов - они медленные. Хочу подчеркнуть, что для каждой программы нужно выбирать свои методы написания. Конкретное задание требует своих средств исполнения. То что Microsoft написал библиотеку Direct X не значит что тут же нужно писать всю графику используя ее.
Приведу пример. Самая популярная игра для Windows - Quake II, Warcraft, Diablo - нет САПЕР и ПАСЬЯНС. Можете не верить, но это факт. В первую категорию играют ограниченный контингент людей в последнюю играли ВСЕ. Я это говорю к тому, что если вы пишите графическое приложение, то нужно ориентироваться на его потребности и выбирать соответствующие технологию зависимости от них. Какие это потребности:
необходимость вывода большого количества часто сменяющихся изображений
большой объем графической информации
аппаратная поддержка
максимальное быстродействие
Используя Direct X можно получит все вышеперечисленное. Набор этих библиотек, изначально разрабатывался как средство для работы с графикой. Что было, когда писали под DOS: строили в участке памяти ( back buffer ) какое то изображение или копировали туда спрайты, а потом перемещали этот back buffer в область "экранной" памяти. Сразу отрисовывался весь экран. С приходом Windows, переместить участок памяти в экранную область не возможно. Приходится использовать Canvas, Handle.
DirectX позволяет решить все эти проблемы. Вы можете подготавливать изображение на так называемой поверхности, и потом FLIP и вся поверхность становится видимой - копируется в экранную область видеопамяти. Должен заметить, что алгоритм работы ничуть не меняется.
С появлением DirectX появились и аппаратные поддержки таких необходимых вещей как: Trancparent Color и Bit blitting.
Термин бит-блиттинг означает процесс перемещения группы битов (образа) из одного места экрана в другое или памяти. В играх на ПК нас интересует перемещение образа из области хранения вне экрана в область видеобуфера. Кто интересуется аппаратными возможностями своей видео карты, то их можно узнать достав Microsoft DirectX CPL. В ней можно просмотреть, какие функции в видео карте реализуются аппаратно, а какие програмно.
Итак процесс работы таков, загружаете спрайты на поверхность (ISurface) затем нужно вызвать процедуру BLT или BLTFAST, потом поменять буферную и видимую поверхность командой FLIP и все.
В начале раздела я написал Direct X, но я несколько обманул Вас. Я расскажу как выводить спрайты с помощью Direct X, но с использованием набора VCL компонентов DelphiX . Я это делаю по той простой причине, что если я напишу пример используя стандартные модули DirectX то их некоторые не поймут, отчаяться и бросят программировать вообще :) Согласитесь не все сразу поймут, что делает данная процедура, хотя она всего лишь меняет поверхности.
var
  hRet: HRESULT;
begin
  Result := False;
  while True do
  begin
    hRet := FDDSPrimary.Flip(nil, 0);
    if hRet = DD_OK then
      Break
    else
      if hRet = DDERR_SURFACELOST then
      begin
        hRet := RestoreAll;
        if hRet <> DD_OK then
          Exit;
      end
      else if hRet <> DDERR_WASSTILLDRAWING then
        Exit;
  end;
  Result := True;
end;
По этому я и решил использовать DelphiX. Писать с помошью него очень просто. Нам потребуется всего два компонента. Первый TDXDraw - если объяснить коротко, то это аналог TCanvas. Еще один компонент это TDXImageList - прямой аналог TImageList, единственно все элементы являются TDIB и не содержат ни каких масок.
Что нужно сделать чтобы успешно создать и анимировать спрайт. Как и с TImageList нужно загрузить BMP файл в элемент TDXImageList. Элемент TImageList предварительно нужно создать в программе или создать из Object Inspector.
DXImageList1.Items[0].picture.LoadFromFile('golem_start.bmp');

// Для вывода нужно использовать процедуру: 
DXImageList1.Items[0].draw(DXDraw1.Surface,0,0,6); 
Вот и все. Прямая аналогия с TImageList ... очень удобно переносить код.


















Список ссылок:

Проект Delphi World © Выпуск 2002 - 2004
Автор проекта: ___Nikolay








======================================================================
Description
TCopyMode values describe how to combine the colors of a source bitmap and a destination bitmap.  
The Windows unit defines the following constants for TCopyMode values:
Value 
Meaning 
Fills the destination rectangle on the canvas with black.  
Inverts the image on the canvas and ignores the source.  
Combines the image on the canvas and the source bitmap by using the Boolean AND operator.  
Combines the inverted source bitmap with the image on the canvas by using the Boolean OR operator.  
Copies the inverted source bitmap to the canvas.  
Combines the image on the canvas and the source bitmap by using the Boolean OR operator, and inverts the result.  
Copies the source pattern to the canvas.  
Combines the source pattern with the image on the canvas using the Boolean XOR operator  
Combines the inverted source bitmap with the source pattern by using the Boolean OR operator. Combines the result of this operation with the image on the canvas by using the Boolean OR operator.  
Combines the image on the canvas and source bitmap by using the Boolean AND operator.  
Copies the source bitmap to the canvas.  
Inverts the image on the canvas and combines the result with the source bitmap by using the Boolean AND operator.  
Combines the image on the canvas and the source bitmap by using the Boolean XOR operator.  
Combines the image on the canvas and the source bitmap by using the Boolean OR operator.  
Fills the destination rectangle on the canvas with white.  

==================================
 Программный модуль
К файлу модуля Unit1.h (Листинг 7.5) добавлено описание структуры FigureType, включающей позицию (X,Y) , смещение (DX,DY) фигуры и номер объекта спрайта (SD); а в секции public класса формы объявлены четыре переменные размеров изображений, две фигуры Fig [2] и соответствующие им две пары спрайтов Sprite [4]. Класс SpriteClass и его методы определены в файлах Sprite.h и Sprite.cpp, соответственно.
ttifndef UnitlH
#define UnitlH
#include ttinclude ftinclude
#include ttinclude "sprite.h"
#include ftinclude "sampreg.h" ftinclude

typedef struct { int X, Y, DX, DY, SD; } FigureType;

class TFormI : public TForm (

_published: // IDE-managed Components TPaintBox *PaintBox;
TImage *DrawBox;
TImage *Background;
TImage *Figures;
TTimer *Timerl;
TPanel * Panel 1;
TSpeedButton *SpeedButtonl;
TSpeedButton *SpeedButton2;
TSpeedButton *SpeedButton3;
TSpinEdit *SpinEditl;
TOpenDialog *OpenDialog;

void _fastcall TimerlTimer(TObject * Sender);

void _fastcall SpinEditlKeyUp(TObject *Sender, WORD &Key, TShiftState Shift);

void _fastcall SpinEditlKeyDown(TObject * Sender,
WORD &Key, TShiftState Shifts-void_fastcall SpeedButtonlClick(TObject *Sender) ;

void_fastcall SpeedButton2Click(TObject * Sender);

void_fastcall SpeedButton3Click(TObject * Sender);

private: // User declarations public: // User declarations _fastcall TFormI(TComponent* Owner);

int W, H, w, h;
FigureType Fig[2];
SpriteClass Sprite[4];
};

extern TFormI *Forml;
#endif
Листинг 7.5. Содержание файла Unii1.h.
Файл модуля Unit1.cpp (Листинг 7.6) содержит 5 обработчиков событий от нажатия кнопок управления и обработчик события таймера.
//-_---------_____________-___-_-----__-____________________-
^include
#pragma hdrstop
#include "Unitl.h"
#pragma link "sampreg"
#pragma resource "*.dfm" TFormI *Forml;
//----------------——--——--—------———------—-----------

_fastcall TFormI::TFormI(TComponent* Owner) : TForm(Owner) ( // Конструктор формы устанавливает размеры по умолчанию W = Н = 400; w = h =61;
} //----------------------------------------------------------

void_fastcall TFormI::SpeedButtonlClick(TObject *Sender) { if (OpenDialog->Execute())
{ // Открыть файл изображения фона
Background->Picture->LoadFromFile(OpenDialog->FileName) ;
W = Background->Picture->Width;
H = Background->Picture->Height;
}
} //----—----------——-------—-——---—-—-—-------------

void_fastcall TFormI::SpeedButton2Click(TObject *Sender) { if (OpenDialog->Execute())
{ // Открыть файл изображения фигур
Figures->Picture->LoadFromFile(OpenDialog->FileName);
w = (Figures->Picture->Width)/6;
h = Figures->Picture->Height;
//______---------------------------------------------------

void_fastcall TFormI::SpeedButton3Click(TObject *Sender)
{ // Инициализировать поля структуры обеих фигур Fig[0].X = W/4; Fig[l].X = 3*W/4;
Fig[0].Y = H/4; Fig[l].Y = 3*H/4;
Fig[0].DX = 1; Fig[l].DX = -1;
Fig[0].DY = 1; Fig[l].DY = -1;
Fig[0].SD = 0; Fig[l].SD = 0;
// Подготовить экземпляры спрайта
Sprite[0].SetSprite(Figures->Canvas, 0,0, 4*w,0, w,h);
Sprite[1].SetSprite(Figures->Canvas, 2*w,0, 4*w,0, w,h);
Sprite[2].SetSprite(Figures->Canvas, w,0, 5*w,0, w,h);
Sprite[3].SetSprite(Figures->Canvas, 3*w,0, 5*w,0, w,h) ;
Timerl->Enabled = true;
}

void _fastcall TFormI::SpinEditlKeyUp(TObject *Sender,
WORD &Key, TShiftState Shift) { Timerl->Interval++; } void _fastcall TFormI::SpinEditlKeyDown(TObject *Sender,
WORD &Key, TShiftState Shift) { Timerl->Interval--; } //-.____-_-_-_-_.....____.__________________________________

int Shift = 0; // переменная прокрутки фона void _fastcall TFormI::TimerlTimer(TObject *Sender) ( // Сместить фигуры в пределах периметра
Fig[0].X += Fig[0].DX; Fig[0].Y += Fig[0].DY;

if (Fig[0].X > (W/2-w)) Fig[0].DX = -1;

if (Fig[0].X < 20) Fig[0].DX = 1;

if (Fig[0].Y > (H-2*h)) Fig[0].DY = -1;

if (Fig[0].Y < 20) Fig[0].DY = 1 ;
Fig[l].X += Fig[l].DX; Fig[l].Y += Pig[l].DY;

if (Fig[l].X > (W-w)) Fig[l].DX = -1;

if (Fig[l].X < (W/2+w)) Fig[l].DX = 1;

if (Fig[l].Y > (H-h)) Fig[l].DY = -1;

if (Fig[l].Y < 30) Fig[l].DY = 1 ;
// Оживить фигуры, переключая экземпляр спрайта
Fig[0].SD = (Fig[0].SD == 0) ? 2 : 0;
Fig[l].SD = (Fig[l].SD == 1) ? 3 : 1 ;
// Нарисовать фон и сдвинуть его влево
if (Shift == 0)
{ DrawBox->Canvas->CopyMode = cmSrcCopy;
DrawBox->Canvas->CopyRect(Rect(О, О, W, H),
Background->Canvas, Rect(0, 0, W, H));
}

else
{ DrawBox->Canvas->CopyMod^s = cmSrcCopy;
DrawBox->Canvas->CopyRect(Rect(0, 0, W-Shift, H),
Background->Canvas, Rect(Shift, 0, W, H) ) ;
DrawBox->Canvas->CopyRect(Rect(W-Shift, 0, W, H),
Background->Canvas, Rect(0, 0, Shift, H)) ;
}
Shift += 2;

if (Shift >= W) Shift -= W;
// Нарисовать фигуры на канве внеэкранного битового образа
Sprite[Fig[0].SD].Draw(DrawBox->Canvas,Fig[0].X, Fig[0].Y) ;
Sprite[Fig[l].SD].Draw(DrawBox->Canvas,Fig[1].X, Fig[l].Y) ;
// Скопировать изображение на экран
PaintBox->Canvas->CopyMode = cmSrcCopy;
PaintBox->Canvas->CopyRect(Rect(0, 0, W, H),
DrawBox->Canvas, Rect(0, 0, W, H)) ;
}
Листинг 7 6 Содержание файла Unit1 cpp.
Быстрые кнопки компонент TSpeedButton служат для управления программой. Первая кнопка открывает и загружает объект Background класса TImage из файла с изображением фона (размером w на H). Вторая кнопка открывает и загружает объект Figures класса TImage из файла с изображениями фигур (размером w на h каждая).
Третья кнопка устанавливает фигуры в начальные позиции и подготавливает объекты спрайта посредством обращении к методу SetSprite., а затем запускает таймер.
Нажатие кнопок компоненты редактирования TSpinEdit вызывает увеличение или уменьшение на единицу значения интервала таймера (по умолчанию устанавливается значение 40, соответствующее кадровой частоте 1/25 сек).
Ядром приложения является обработчик события OnTimer объекта Timerl. Первый блок кода реакции на прерывание от таймера отвечает за перемещение фигур, изменяя направление движения всякий раз, когда они "утыкаются" в фиксированные границы отведенного периметра. Рис. 7.3 изображает структуру канвы объектов DrawBox и PaintBox.

Рис. 7.3. Структура канвы для рисования движущихся фигур.
Следующий блок реализует прокрутку фона справа-налево, делая анимацию более реальной (можете называть этот процесс" виртуальной реальностью, если хотите). Фигуры могут сходиться и расходиться, но общее впечатление их движения слева-направо сохраняется. Для начала надо скопировать изображение фона целиком в буфер внеэкранного изображения, а затем организовать циклический сдвиг влево данных буфера на величину, установленную переменной Shift. Заключительный блок рисует, при помощи метода Draw, фигуры на канве невидимого объекта DrawBox, а затем копирует изображение канвы на экран, в видимый объект PaintBox.

7.5.3 Спрайты
Экземпляры класса SpriteClass (Листинг 7.7) обеспечивают анимацию одной маскируемой фигуры. Банк данных спрайта содержит канву невидимой компоненты TImage с битовыми образами фаз. Естественно, что все изображения банка данных спрайта должны быть одинакового размера. Изображения фаз анимации должны иметь черный фон, маска контура - белый.
class SpriteClass
private:
TCanvas *SpriteCanvas;

int ImageLeft, ImageTop;

int MaskLeft, MaskTop;

int Width, Height;

public:
void SetSprite(TCanvas *aSpriteCanvas,

int aImageLeft, int aImageTop, int aMaskLeft, int aMaskTop, int aWidth, int aHeight);

void Draw( TCanvas *aDrawCanvas, int aLeft, int aTop) ;
);
Листинг 7.7. Объявление класса спрайта в файле Spite, h.
В классе спрайта определены только два метода установки и рисования спрайта (Листинг 7.8). При вызове метода SetSprite аргументы aImageLeft и aImageTop определяют позицию спрайта на канве aSpriteCanvas, аргументы aMaskLeft и MaskTop - позицию маски, аргументы aWidth и aHeight - размеры спрайта. При вызове метода Draw аргумент aDrawCanvas задает канву рисования, а аргументы aLeft и aTop - позицию спрайта на этой канве.

void SpriteClass::SetSprite(TCanvas *aSpriteCanvas,
int aImageLeft, int aImageTop, int aMaskLeft, int aMaskTop, int aWidth, int aHeight) { SpriteCanvas = aSpriteCanvas;
ImageLeft = aImageLeft;
ImageTop = aImageTop;
MaskLeft = aMaskLeft;
MaskTop = aMaskTop;
Width = aWidth;
Height = aHeight;


void SpriteClass::Draw(TCanvas *aDrawCanvas,

int aLeft, int aTop) { aDrawCanvas->CopyMode = cmSrcAnd;
aDrawCanvas->CopyRect(Rect(aLeft, aTop,
aLeft+Width, aTop+Height), SpriteCanvas, Rect(MaskLeft, MaskTop, MaskLeft+Width, MaskTop+Height)) ;
aDrawCanvas->CopyMode = cmSrcPaint;
aDrawCanvas->CopyRect(Rect(aLeft, aTop,
aLeft+Width, aTop+Height), SpriteCanvas,
Rect(ImageLeft, ImageTop, ImageLeft+Width.ImageTop+Height));
}
Листинг 7.8. Определение методов класса спрайта в файле Sprite.cpp.
Довольно трудно составить впечатление от работы приложения анимации по снимку одного кадра - тем более в черно-белом исполнении (Рис. 7.4). Книга не является подходящим носителем для распространения компьютерных мультфильмов. Если проблема вас заинтересовала, нет другого способа изучать ее дальше, как придумать и подготовить картинки и разработать собственное приложение, используя приведенные листинги в качестве прототипа.

Рис. 7.4. Снимок с экрана работающего приложения MOVEIT.
Сами фигуры представляют собой массив типа структуры, а не экземпляр какого-то класса. Однако не надо быть гениальным программистом, чтобы ввести логику работы с фигурами в соответствующий класс FigureClass, который будет инкапсулировать, в частности, свойство периметра и метод перемещения. Однако не стоит наследовать SpriteClass от класса фигур. Дело в том, что экземпляры этих классов имеют разную природу: спрайты - это графические объекты, а фигуры - логические конструкции. Если бы вам потребовалось произвести анимацию более, чем двух одинаковых фигур, все они по-прежнему будут разделять единственный класс спрайта, используя поле структуры Fig [ I ] . SD для связи I-фигуры с нужным экземпляром спрайта.
Взгляните на изображения фаз анимации самолета или вертолета. На второй фазе каждой фигуры пропеллер просто отсутствует. Постоянно переключая фазы, можно создать иллюзию вращения пропеллера. Зная, что программа не моделирует фактическое вращение, спросите, тем не менее, у наблюдающего за работой приложения: "В какую сторону вращается пропеллер?" - и любой уверенный ответ убедит вас в том, что время на программирование примера было потрачено не зря. Можно придумать какой-нибудь другой впечатляющий эффект, например, стреляющего пулемета. Расширяя банк данных спрайта дополнительными фазами фигур и соответствующими масками, можно добиваться различных эффектов

Комментариев нет:

Отправить комментарий