2D-графика

advertisement
Анимированные cпрайты в TrueSpace
Как создать высокохудожественное изображение не имея хороших навыков
рисования? Раньше все спрайты приходилось рисовать по точкам или в
простеньких редакторах а-ля MS Paint ( хотя году в 92-93 такой редактор
считался вполне нормальным :). С появлением таких пакетов как 3D
Studio, Ray Dream Studio и т.д. положение изменилось. Весь процесс
создания заключается в изготовлении единичной модели, которую потом
можно отрендерить под любым углом и придать необходимые движения.
Но вернемся к практической части. Первое: нужно достать непосредственно
сам пакет 3D графики. Я выбрал TrueSpace 4 по нескольким причинам:
Он не так требователен к ресурсам : вполне нормально работает на P-233 с
32Mb, a 3D Studio нужно 48Mb минимум
Более визуализирован: все операции можно производить прямо на объекте.
Экспортирует в формат Direct3D - *.x
На мой взгляд его проще понять без книжки. Интуитивный интерфейс.
Демо версию TrueSpace 4.3 можно скачать на сервере фирмы-разработчика:
Caligari, или Вы можете поискать программу на многочисленых FTP
архивах. На данный момент доступна версия 5.X, но честно говоря все
необходимые функции есть и в старой версии.
Этап 1 - сделать нужную модель
Здесь можно поступить двумя
путями - скачать готовые модели
со специальных серверов, либо
делать что-то самому. Быстрее
всего достать уже готовую и
изменить ее для своих целей. Но
можно делать с нуля, применяя
различные модификаторы к
стандартным примитивам. В
примере я взял готовую модель
самолета B-25:
Выглядит она впечатляюще.
Маленький совет на : стадии
работы не заливайте модель
текстурами - это сильно тормозит
работу компьютера. Текстуры
наложите только в последнюю
очередь.
Этап 2 - задание движения
Допустим наш самолет
летит только вперед и
делает поворот вправо и
влево. Для этого модель
надо повернуть по ходу
его полета. Вызываем
меню объекта ( правый
клик на значке курсора )
и вводим параметры
поворота модели: X : 0, Y
: 0, Z : -90.
Далее приводим модель в
начальное положение крайне правое. Для этого
установим поворот Y : 45.
Далее инициализация
анимации : нажмите
кнопку 2 на рисунке (
Record ) и запускаем
анимацию - кнопка 3.
Программа запомнила
начальное положение
объекта.
Теперь введем количество
кадров для анимации ( на рис
цифра 1 ) 30 кадров и
развернем самолет в его
конечное положение ( крайне
левое ). Для этого введем Y : 45 градусов. Все - теперь
нажав кнопку Play вы сможете
увидеть поворот самолета.
Поворачивая камеру Вы
можете создать анимацию под
любыми углами. Все зависит
от выбора вида в игре.
Этап 3 - заключительный этап рендеринг
Для некоторых он может быть головной болью из-за нехватки быстродействия.
Каждый кадр анимации записывается в отдельный файл или в видео ролик.
Тут все просто. Главное в свойствах рендеринга поставить цвет фона (
BackGround : Color ) и сглаживание ( AntiAlias : None ).
BackGround - нужно выбрать, такой какого цвета нет на
сомой модели, иначе не возможно
будет выводит спрайты с прозрачным
цветом.
Что из этого получилось, можно увидеть тут.
Единственный минус всех пакетов, и TrueSpace в частности - он создает
на каждый кадр свой отдельный файл. И в конце рендеринга у Вас
получится огромное количество файлов с которыми очень неудобно
работать. Надо склеить каждое движение в один файл и для этого я
написал небольшую программку BMPCreator.
Пользоваться ей очень просто: Вы задаете каталог, где лежат BMP файлы. Задаете
( если понадобится ) отсечение сверху, снизу, справа, слева и отступ между
спрайтами.
Далее, задав выходное имя файла, нажимаете
'Создать' и все отдельные спрайты склеиваются в
один файл.Программа создает временный файл и
Вы сразу можете посмотреть полученную
анимацию, нажав "загрузить". Если Вас все
устраивает, то сохраняйте полученный файл "Сохранить в файл".
Потом его очень удобно грузить в ImageList или в
DirectDrawSurface. На каждое законченное
движение лучше создавать свой файл. Для
компиляции потребуется DelphiX и RXLib. Да,
совсем забыл сказать - скомпилированную
программу я не высылаю, если вы не можете
откомпилировать готовый пример - вам не чего
заниматься созданием игр :)
Изменение стандартной формы
Вас наверное часто удивляла форма окна, которая появляется при загрузке Norton Utilites и
других подобных программ. Она имела не прямоугольный размер и ВЫ задавались
вопросом : как получить такую форму? / Хотя я подозреваю, что это обычный Bitmap/
Оказывается сделать это довольно просто. При этом Ваша форма может иметь самые
замысловатые очертания т.к. все это задается с помощью полигона.
Все делается с помощью одной единственной процедуры :
SetWindowRgn(Handle, R, True);
Handle - указатель на форму, вид которой хотим поменять
R - указатель на регион, для установки R смотрите функцию CreatePolygonRgn
True - флаг, при значении TRUE сразу после установки перерисовка
В примере я задал в качестве региона простой треугольник, но при большей
изобретательности можно создавать очень интересные варианты. Например форму-круг
или эллипс.
Прозрачная форма
Приведу простой пример, как можно использовать данную форму.
Допустим Вам надо сделать программу- напоминалку. Висит эта
форма и на ней постоянно отображаются Ваши встречи, дела,
праздники или другая полезная информация. Получится этакий
Activ Desctop :) Но будет он жрать ресурсов на порядки
меньше.Реализуется все это очень просто.
Переписываем конструктор:
constructor TForm1.Create(AOwner: TComponent);
begin
inherited;
HorzScrollBar.Visible:= False; // убираем сколлбары, чтобы не
мешались
VertScrollBar.Visible:= False; // при изменении размеров формы
RebuildWindowRgn; // строим новый регион
end;
А вот процедура "перестройки" региона
формы:
procedure TForm1.RebuildWindowRgn;
var
FullRgn, Rgn: THandle;
ClientX, ClientY, I: Integer;
begin
// определяем относительные координаты клиентской части
ClientX:= (Width - ClientWidth) div 2;
ClientY:= Height - ClientHeight - ClientX;
// создаем регион для всей формы
FullRgn:= CreateRectRgn(0, 0, Width, Height);
// создаем регион для клиентской части формы и вычитаем его из FullRgn
Rgn:= CreateRectRgn(ClientX, ClientY, ClientX + ClientWidth, ClientY
+ClientHeight);
CombineRgn(FullRgn, FullRgn, Rgn, rgn_Diff);
// теперь добавляем к FullRgn регионы каждого контрольного элемента
for I:= 0 to ControlCount -1 do
with Controls[I] do
begin
Rgn:= CreateRectRgn(ClientX + Left, ClientY + Top, ClientX + Left +Width,
ClientY + Top + Height);
CombineRgn(FullRgn, FullRgn, Rgn, rgn_Or);
end;
SetWindowRgn(Handle, FullRgn, True); // устанавливаем новый регион окна
end;
И самый последний штрих. Если Ваша форма, будет
с изменяемыми размерами, то Вам надо добавить:
procedure TForm1.Resize;
begin
inherited;
RebuildWindowRgn; // строим новый регион
end;
Перемещение формы
Еще один штрих - произвольное перемещение формы, а не за Title Bar. Так
сделано в программе WinAmp. Пишем всего одну процедуру:
procedure TForm1.WMNCHitTest(var M: TWMNCHitTest);
begin
inherited; // вызов унаследованного обработчика
if M.Result = htClient then // Мышь сидит на окне? Если да
M.Result := htCaption; // - то пусть Windows думает, что мышь на caption bar
end;
Если Вам понадобится вызов Popup меню, то можно поступить следующим
образом:
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;Shift:
TShiftState; X, Y:Integer);
const
SC_DragMove = $F012;
begin
// Если нужно вывести меню - выходим, а по левой кнопке просто
перетаскиваем.
if Button=mbRight then exit;
ReleaseCapture;
perform(WM_SysCommand, SC_DragMove, 0);
end;
Полупрозрачная форма
Этот пример написан Григорьевым Антоном, а я просто нашел его и поместил в
этот раздел так ка он очень хорошо подходит для темы нестандартных окон.
Смотрится достаточно эффектно, но единственный минус скорость работы.
Надеюсь, что этот раздел будет постепенно пополняться.
Да, хочется отметить что начиная с Windows 2000 на аппаратном уровне
появилась поддержка прозрачных окон. Точнее сказать прозрачность
поддерживается за счет использования нового графического интерфейса GDI+. Этот интерфейс значительно расширяет функции стандартного GDI, но
наданный момент все его функции раелизуются программно. Этот минус MS
устранит в следующих версиях.
Если вы не очень хорошо разбираетесь в этих примерах, а создать своеобразную
форму все же хочется, то вым поможет набор компонентов FormRgn. Все
описаное выше и многие другие эффекты с формами, можно сделать путем
изменения настроек в Object Inspector. Компоненты снабжены файлом помощи
и примерами.
Работа со спрайтами
Содержание
Введение
Спрайты 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.
Ну вот, у нас есть спрайт, маска и нам это вывести его на
экран. Для этого существует функция 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;
...
Wizard:=Tbitmap.Create;
Wizard.Loadfromfile('spr1.bmp');
WizardMask:=Tbitmap.Create;
WizardMask.Loadfromfile('spr2.bmp');
Buffer:=Tbitmap.Create; // Копируем маску в буфер
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);
...
// Перемещаем буфер на форму
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 (по Русски плохо звучит). В игре
он черный.
Начальный
вариант
спрайта
Рис 1
Это уже
полученная
маска
Рис 2
Вызвали
MaskBLT
Рис 3
MaskBlt + BitBlt
Рис 4
С 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,clRe
d); Спрайт должен выглядеть так:
Небольшое замечание по поводу 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,SrcP
aint); - маска
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 функции. Более подробно работа с данной библиотекой описана в
MSDN.
Использование Direct X
Чем плохи рассмотренные выше методы вывода спрайтов они медленные. Хочу подчеркнуть, что для каждой
программы нужно выбирать свои методы написания.
Конкретное задание требует своих средств исполнения.
То что 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 ... очень удобно
переносить код. Пример использования можно посмотреть
тут.
Быстрая работа с графикой в среде
Windows
Пример быстрой работы с графикой в среде
Windows без использования средств DirectX
Совместимость: Windows 95, 98, NT, 2000, Me,
TrE, XP, Whistler, Tristler :))
type
TfmMain = class(TForm)
pbDraw: TPaintBox;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
procedure CreateBitmap(aSX, aSY: Integer);
procedure RecreateBitmap(aSX, aSY: Integer);
procedure DeleteBitmap;
procedure RestrictSize(var msg: TMessage); message
WM_GETMINMAXINFO;
procedure pbDrawPaint(Sender: TObject);
private
ScrBitmap: TBitmap;
Scr: Pointer;
SX, SY: Integer;
type
TBig = array[0..0] of Integer;
procedure TfmMain.CreateBitmap(aSX, aSY: Integer);
var
BInfo: tagBITMAPINFO;
begin
// Создание DIB
SX := aSX; SY := aSY;
BInfo.bmiHeader.biSize :=
sizeof(tagBITMAPINFOHEADER);
BInfo.bmiHeader.biWidth := SX;
BInfo.bmiHeader.biHeight := -SY;
BInfo.bmiHeader.biPlanes := 1;
BInfo.bmiHeader.biBitCount := 32;
BInfo.bmiHeader.biCompression := BI_RGB;
ScrBitmap := TBitmap.Create();
ScrBitmap.Handle := CreateDIBSection(Canvas.Handle,
BInfo, DIB_RGB_COLORS, Scr, 0, 0);
ZeroMemory(Scr, SX * SY * 4);
end;
procedure TfmMain.DeleteBitmap;
begin
// Удаление DIB
ScrBitmap.FreeImage();
ScrBitmap.Destroy;
end;
procedure TfmMain.RecreateBitmap(aSX,
aSY: Integer);
var
BInfo: tagBITMAPINFO;
begin
// Пересоздание DIB при изменении размеров
"экрана"
ScrBitmap.FreeImage();
SX := aSX; SY := aSY;
BInfo.bmiHeader.biSize :=
sizeof(tagBITMAPINFOHEADER);
BInfo.bmiHeader.biWidth := SX;
BInfo.bmiHeader.biHeight := -SY;
BInfo.bmiHeader.biPlanes := 1;
BInfo.bmiHeader.biBitCount := 32;
BInfo.bmiHeader.biCompression := BI_RGB;
ScrBitmap.Handle :=
CreateDIBSection(Canvas.Handle, BInfo,
DIB_RGB_COLORS, Scr, 0, 0);
ZeroMemory(Scr, SX * SY * 4);
end;
procedure TfmMain.FormCreate(Sender: TObject);
begin
CreateBitmap(pbDraw.ClientWidth, pbDraw.ClientHeight);
pbDraw.Canvas.Draw(0, 0, ScrBitmap);
Caption := 'Визуализатор'; Application.Title := Caption;
end;
procedure TfmMain.FormDestroy(Sender: TObject);
begin
DeleteBitmap();
end;
procedure TfmMain.FormResize(Sender: TObject);
begin
ReCreateBitmap(pbDraw.ClientWidth, pbDraw.ClientHeight);
pbDraw.Canvas.Draw(0, 0, ScrBitmap);
end;
procedure TfmMain.RestrictSize(var msg: TMessage);
var
p: PMinMaxInfo;
begin
// Ограничитель размеров окна (обработка
сообщений Windows).
// Удобная вещь кстати (важно: см. объявление
процедуры в классе TFmMain)
p := PMinMaxInfo(Msg.lParam);
p.ptMinTrackSize.x := 520;
p.ptMinTrackSize.y := 240;
end;
procedure TfmMain.pbDrawPaint(Sender: TObject);
begin
pbDraw.Canvas.Draw(0, 0, ScrBitmap);
end;
Пример работы с данной конструкцией
SX - текущий размер нашего "экрана" по
горизонтали
SY - по вертикали
TBig(Scr^). Scr - это указатель на массив пикселей
битмапа, который в нашем случае имеет
разрядность 32 (32 бита, или 4 байта на пиксел,
что эквивалентно типу Integer. См. объявление
типа TBig).
Конструкция TBig(Scr^) позволяет адресовать эту
память как массив пиксел. Чтобы получить доступ
к пикселу нужно использовать индекс массива [x
+ y * SX].
Функция RGB.
Это стандартная делфяцкая функция, не приспособленная для того что мы тут
творим, а только для своего "родного" класс TCanvas и его цветовых кодов.
В Windows при использовании 32-разрядных битмапов формат пиксела
такой (начиная с первого байта):
BBBBBBB GGGGGGGG RRRRRRRR ********
В Delphi (то что ВСЕГДА возвращает функция RGB, при любой разрядности
картинки):
RRRRRRRR GGGGGGGG BBBBBBBB ********
Усматривается аналогия :) Все что нужно это просто перечислить аргументы
функции в обратном порядке :))
Big(Scr^)[x + y * SX] := RGB(B, G, R);
B, G, R - соответственно значения интенсивности синего, зеленого, и
красного цветов размером байт, т.е. [0..255].
Палитра 32-разрядным режимом не поддерживается, за нас думает Windows
(вернее, понятия палитры в таком режиме вообще нет). Ну а нам остается
это все юзать как надо +)))
Чтобы почистить виртуальный экран, нужно сделать так: ZeroMemory(Scr, SX
* SY * 4);
Download