Михаил Краснов - Графика DirectX в Delphi
hRet := FDD.CreateSurface(ddsd, FDDSWork, nil);
if Failed(hRet) then ErrorOut(hRet, 'Create Surface');
// Цветовой ключ для вспомогательной поверхности
hRet := DDSetColorKey (FDDSWork, RGB(0, 0, 0));
if Failed (hRet) then ErrorOut(hRet, 'DDSetColorKey');
При воспроизведении кадра работаем непосредственно с элементами вспомогательного массива:
function TfrmDD.UpdateFrame : HRESULT;
var
i : Integer; hRet : HRESULT;
begin
ThisTickCount := GetTickCount;
if ThisTickCount - LastTickCount > 10 then begin
Angle := Angle +0.05; // Сдвиг синусоиды
if Angle > 2 * Pi then Angle := Angle - 2 * Pi;
LastTickCount := GetTickCount;
end;
// Воспроизводим картинку фона
hRet := FDDSBack.BltFast (0, 0, FDDSBackGround, nil, DDBLTFAST WAIT);
if Failed(hRet) then begin
hRet := RestoreAll;
if Failed (hRet) then begin
Result := hRet;
Exit;
end;
end;
// Обнуляем элементы массива
ZeroMemory (@FrameBuffer, SizeOf (FrameBuffer));
// Заполняем массив для получения синусоиды
for i := 0 to 99 do
FrameBuffer [50 - trunc (sin (Angle + i * 2 * Pi / 100) * 25), i] :=
120;
// Воспроизводим поверхность синусоиды
hRet := FDDSBack.BltFast (0, 0, FDDSWork, nil,
DDBLTFAST_WAIT or DDBLTFAST_SRCCOLORKEY);
if Failed(hRet) then begin hRet := RestoreAll;
if Failed (hRet) then begin
Result := hRet;
Exit;
end;
end;
Result := DD__OK;
end;
Пример действительно важен, показывает, как отображать данные, размещенные в системной памяти. В некоторых случаях, например при сложных вычислениях с матрицами, такой подход может облегчить решение задачи.
Проект каталога ЕхЗЗ принципиально ничем не отличается от предыдущего, только используется 16-битный режим, а синусоида выводится на весь экран. Здесь вам надо обратить внимание на изменения в описании массива:
FrameBuffer : Array [0..479, 0..639] of WORD;
Значение ipitch для 16-битной поверхности задаем 640x2 пикселов, как ширина поверхности, умноженная на размер одной ячейки. Синусоида располагается на всем экране, и поверхность фона теперь отсутствует. Для простоты подготовки синусоиду рисуем синим цветом:
// Очистка фона, она же - очистка экрана
ZeroMemory (@FrameBuffer, SizeOf (FrameBuffer));
for i := 0 to 639 do
FrameBuffer [240 - trunc (sin (Angle + i * 2 * Pi / 640) * 100), i] :=
255; // Для синего цвета достаточно поместить в ячейку 255
Result := FDDSBack.BltFast (О, О, FDDSWork, nil, DDBLTFAST WAIT);
Закончим самым тривиальным способом построения синусоиды, основанным на блиттинге (проект каталога Ех34). Важен этот простой пример тем, что иллюстрирует существование образов в таких количествах, сколько нам необходимо. Подобным многократным блиттингом мы активно будем пользоваться в следующей главе.
Отдельный образ загружается из растра, при воспроизведении кадра он копируется на экране 640 раз:
for i := 0 to 639 do begin
hRet := FDDSBack.BltFast (i, 240 -
trunc (sin (Angle + i * 2 * Pi / 640) * 100),
FDDSImage, nil, DDBLTFAST_WAIT or DDBLTFAST_SRCCOLORKEY);
if Failed (hRet) then begin
Result := hRet;
Exit;
end;
end;
Что вы узнали в этой главе
Мы выяснили, что для задания прозрачности участков поверхности используется механизм цветового ключа.
Также познакомились с принципами построения анимации и приемами, применяемыми для создания визуальных эффектов. Один из важнейших механизмов, используемых для работы с содержимым поверхности, заключается в непосредственном доступе к ее пикселам. Осуществляется такой доступ для поверхностей, размещаемых в видеопамяти путем реализации механизма запирания поверхности. К тому же подобные поверхности требуют особого внимания в ситуациях восстановления минимизированного приложения.
Глава 4 Спрайтовая анимация
Спрайты
Хранитель экрана
Проверка столкновений
Спрайты и оконный режим
Что вы узнали в этой главе
Данная глава содержит вводный материал по созданию анимации на основе спрайтов и на примерах демонстрирует используемые приемы ее программирования. Как правило, именно такие приемы используются при моделировании игр и схожих с ними приложений.
Примеры располагаются в каталоге ExamplesChapter04.
Спрайты
В большинстве предыдущих примеров на экране присутствовал одинокий образ, вид которого не менялся с течением времени. Теперь нам предстоит узнать, как создавать движущиеся образы, меняющиеся со временем или в зависимости от обстоятельств. Также попутно нам предстоит узнать еще много нового о DirectDraw.
Разработчики восьмой версии этой замечательной библиотеки позаботились о программистах, переработав модуль DDutil и предоставив в наше распоряжение объектно-ориентированную оболочку для использования DirectDraw. Код приложений выглядит удобочитаемым и легко воспринимаемым. Ваш покорный слуга перенес этот модуль на Delphi (назвав DDutil), и мы сможем воспользоваться удобными нововведениями. Однако в рассматриваемых до сих пор примерах эта библиотека не использовалась и во многих последующих примерах также не будет применяться.
Во-первых, использование такой, как и любой другой объектно-ориентированной библиотеки в Delphi приводит к значительным накладным расходам, потерям драгоценного времени, поэтому указанные библиотеки лучше включать лишь в простые примеры.
Во-вторых, подобные библиотеки не могут вместить в себя все возможности DirectDraw, программист не в состоянии только с их помощью реализовать все свои идеи и ему все равно потребуются знания более низкого уровня.
В-третьих, если опираться только на готовые библиотеки, теряется чувство понимания собственных действий, а вынужденное использование механизмов, не включенных в библиотеку, выглядит чуждым и неестественным. В принципе, такое возникает очень часто при программировании в среде Delphi, например создание ловушек сообщений для новичка выглядит вычурным и сложным.
Я надеюсь, что ознакомление с предыдущим материалом книги прошло для вас без проблем, и вы теперь можете свободно ориентироваться в этих программах с длинным и громоздким кодом. Если это так, вы будете легко разбираться и в чужих программах, написанных на другом языке. Вы можете встретить массу примеров по использованию DirectX в книгах, ориентированных на С-программиста в DirectX SDK или Сети. Код таких примеров вами должен легко пониматься, поскольку код наших предыдущих примеров был к нему очень близок.
Мы могли бы теперь и не отвлекаться на изучение нового для сегодняшнего дня подхода, но, поскольку такой подход предлагается разработчиками, то он фактически узаконивается в качестве стандарта, и, со временем, вам будут все чаще и чаще встречаться программы, построенные именно на подобном подходе. Нам надо обязательно познакомиться с ним, чтобы вы не чувствовали себя в такой ситуации неуютно.
Код теперь выглядит проще, но я подчеркну, что ваши программы только выиграют, если вы будете создавать их так, как мы делали это в предыдущих примерах.
Ну что же, после такого вступления можно переходить к рассмотрению первого примера, проекта каталога Ex01. Выглядит его работа несложной: по экрану времени перемещаются образы логотипа DirectX, отскакивая от стенок. Пример является моей трансляцией одного из примеров, входящих в DirectX 8.0 SDK, я внес в код минимум изменений по сравнению с первоисточником.
Поведение спрайтов не будем подробно рассматривать, проанализируем голько то, что связано непосредственно с DirectDraw.
В коде отсутствуют многие знакомые нам типы, вместо них появились новые:
g_pDisplay : CDisplay; // Главный объект
g_J?LogoSurface : CSurface; // Поверхность образа
g_pTextSurface : CSurface; // Поверхность текста
Я долго думал, изменять ли префикс таких типов на префикс "т", принятый для Delphi, и решил оставить все-таки его таким же, как и в первоисточнике.
лавный объект инкапсулирует методы и свойства, связанные с созданием и управлением поверхностями. Объекты присоединяемых к главному объекту поверхностей можно создавать пустыми, либо по содержимому растра, либо путем вывода текста:
g_pDisplay := CDisplay.Create; . // Создание главного объекта
// Метод создания полноэкранного дисплея
hr := g_pDisplay.CreateFullScreenDisplay(Handle, ScreenWidth,
ScreenHeight, ScreenBitDepth);
// Анализ успешности действия
if FAILED(hr) then ErrorOut (hr, 'This display card does
not support 640x480x8.');
// Создание внеэкранной поверхности спрайта
hr := g_pDisplay.CreateSurfaceFromBitmap(g_pLogoSurface, imageBmp,
SPRITE_DIAMETER, SPRITEJDIAMETER);
if(FAILED(hr)) then ErrorOut (hr, 'CreateSurfaceFromBitmap');
// Создание внеэкранной поверхности с текстом
hr := g_pDisplay.CreateSurfaceFromText(g_pTextSurface, Font.Handle,
HELPTEXT, RGB(0,0,0>, RGB(255, 255, 0));
if(FAILED(hr)) then ErrorOut (hr, 'CreateSurfaceFromText');
// Метод поверхности для установки цветового ключа
hr := g_pLogoSurface.SetColorKey(0);
// Ключ - черный цвет
if(FAILED(hr)) then ErrorOut (hr, 'SetColorKey');
Как я и обещал, код действительно упростился, нет ни слова о первичной поверхности и вообще о буферах. Вся эта черновая работа скрыта от глаз разработчика. Воспроизведение также значительно упростилось. Основано оно целиком на использовании методов главного объекта:
for iSprite := 0 to NUM_SPRITES - 1 do // Цикл вывода спрайтов
g_pDisplay.ColorKeyBlt(g_Sprite[iSprite].fPosX,
g_Sprite[iSprite].fPosY, g_pLogoSurface.GetDDrawSurface, nil);
// Вывод текста подсказки
g_pDisplay.Blt(10, 10, g_pTextSurface, nil);
// Завершение работы. Выполняем переключение поверхностей